skip to content

questions

4

What does `pip install -e .` do that a plain `pip install .` does not?

level: juniorimportance: must knowfreq 62%

answer

  1. Two installs, two very different results
  2. One copies the code, one points at it
  3. Something in site-packages redirects the import
  4. A .pth file or a generated finder
  5. PEP 660 editable wheel, no code copied

basics

~20 s

pip install -e . installs the project in editable mode: site-packages receives a pointer back to your working source tree instead of a copy of it, so a source edit takes effect on the next interpreter start without reinstalling.

solid answer

~40 s

`pip install .` builds a wheel from the project and unpacks it into `site-packages`, so the environment then owns a frozen copy of the code and every later edit needs another install to be seen. `pip install -e .` runs the same build backend but asks it for an *editable* wheel under PEP 660: what lands in `site-packages` is metadata plus a redirection — typically a `.pth` file that adds your source directory to `sys.path`, or a `.pth` line that imports a small generated finder — so `import yourpkg` loads the file you are editing. It is still a real installation: the distribution shows up in `pip list`, its dependencies are resolved and installed, and `importlib.metadata.version()` can read its version. Editable mode is a development convenience; what you deploy should be the built wheel.

code

python · 5 lines
python
import importlib.util

spec = importlib.util.find_spec("json")
print(spec.origin)
print(spec.submodule_search_locations)

go deeper

for a junior

Be ready to state the difference in one sentence: a plain install copies your code into the environment, while pip install -e . points the environment at your source tree so edits take effect without reinstalling.

for a middle

Explain the mechanics behind that sentence. The build backend still runs, but it produces an editable wheel whose payload is a redirection into your checkout plus ordinary .dist-info metadata, so dependency resolution and pip list behave exactly as normal.

for a senior

Show judgement about where each belongs. Editable in development and local test runs; a built wheel in the packaging job and in production, so a module that was never declared fails there rather than in front of a user.

for a principal

Own the policy across projects: which environments may use editable installs, where the real wheel gets exercised before release, and how you keep a developer's convenience from becoming the shape of the deployed artefact.

### Both commands go through the build backend `pip install .` and `pip install -e .` begin identically. pip reads `pyproject.toml`, looks at the `[build-system]` table, installs the declared build backend into an isolated build environment, and calls it. What differs is *which hook* pip asks for, and therefore what the resulting wheel contains. For a plain install pip asks the backend to build a normal wheel. A wheel is a zip file holding the importable package's `.py` files, any package data, and a `.dist-info` metadata directory. pip unpacks that into the environment's `site-packages`. From that moment the environment owns a private copy of your code, and the interpreter never looks at your source tree again — it looks at `site-packages`. That is exactly what a user gets when they install your project from an index, which is the value of a plain install: it exercises the real artefact. For an editable install pip asks the backend for an *editable* wheel, the hook standardised by PEP 660. The backend is free to decide how that wheel redirects imports, and the two common shapes are both tiny. The simplest writes a `.pth` file into `site-packages` whose single line is the absolute path of the directory containing your importable package; the `site` module reads every `.pth` in `site-packages` at interpreter start and appends such lines to `sys.path`, so `import yourpkg` resolves into your working tree. The stricter shape writes a `.pth` line that imports a generated module which registers a finder on `sys.meta_path`, mapping only the package names the project actually declares to their files on disk. Either way, the module source is never copied into the environment. ### An editable install is still an install This is the part candidates most often get wrong. An editable install is not a hack outside the packaging system. pip writes a normal `.dist-info` directory with `METADATA` and `RECORD`; the distribution appears in `pip list` and can be uninstalled; its declared dependencies are resolved and installed exactly as for any other install; another distribution that requires it is satisfied; `importlib.metadata.version("your-dist-name")` returns its version; and any console scripts it declares are generated into the environment's script directory. Because the install came from a local path, pip also records a `direct_url.json` in the `.dist-info` whose `dir_info` object carries `"editable": true`, which is how tooling can tell an editable install apart after the fact. ### The distribution and the importable package are different names Worth keeping straight while answering. The *distribution* is what pip installs and what `pip list` shows — the `name` in `[project]`. The *importable package* is the directory with `__init__.py` that `import` finds. They routinely differ in spelling, and an editable install is precisely the link between them: it teaches the environment that this distribution's importable package lives over there in your checkout. ### What "no reinstall needed" does not cover Only the module source is redirected. Metadata is snapshotted when you install, so adding a dependency, bumping the version, or declaring a new console script all need the install re-run. A compiled extension module still has to be rebuilt — editing its C source changes nothing until it is compiled. And a *running* interpreter caches imported modules in `sys.modules`; your edit is picked up by the next process, not by the one already executing. Development servers create the illusion of live editing by restarting the process for you. ### When to use which Use `-e` in a development environment and while running the test suite locally, where the whole point is a fast edit-run loop and where you want the tests to import the code the same way a user would. Use a built wheel everywhere the artefact matters: in a container image, in the packaging job of a pipeline, and in production. An editable install ties the environment to a working tree that may be dirty, may move, or may not exist on the target machine, and it hides a classic packaging bug — a module that imports fine because the whole source directory is on `sys.path`, but was never declared and so is missing from the real wheel. Building the wheel once and installing it is what surfaces that before a user does.

  • Does an editable install still record the distribution's metadata and dependencies?
    Yes. pip writes a normal `.dist-info` directory with `METADATA` and `RECORD`, and for a local path install a `direct_url.json` whose `dir_info` marks the install editable. Dependencies are resolved and installed as usual, `importlib.metadata.version()` works, console scripts are generated, and another distribution that depends on this one is satisfied. Only the module files are redirected instead of copied.
  • When would you deliberately install the built wheel instead of using `-e`?
    Whenever the artefact itself matters: container images, the packaging job in a pipeline, and production. An editable install makes the environment depend on a working tree that may be dirty, moved or absent, and it can hide a module that is importable only because the source directory sits on `sys.path` and was never declared for the wheel. Installing the wheel makes that fail where you can see it.
  • You edited a module, but a long-running process still uses the old code. Why?
    Because an editable install redirects where imports *resolve*, not when they happen. A module already imported is cached in `sys.modules` and is not re-read; the edit takes effect the next time the process starts. Reloading is possible but does not rebind names that other modules already imported, which is why development servers restart the process instead.

A plain install files a photocopy of your project in the library; an editable install files a slip of paper saying the original is on that desk over there.

saying these in an interview costs you the question

  • Says pip watches the filesystem and copies changed files
  • Thinks -e skips the build backend entirely
  • Believes an editable install is not a real install
  • Claims an editable install cannot satisfy another package's dependency
  • Recommends pip install -e for production deployments
  • Expects edits to reach an already-running interpreter

context

open as a page

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

level: middleimportance: should knowfreq 33%

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.

open as a page

What does an editable install (`pip install -e .`) actually put into site-packages?

level: middleimportance: should knowfreq 44%

basics

~20 s

Metadata plus a redirection, not your code: a normal .dist-info directory, and a .pth file whose line either adds your source directory to sys.path or imports a small generated finder that maps declared packages to their files.

open as a page

`pip install -e .` is in place, yet an email-digest service still imports an old clock-skew fix — how do you find which copy Python is loading?

level: seniorimportance: should knowfreq 41%

basics

~10 s

Stop guessing and ask the import system: print the module's file or importlib.util.find_spec(name).origin in the exact interpreter that runs the code, then compare it against sys.path order and sys.executable to see which copy wins.

open as a page