Python Environments: venv vs Poetry
Python's packaging story has stabilized in the last few years. There is no single answer to "how should I manage dependencies," but there are three reasonable answers, and a small number of mistakes that consistently waste an afternoon. This is what to use and why.
venv: the standard library answer
Since Python 3.3, venv ships with the interpreter. It creates a directory with its own python binary and site-packages. Activate it, and pip install writes there instead of system Python.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
This is fine for solo scripts and small projects. The pain shows up when you want a lockfile, deterministic builds, or one tool that publishes to PyPI.
pip-tools: the minimal upgrade
If you like venv but want a lockfile, pip-tools gives you pip-compile. You write a requirements.in with top-level deps; pip-compile resolves and pins the full tree into requirements.txt.
# requirements.in
fastapi
sqlalchemy[asyncio]
pytest
# Compile
pip-compile requirements.in
pip-sync requirements.txt
Poetry: dependencies + packaging + publish
Poetry is a single tool for dependency management, virtual environment management, and PyPI publishing. Its pyproject.toml + poetry.lock pair is the closest Python comes to Cargo.toml + Cargo.lock.
poetry new my-project
cd my-project
poetry add fastapi sqlalchemy
poetry add --group dev pytest ruff
poetry install
poetry run pytest
poetry shell
The killer feature is the resolver: Poetry will refuse to install a dependency set that can't be resolved, instead of silently picking an old version like raw pip does.
uv: the fast newcomer
Astral's uv (the same people who make ruff) is a Rust-written drop-in replacement for pip + venv + pip-tools. It's 10-100× faster and has been stable enough for production for over a year now. If you're starting fresh and don't need Poetry's publishing workflow, uv is the boring default.
uv venv
uv pip install -r requirements.txt
uv pip compile requirements.in -o requirements.txt
Per-project Python versions
pyenv manages multiple Python interpreters, with a .python-version file selecting one per directory. Combine with uv or Poetry and you have a clean per-project setup.
The mistakes to avoid
- Don't
pip installinto system Python. Most distros now block this with PEP 668. Usepipxfor CLI tools, venv for everything else. - Don't commit your
.venv/. Add it to.gitignorelike you wouldnode_modules/. - Don't trust
pip freezeas a lockfile. It captures the resolved tree but not the constraints — re-runningpip install -rfrom a frozen file is reproducible only on the same Python version with the same wheels available.