skip to content

After `pip install -e .`, which kinds of project change still require re-running the install?

level: middleimportance: should knowfreq 33%

answer

  1. Redirected, not re-read
  2. One run decides everything except file contents
  3. Anything recorded at install time goes stale
  4. Version, dependencies, generated command wrappers
  5. Compiled extensions and strict mappings too

basics

~20 s

Anything that is not module source read at import time: project metadata such as the version, dependencies and console-script entry points, compiled extension modules, and — under a strict editable mapping — newly added modules. Editing existing Python source needs nothing.

solid answer

~50 s

An editable install redirects imports; it does not re-read your project. So everything captured at install time goes stale when you change it. Metadata is the big one: bumping the version, adding or removing a dependency, or declaring a new console script in `pyproject.toml` has no effect until you re-run `pip install -e .`, because the `.dist-info` and the generated script wrappers were written once. A compiled extension module needs an actual rebuild — the redirection points at files, it does not invoke a compiler. And the redirection's shape matters: a permissive mapping that put a directory on `sys.path` picks up new modules and subpackages for free, while a strict mapping that enumerated declared modules into a generated finder will not see a file you added afterwards. Ordinary edits to existing `.py` files need nothing but a fresh process.

code

python · 4 lines
python
import importlib.metadata

for ep in importlib.metadata.entry_points(group="console_scripts"):
    print(ep.name, "->", ep.value)

go deeper

for a junior

Remember the split: editing the body of an existing module needs only a fresh run, while touching the project's declaration file usually means running the install again.

for a middle

Be able to justify the split from the mechanism — metadata, generated command wrappers and compiled artefacts are produced once at install time, whereas module source is read at each import through the redirection.

for a senior

Recognise the symptoms quickly in a team setting: a stale reported version, a missing command, a plugin that discovery cannot see, or a module that will not import under a strict mapping — and know which of those a reinstall fixes and which needs a rebuild.

for a principal

Set the expectation that the development environment is not the release artefact: a clean build and install of the wheel in the pipeline is what proves the metadata, the entry points and the file list are actually correct.

### Editable means "imports are redirected", not "the project is re-read" It is easy to hear "editable" as "pip keeps watching my project". It does not. `pip install -e .` runs once, the build backend produces an editable wheel once, and pip installs its contents once. Everything that was decided during that run is frozen until the next one. What survives editing is only the thing that is resolved lazily, at import time: the content of a `.py` file that the redirection points at. Drawing that line makes the reinstall rules fall out rather than needing memorisation. ### Metadata is a snapshot The `.dist-info` directory written at install time carries the distribution's name, version and requirements. Change the version in `pyproject.toml` and `importlib.metadata.version()` keeps reporting the old one — which quietly matters, because a service that logs or reports its own version through that call will report a stale value all afternoon. Add a dependency and nothing installs it; your imports fail until you re-run the install, and the failure is confusing precisely because the declaration is right there in the file. Console-script entry points are the sharpest version of this. When you declare one, the installer generates a small executable wrapper in the environment's script directory and records the entry point in metadata. Both happen at install time. Add a new command and it simply does not exist on your path; rename one and the old wrapper stays behind, still pointing at a function you deleted. Anything that discovers plugins through entry points behaves the same way — a newly declared plugin is invisible to the discovery call until the distribution is reinstalled, because discovery reads installed metadata rather than your source. ### Compiled code is not source If the project ships an extension module written in C, the editable install maps the *built* artefact. Editing the C source does nothing at all; the compiled object on disk is unchanged. You need a rebuild, which for most backends means re-running the editable install so the build step executes again. Candidates who only ever ship pure Python often miss this, and it is worth saying out loud because the symptom — an edit that has no effect — looks identical to the stale-import symptom while having a different cause. ### New files depend on the redirection's shape This is where the two forms of editable install diverge. A permissive mapping put a directory on `sys.path` through a `.pth` file, so anything you later create inside it is importable immediately — new modules, new subpackages, no reinstall. A strict mapping enumerated the modules the project declared into a generated finder; that mapping is a list, and a file created afterwards is not on it, so the import fails until you reinstall. Neither is wrong: strict trades convenience for an import surface that matches the wheel. Knowing which one your project got explains an otherwise baffling `ModuleNotFoundError` for a file you can see on disk. A related case bites both forms: adding a whole new top-level package. Even under a permissive mapping the file resolves fine, but the packaging configuration may not include it — automatic discovery ran during the install — so it will be missing from the wheel you build later even though it imports perfectly today. ### And the boring one: restart the process A module already imported lives in `sys.modules` until the process exits. An edit to existing source is picked up by the next process, not the current one. That is not a reinstall, but it is the same question in the interview — "my change did nothing" — and it is worth separating cleanly from the metadata cases. ### The practical habit Re-running `pip install -e .` is cheap and safe, so the useful rule is simply: touched `pyproject.toml`, added a command, added a dependency, changed compiled code, or created a file that will not import — reinstall. Touched only the body of an existing module — restart the process. Teams that write this down stop losing afternoons to it, and in CI the question does not arise at all, because the pipeline installs a freshly built wheel from a clean environment every time.

  • You added a console script to `pyproject.toml` and the command is not found. Why?
    The executable wrapper for a console script is generated by the installer into the environment's script directory, and the entry point is recorded in the installed metadata — both at install time. An editable install redirects module imports only, so nothing regenerates when you edit the declaration. Re-run `pip install -e .` and the wrapper appears.
  • Why can a new module import fine for you but be missing from the built wheel?
    Because import resolution and packaging are answering different questions. A permissive editable mapping put a directory on `sys.path`, so anything inside it imports regardless of whether it was ever declared. The wheel contains only what the packaging configuration selects. Installing a freshly built wheel somewhere clean and importing it is what catches the gap.
  • Does an editable install rebuild a compiled extension module when you edit its source?
    No. The redirection points at files; it does not run a compiler. Editing C source leaves the previously built object untouched, so behaviour is unchanged until a rebuild happens, which usually means re-running the editable install so the backend's build step executes again.

The forwarding address gets you today's post at the new house, but the name on the mailbox, the keys cut for you and the doorbell wiring were all set up on moving day and only change if someone comes back to redo them.

saying these in an interview costs you the question

  • Believes pip re-reads pyproject.toml on every import
  • Expects a new console script to appear without reinstalling
  • Thinks a new dependency is installed automatically
  • Assumes editing C source takes effect without a rebuild
  • Says no change ever requires reinstalling after -e
  • Confuses restarting the process with reinstalling

context