Editable Installs & Local Packages
Editable installs link your source tree into the virtual environment so code changes take effect immediately. Combined with src/ layout, this gives reliable imports in development and tests.
Search across all documentation pages
Editable installs link your source tree into the virtual environment so code changes take effect immediately. Combined with src/ layout, this gives reliable imports in development and tests.
# src/ layout project
uv pip install -e .
# or: pip install -e .
# Local path dependency in pyproject.toml
# dependencies = ["my-utils @ file:///path/to/my-utils"]When to reach for this:
myapp/
├── pyproject.toml
├── src/
│ └── myapp/
│ ├── __init__.py
│ └── core.py
└── tests/
└── test_core.py
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.14"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/myapp"]uv pip install -e ".[dev]"
uv run pytest# tests/test_core.py
from myapp.core import greet
def test_greet():
assert greet("Ada") == "Hello, Ada!"What this demonstrates:
src/ layout prevents accidental imports from the repo rootimport myapp resolve to src/myapp/core.py are visible on next test run without reinstallpip install -e . writes a .pth file in the venv pointing to src/pyproject.toml configmy-utils @ file://...) symlink local packages into the dependency tree| Layout | Import risk | Recommended |
|---|---|---|
src/myapp/ | Low (must install) | Yes |
myapp/ at root | Tests import uninstalled code | No |
# uv workspace for monorepos
[tool.uv.workspace]
members = ["packages/*"]
# packages/myapp/pyproject.toml
[project]
name = "myapp"
dependencies = ["my-utils"]
# packages/my-utils/pyproject.toml
[project]
name = "my-utils"uv sync # installs all workspace members editablesrc/ layout; always pip install -e . before testing..pth points to old path. Fix: reinstall: uv pip install -e . --force-reinstall.__init__.py - namespace packages may confuse editable discovery. Fix: include __init__.py unless you intentionally use namespace packages.pip install from PyPI needs a real wheel. Fix: editable is for dev; uv build for distribution.| Alternative | Use When | Don't Use When |
|---|---|---|
PYTHONPATH=src | Quick hack, no install | Production or CI (fragile) |
Regular install (pip install .) | Verifying the built package | Active development |
| uv workspace | Monorepo with 2+ packages | Single-package apps |
Editable. The installed package links back to your source directory.
No for new .py files inside an existing package. Yes if you add a new top-level package or change entry points.
It forces tests to import the installed package, catching packaging bugs before release.
uv workspaces or dependencies = ["sibling @ file:///${PROJECT_ROOT}/packages/sibling"] with relative paths.
Yes: uv pip install -e ../my-utils or declare it as a workspace member.
Yes: poetry install installs the current project editable by default.
Package data must be declared in pyproject.toml ([tool.hatch.build.targets.wheel.force-include]). Source edits to data files are picked up immediately.
python -c "import myapp; print(myapp.__file__)" should point to src/myapp/.
Mount source as a volume and pip install -e . in the container for live-reload development.
Yes. Only your project (and workspace siblings) are editable; PyPI deps install normally.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026