Skip to content

← Seams, Modules, Packaging and Tooling step 4 of 36

Medium Framework

What goes on sys.path first

Reproduce the interpreter’s sys.path seeding rule for every way of starting Python.

def sys_path_for(argv: Sequence[str], cwd: str, safe_path: bool) -> list[str]:

Return the entries CPython prepends to sys.path — a list of zero or one element. argv is the argument list after the interpreter name, cwd is an absolute POSIX path, and safe_path is -P / PYTHONSAFEPATH=1.

The rules, in order:

  1. safe_path is true → []. Nothing is prepended, in every shape below.
  2. argv is empty (the REPL, or stdin) → [""].
  3. argv[0] == "-c"[""]. The empty string means “the current directory, resolved at each import”, which is not the same as an absolute cwd.
  4. argv[0] == "-m"[cwd]. Note this is the working directory, not the directory of the module being run.
  5. argv[0] ends with ".py" → the script’s directory, made absolute.
  6. Anything else is a directory or a zipfile → the name itself, made absolute.

“Made absolute” means: if the path does not start with /, join it onto cwd; then normalise away . and .. segments. Use posixpath, not os.path — the result must not depend on which platform you happen to be grading on. A script path with no directory part ("run.py") has a directory of ".", which normalises to cwd.

Why this is worth knowing by heart. Rule 4 is why a cron entry that forgets to cd imports a different package than the one you tested. Rule 3 is why a program that calls os.chdir() can change where its own later imports come from. Rule 1 is the switch that makes both of those impossible — and the diagnostic that proves shadowing is happening.

Your submission must pass mypy --strict. argv is a Sequence[str], so index it only after you have established it is non-empty.