What happens to each line of a .pth file found in a site-packages directory?
answer
- A tiny text file with two behaviours
- Only meaningful in certain directories
- One prefix makes a line run
- Relative to the file, not the process
- Errors go to stderr and startup continues
basics
~20 sThe site module reads the file at startup. Blank and comment lines are skipped, a line starting with import is executed as code, and every other line is treated as a directory path, resolved against the file's own directory and appended to sys.path.
solid answer
~50 sA `.pth` file only means anything inside a directory that `site` treats as a site directory — the ones it adds at startup, or one you register with `site.addsitedir`. For each such directory, `site` sorts the `.pth` file names and reads them line by line. A blank line or a line starting with `#` is skipped. A line beginning with `import ` (or `import` followed by a tab) is **executed** with `exec`, which is how a `.pth` can run arbitrary code before your program's first statement. Any other line is treated as a path, made absolute relative to the directory containing the `.pth`, and appended to `sys.path` if that directory exists and is not already there. If an executed line raises, `site` writes the error to stderr, ignores the rest of that file and startup continues.
code
python · 11 linesimport pathlib
import site
import sys
import tempfile
d = pathlib.Path(tempfile.mkdtemp())
(d / "libs").mkdir()
(d / "demo.pth").write_text("libs\nimport sys; print('pth line ran')\n")
site.addsitedir(str(d))
print(str(d / "libs") in sys.path)go deeper
Recognise the file when you see it: a text file in site-packages whose lines usually name extra directories to search. Know that an editable install is typically what put it there.
Explain both line behaviours — path entries resolved against the containing directory, and the leading import that is executed — and that only site directories are scanned for these files.
Treat a .pth as a real execution surface when diagnosing environment drift: enumerate them, read them, and know that a failing line degrades silently to stderr rather than failing the process.
Own the guardrail: anything that can write a site directory executes code in every process using that interpreter, so environments are built by a controlled process and startup side effects belong in an explicit entry point.
### The file format A `.pth` file is a plain-text file whose name ends in `.pth`, living in a *site directory*. The format is deliberately trivial and is processed one line at a time: - **Blank lines** and lines whose first character is `#` are ignored. - **A line starting with `import ` or `import\t`** is passed to `exec`. It runs as code, at startup, in the `site` module's context. - **Anything else** is treated as a directory path. It is joined against the directory that contains the `.pth` file (so relative entries are relative to the site directory, not to your working directory), and appended to `sys.path` when the directory exists and is not already present. The surprise for most people is the second rule. It exists so that a distribution can perform startup-time setup — registering a codec, installing an import hook, patching something — without the application knowing. It is used rarely and deliberately, and it is the reason a `.pth` is a genuine execution surface, not just a path list. ### Where the file has to live A `.pth` is not a general mechanism that applies anywhere on `sys.path`. It is read only from directories `site` treats as site directories: the ones it computes from the prefixes at startup, the per-user directory when it is enabled, and anything registered explicitly via `site.addsitedir(path)`. Dropping a `.pth` into an ordinary directory that merely happens to be on `sys.path` — your project root, say — does nothing at all. This trips people who try to "fix" a path problem by leaving a `.pth` next to their script. `site.addsitedir` is the supported way to opt a directory in at runtime: it appends the directory itself and then processes its `.pth` files exactly as startup would. ### Why you meet them in practice The most common encounter is an editable install. Rather than copying your source into site-packages, the installer writes a small `.pth` there that names your source tree (or executes a line that installs a finder for it). Startup appends the directory, so your working copy is importable, and edits take effect without reinstalling. That is also why an editable install is invisible to anything that only reads copied files: the evidence lives in a one-line text file, not in a package directory. The second common encounter is a distribution that needs to run at startup — a coverage or profiling hook, a compatibility patch, a codec registration. These use the executable `import` line. ### Ordering, and why not to rely on it Within one site directory, `site` sorts the `.pth` file names before processing them, which is why you occasionally see numeric prefixes such as `00-something.pth` used to force an order. Across site directories, order follows the order in which `site` processes the directories themselves. Both are implementation-level facts, not a stable contract you should build behaviour on. If two `.pth` files must run in a particular order, that is a design smell: prefer one explicit entry point that does the setup in a known sequence. ### Failure behaviour Errors do not take the interpreter down. When a line raises, `site` prints the failing file, the line number and the traceback to stderr, then abandons the remainder of that file and continues startup. The practical effect is nasty in production: your program runs, but some setup silently did not happen, and the only evidence is a message that scrolled past on stderr — often into a log nobody greps. A duplicated or missing side effect that appears in one environment and not another is very often a `.pth` file that half-ran. ### Security and hygiene An executable line in a `.pth` file runs before your first statement, with your process's privileges, every time that interpreter starts. Anything that can write into a site directory therefore has code execution in every process using that interpreter. This is one concrete reason environments should be built from a lockfile by a process you control, and site directories should not be world-writable. For your own code, prefer the boring alternative: do setup explicitly in your entry point, where it is visible, ordered and testable. Reach for a `.pth` only when the setup genuinely must happen before user code — and then make it a single import line that delegates to a real module you can read, rather than logic inlined into a text file. This format and its rules are unchanged on 3.14.
- Why does an editable install make your source importable without touching PYTHONPATH?The installer writes a small `.pth` into the environment's site-packages naming your source tree, or executing a line that installs a finder for it. Startup processes that file and appends the directory to `sys.path`, so edits in your working copy take effect immediately with no reinstall and no environment variable involved.
- Does dropping a .pth file into any directory on sys.path have an effect?No. `.pth` files are read only from site directories — those `site` computes at startup, the per-user directory when enabled, and any registered with `site.addsitedir`. A `.pth` beside your script, or in an ordinary `PYTHONPATH` directory, is just an unread text file.
- What happens if the executed line in a .pth file raises an exception?`site` writes the file name, line number and traceback to stderr, skips the rest of that file and lets startup continue. Your program runs with the setup partly applied, which is why these failures show up as mysterious behaviour differences between environments rather than as a crash.
saying these in an interview costs you the question
- Thinks a .pth works in any directory on sys.path
- Believes .pth files can only list paths, never run code
- Resolves relative entries against the working directory
- Says a failing line aborts interpreter startup
- Treats .pth ordering as a documented guarantee