uv, pip, and packaging
Create reproducible environments, define pyproject metadata, and ship importable Python packages.
uv, pip, and packaging
A Python project is reproducible when another developer can create an isolated environment, install the intended dependency graph, import the package, and run its commands without guessing.
pip and uv solve different-sized problems
pip installs packages. It does not create environments, choose Python interpreters, or define a
universal lockfile workflow. Those jobs traditionally require venv, pip-tools, and other tools.
uv provides an integrated workflow:
- install and select Python versions;
- create and manage
.venv; - add, remove, resolve, and lock dependencies;
- install the current project;
- run commands inside the project environment;
- build and publish distributions.
Use pip when compatibility or a constrained environment requires it. Prefer uv for a new
application when you want one fast, consistent interface.
Start a package
uv init --package weather-tools
cd weather-tools
uv add httpx
uv add --dev pytest ruff mypyA useful src layout is:
weather-tools/
├── pyproject.toml
├── README.md
├── src/
│ └── weather_tools/
│ ├── __init__.py
│ └── cli.py
└── tests/
└── test_cli.pyThe src layout prevents tests from accidentally importing the repository directory instead of the
installed package. It catches missing package data and build configuration earlier.
Understand pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "weather-tools"
version = "0.1.0"
description = "Weather data utilities"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.27,<1",
]
[project.optional-dependencies]
postgres = ["sqlalchemy[asyncio]>=2.0,<3", "psycopg[binary]>=3.2,<4"]
[project.scripts]
weather = "weather_tools.cli:main"
[dependency-groups]
dev = [
"mypy>=1.10",
"pytest>=8",
"ruff>=0.5",
]
[tool.hatch.build.targets.wheel]
packages = ["src/weather_tools"]Important boundaries:
[build-system]tells build frontends how to create wheels and source distributions.[project]contains standardized package metadata and runtime dependencies.[project.optional-dependencies]exposes installable features to package consumers.[dependency-groups]describes local development groups; it is not package metadata.[project.scripts]creates executable commands without handwritten launcher scripts.
Avoid declaring the same dependency in multiple places. Runtime imports belong in dependencies;
test and lint tools belong in a development group.
Applications and libraries pin differently
For an application, commit uv.lock and deploy from it:
uv lock
uv sync --locked--locked fails if pyproject.toml and the lockfile disagree, which is appropriate in CI.
A reusable library should usually declare compatible ranges because its dependencies must coexist with the consuming application. Test the lowest and highest supported Python/dependency combinations when compatibility is important. A lockfile still makes the library's own development environment repeatable, but consumers resolve from the declared metadata.
Run tools without activating
uv run weather --help
uv run pytest
uv run python -m weather_tools.cliuv run makes scripts and automation less dependent on shell activation state. If you prefer manual
activation:
# POSIX shells
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1Equivalent pip workflow
python -m venv .venv
# POSIX
. .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[postgres]"
python -m pip install pytest ruff mypyUse python -m pip, not a bare pip, when interpreter ambiguity is possible. Editable installation
keeps import behavior package-like while reflecting source changes.
pip freeze > requirements.txt records an environment, including indirect dependencies and local
accidents. It is useful for some application deployments, but it is not a substitute for curated
package metadata. Never paste a frozen environment into [project.dependencies].
Build and inspect distributions
uv build
uvx twine check dist/*Test the wheel in a clean environment:
uv venv --python 3.12 .wheel-test
uv pip install --python .wheel-test dist/*.whl
.wheel-test/bin/python -c "import weather_tools"On Windows, the final executable is .wheel-test\Scripts\python.exe.
Package design guidance
- Keep import-time work minimal. Do not open network or database connections in
__init__.py. - Expose a small public API; internal modules may change without breaking consumers.
- Use relative imports within one package and absolute imports across top-level packages.
- Include non-Python assets explicitly through the selected build backend.
- Derive versions from one source. Duplicated versions drift.
- Separate configuration from secrets; never package
.envfiles.
Common pitfalls
“It works from the repository root”
The current directory is on sys.path, so a missing installation can remain hidden. Run tests
against an installed package and test a built wheel before release.
Unbounded or exact library dependencies
No upper bound can admit a future breaking release; exact pins make dependency resolution hostile for consumers. Choose ranges based on upstream compatibility guarantees and your test matrix.
Import package name differs from distribution name
Hyphens in weather-tools become underscores in import weather_tools. Document both names where
users install and import the package.
Updating without reviewing
uv lock --upgrade
uv tree
uv run pytestReview lockfile changes and release notes. “Latest” is a moving set of behavior.
Practice
- Create a
src-layout package with one console script. - Add a runtime dependency and a development-only dependency; locate each in
pyproject.toml. - Build a wheel and import it from a clean environment outside the repository.
- Add one optional feature and verify both base and feature installations.
- Intentionally make the lockfile stale and observe
uv sync --lockedfail.
LEARNING RECORD
Finish this lesson
Mark it complete when you can explain the main decision without looking.
KNOWLEDGE CHECK