Engineering Notes
Home/Languages/Python/Packaging, Dependencies and Environments
Languages · Python

Packaging, Dependencies and Environments

Manage Python dependencies like a professional: virtual environments, pyproject.toml, version specifiers, lock files for reproducible builds, and the modern tool landscape from pip-tools to Poetry and uv.

💻 Languages● Intermediate⏱ 3 min read🛠️ 2 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • Virtual environments and why they matter
  • pyproject.toml and project metadata
  • Version specifiers and compatible releases
  • Lock files, wheels and publishing

📚 Before you start

1Virtual Environments

Every project gets its own isolated environment so dependency versions don't clash:

bash
python -m venv .venv && source .venv/bin/activate    # built in
uv venv && uv pip install -r requirements.txt         # uv: very fast installer and resolver

Never install project dependencies into the system Python. In containers, the image is the environment (but a venv inside the image still helps with multi-stage builds).

2pyproject.toml

The modern single source of project metadata (PEP 621):

toml
[project]
name = "orders-service"
version = "1.4.0"
requires-python = ">=3.11"
dependencies = ["fastapi>=0.110,<1", "sqlalchemy~=2.0", "httpx>=0.27"]

[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]

[project.scripts]
orders = "orders.cli:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Tool configuration (ruff, mypy, pytest) also lives here.

3Version Specifiers

SpecifierMeaning
==2.1.3Exactly this version
>=2.1,<3At least 2.1, below 3
~=2.1Compatible release: >=2.1, ==2.*
~=2.1.3>=2.1.3, ==2.1.*
!=2.1.5Exclude a broken release

Libraries should use ranges (so they compose with other libraries); applications should lock exact versions for reproducible builds.

4Locking

A lock file pins the entire dependency tree (including transitive dependencies) with hashes: uv lock / uv.lock, poetry.lock, pip-compile → requirements.txt with == pins. CI and Docker install from the lock (uv sync --frozen, pip install --require-hashes -r requirements.txt). Update deliberately (Dependabot/Renovate PRs) and run tests.

5Tools at a Glance

ToolRole
pip + venvBaseline installer and environments
pip-toolsCompile pinned requirements from loose ones
PoetryDependency management, locking, building, publishing
uvFast all-in-one: venvs, resolver, lock files, Python versions, tools
pipx / uv toolInstall CLI apps in isolated environments
pyenv / uv pythonManage multiple Python versions

6Building and Publishing

python -m build produces a source distribution (sdist) and a wheel (a prebuilt zip installed without running setup code). Platform-specific wheels contain compiled extensions. Publish with twine upload or uv publish, ideally via trusted publishing from CI.

⚠️ Common Pitfalls

  1. Installing into the global interpreter or sharing one venv between projects.
  2. Unpinned application dependencies: a transitive release breaks production.
  3. Pinning exact versions in libraries: impossible to install alongside others.
  4. Committing the .venv directory.
  5. Different Python versions locally vs in CI/production.

📋 Quick Reference

text
one venv per project (python -m venv / uv venv) · pyproject.toml = metadata + deps + tool config
libraries: ranges (>=,<, ~=) · apps: lock everything (uv.lock / poetry.lock / pip-compile) with hashes
~=2.1 means >=2.1,==2.* · install from the lock in CI and Docker · wheels > sdists
uv / Poetry / pip-tools · pipx for CLIs · same Python version everywhere

🧠 Key takeaways

  • One virtual environment per project; never the system Python.
  • Libraries declare ranges; applications lock the full tree with hashes.
  • ~=2.1 means >=2.1, ==2.*: a compatible release.
  • Install from the lock in CI and Docker; keep Python versions consistent.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1Why use virtual environments?core▾

To isolate each project's dependencies and Python version from others and from the system interpreter, preventing version conflicts and making environments reproducible.

Follow-ups interviewers ask
  • In Docker? The image isolates, but a venv still helps multi-stage builds.
Q2How do you make Python builds reproducible?core▾

Declare dependencies in pyproject.toml, generate a lock file of the full resolved tree with hashes (uv.lock, poetry.lock, pip-compile), install from it in CI/Docker with frozen/require-hashes options, and pin the Python version.

Follow-ups interviewers ask
  • How do you update? Automated PRs (Renovate/Dependabot) that regenerate the lock and run tests.
Q3Libraries vs applications: how should dependencies differ?core▾

Libraries specify compatible ranges so they can coexist with other libraries in users' environments; applications lock exact versions for deterministic deployments.

Follow-ups interviewers ask
  • Upper bounds in libraries? Debated; avoid overly tight caps.
Q4What does uv bring?senior▾

A very fast Rust-based tool replacing pip, pip-tools, virtualenv, pipx and pyenv functions: resolution, lock files, venv management, Python installs, and running tools.

Follow-ups interviewers ask
  • Compatible with pip? uv pip offers a pip-compatible interface.
Q5What's the difference between an sdist and a wheel?core▾

An sdist is source code that may need building (compiling extensions) at install time; a wheel is a prebuilt archive installed by unpacking, faster and without build tools. Platform wheels include compiled binaries.

Follow-ups interviewers ask
  • manylinux? Wheel tags for Linux binaries compatible across distributions.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. What does ~=2.1 allow?
Compatible release.
2. Should a library pin exact dependency versions?
Apps lock, libraries range.
3. What does a lock file contain?
Reproducibility.
4. What's a wheel?
Fast installs.