Python Modules and Packages Quiz: Imports, venv and argparse

Test yourself: 12 questions and three programs on Python imports and sys.modules, packages and python -m, venv, pip and pyproject.toml, and argparse scripts.

  • Course: Python study plan
  • Module: Modules, packages and imports
  • Kind: Checkpoint — cleared at 70%
  • Reading time: 22 min
  • Runtime: CPython 3.11

Checkpoint — Modules, packages and imports is the checkpoint that closes the Modules, packages and imports module: a graded quiz and whole-program exercises, passed at 70%.

Instructions

This checkpoint covers the whole module: what import does and the sys.modules cache, from-imports copying bindings, __name__ and circular imports, packages with __init__.py, relative imports and python -m, the standard-library map, virtual environments with pip and pyproject.toml, and command-line scripts with argparse, the three streams and exit codes.

How it works. Twelve questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.

Before you start, make sure you can answer these from memory:

  • How many times does a module's top-level code run in one process, and why?
  • After from config import DEBUG, what does rebinding config.DEBUG do to your DEBUG?
  • What does import shop do about shop.models?
  • Why does python shop/cli.py break relative imports, and what is the fix?
  • What is the difference between requests>=2.31,<3 in pyproject.toml and requests==2.31.0 in a lock file?
  • Which stream do error messages go to, and what exit code does argparse use for bad usage?
  • Where does a data file inside a package get opened from?

The three programs are a dependency resolver that orders modules by their imports and detects cycles, a requirements parser that checks installed versions against specifiers, and a command-line tool whose arguments are parsed by argparse from lines of input.

Common questions

How many times does a module's top-level code run in one process?

Once, at the first import anywhere in the process. The module object is then cached in sys.modules, and every later import of it is a dictionary lookup that binds the cached object, so top-level side effects such as printing or opening files never repeat.

After from config import DEBUG, does rebinding config.DEBUG change your DEBUG?

No. from config import DEBUG copied the binding at import time, so your DEBUG still refers to the old object after config.DEBUG is rebound. Read module-level state that changes through the module, as config.DEBUG, to see the new value.

What is the difference between requests>=2.31,<3 and requests==2.31.0?

requests>=2.31,<3 in pyproject.toml is a loose range for a direct dependency: any 2.x from 2.31 on, so the project can coexist with other packages. requests==2.31.0 in a lock file pins one exact version, so every deployment installs the same thing.

Exercises

Import graph

Read lines <module>: <imports…> (possibly none) and an entry module on the last line entry <name>. Compute the order in which module bodies finish executing when the entry is imported — a depth-first walk where each module's imports complete before it does, each module once — and print it. If the walk meets a module that is currently being imported (a cycle), print cycle: a -> b -> … -> a for the first cycle found, using the path from that module back to itself, and stop.

Input: lines, then entry <name>. Output: order: <names> or the cycle: line.

a: b c
b: c
c:
entry a

prints

order: c b a

Requirements check

Requirements arrive as name<specifiers> lines and the installed set as name==version lines after a -- line. Specifiers are comma-separated op version pairs with ==, !=, >=, <=, >, <, or ~= (compatible release: ~=1.4 means >=1.4,<2; ~=1.4.2 means >=1.4.2,<1.5). Print each requirement's status and finish with problems <n> counting the missing and conflicting ones.

Input: lines, --, lines. Output: <name>: ok|conflict|missing per requirement, then problems <n>.

requests~=2.31
numpy>=1.26,<2
mypy
--
requests==2.32.1
numpy==2.0.0

prints

requests: ok
numpy: conflict
mypy: missing
problems 2

An inventory command

Build a sub-command tool with argparse (exit_on_error=False): add name --qty N (default 1), remove name [--all] (removes one unit, or every unit with --all), show (prints name qty lines in name order, or empty). Each input line is a command line split with shlex.split; a usage error prints usage error; removing an unknown item prints unknown item. Keep the state across lines.

Input: command lines. Output: one line per show row, plus the error lines.

add bolt --qty 3
add nut
remove bolt
remove screw
show
remove nut --all
show

prints

unknown item
bolt 2
nut 1
bolt 2

In this module: Modules, packages and imports

← Scripts and the command line — argv, argparse, exit codes and streams · math, statistics, fractions, decimal and random →