Python Virtual Environments and Dependency Management: venv, pip, uv and Poetry
A virtual environment isolates each Python project's dependencies, preventing version conflicts. It's a fundamental practice in any professional Python project.
1. venv: virtual environments with the standard library
# Create virtual environment
python -m venv .venv
# Activate (Linux/macOS)
source .venv/bin/activate
# Activate (Windows CMD)
.venv\Scripts\activate.bat
# Activate (Windows PowerShell)
.venv\Scripts\Activate.ps1
# Verify the active environment
which python # Linux/macOS
where python # Windows
# Deactivate
deactivate
Convention: name it .venv (with a dot) so VS Code and other tools auto-detect it, and .gitignore excludes it by default.
# .gitignore
.venv/
__pycache__/
*.pyc
dist/
build/
*.egg-info/
2. pip: install and manage packages
pip install requests
pip install requests==2.31.0
pip install "requests>=2.28,<3.0"
pip install --upgrade requests
pip uninstall requests
pip list
pip list --outdated
pip show requests
3. requirements.txt: freeze dependencies
pip freeze > requirements.txt
pip install -r requirements.txt
# Separate by environment
pip install -r requirements.txt # Production
pip install -r requirements-dev.txt # Development
# requirements.txt (production)
requests==2.31.0
Pillow==10.3.0
fastapi==0.111.0
uvicorn==0.30.1
# requirements-dev.txt (development)
-r requirements.txt
pytest==8.2.0
pytest-cov==5.0.0
mypy==1.10.0
ruff==0.4.8
4. uv: the modern fast package manager
uv is a package manager written in Rust, up to 100× faster than pip:
# Install uv
pip install uv
# or
curl -LsSf https://astral.sh/uv/install.sh | sh # Linux/macOS
# Create environment
uv venv .venv
source .venv/bin/activate
# Install packages (much faster than pip)
uv pip install requests fastapi pillow
uv pip sync requirements.txt
uv pip freeze > requirements.txt
# Manage Python versions
uv python install 3.12
uv python list
uv as a full project manager (uv 0.2+)
uv init my-project
cd my-project
# Add dependencies (auto-updates pyproject.toml)
uv add requests fastapi
uv add --dev pytest mypy ruff
# Install all dependencies
uv sync
# Run scripts in project environment
uv run python script.py
uv run pytest
uv run mypy src/
# Dependency tree
uv tree
5. Poetry: full project management
curl -sSL https://install.python-poetry.org | python3 -
poetry new my-package
cd my-package
poetry add requests pillow
poetry add --group dev pytest mypy
poetry install # Install from pyproject.toml + poetry.lock
poetry run pytest
poetry update # Update all dependencies
poetry build # Build distributions
poetry publish # Publish to PyPI
pyproject.toml with Poetry
[tool.poetry]
name = "my-package"
version = "0.1.0"
description = "My package description"
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.28"
Pillow = "^10.0"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
mypy = "^1.0"
ruff = "^0.4"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
6. Tool comparison
| Tool | Speed | Lockfile | Publish | Best for |
|---|---|---|---|---|
| pip + venv | Normal | No native | No | Simple scripts |
| uv + pip | Very fast | Yes (uv.lock) | No | All projects |
| Poetry | Normal | Yes | Yes | Publishable packages |
| conda | Slow | Yes | No | Data science |
2025 recommendation: use uv for everything — install packages, manage Python versions, run scripts.
7. Pinning and lockfiles for reproducibility
# pip-tools approach
pip install pip-tools
pip-compile requirements.in --generate-hashes -o requirements.txt
pip-sync requirements.txt
# uv approach (automatic)
uv lock # Generates uv.lock
uv sync # Installs exactly what's in lockfile
# requirements.in (direct deps only, no pinned versions)
requests>=2.28
Pillow>=10.0
fastapi>=0.111
8. Dev vs production dependencies
pip install -r requirements.txt # Production only
uv sync --no-dev # Exclude dev deps
poetry install --without dev # Poetry equivalent
9. Best practices
- Always use a virtual environment per project — never install globally.
- Add
.venv/to.gitignore— environments aren't shared, lockfiles are. - Commit lockfiles (
poetry.lock,uv.lock) for exact reproducibility. - Separate dev from production:
pytest,mypy,ruffdon't belong in production. - Update regularly:
uv lock --upgradeorpoetry updatefor security patches.
10. Tool configuration in pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=src --cov-report=term-missing -v"
[tool.mypy]
strict = true
ignore_missing_imports = true
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "UP"]
[tool.ruff.format]
quote-style = "double"
uv run pytest # Tests + coverage
uv run mypy src/ # Type checking
uv run ruff check . # Linting
uv run ruff format . # Formatting
Related developer conversions
Python projects often produce documents and reports — these are the conversions Python developers automate most:
- Markdown to PDF — generate release notes & docs from
.md - Markdown to DOCX — share editable docs with non-developers
- JSON to CSV — quick data exports from a
requirements.json - CSV to JSON — feed CI/test fixtures
- HTML to PDF — Sphinx/MkDocs build outputs
- DOCX to PDF — finalize generated reports