Difficulty: Beginner
Why are virtual environments important? How do you manage Python dependencies?
Virtual environments create isolated Python installations with their own packages, preventing version conflicts between projects.
Without virtual environments: - Project A needs Django 4.0, Project B needs Django 3.2 - conflict! - System Python gets polluted with project-specific packages
Tools: - venv (built-in): creates lightweight virtual environments - pip: installs packages from PyPI - requirements.txt: lists pinned dependencies - pyproject.toml: modern project configuration (PEP 621)
Modern alternatives: Poetry, PDM, uv (fast Rust-based installer).
# Create a virtual environment
# python -m venv myproject_env
# Activate it
# Windows: myproject_env\Scripts\activate
# macOS/Linux: source myproject_env/bin/activate
# Now pip installs go to the venv, not system Python
# pip install requests flask
# Check where Python and packages live
import sys
print(f"Python: {sys.executable}")
print(f"Path: {sys.prefix}")
# List installed packages
# pip list
# pip freeze > requirements.txt
# Install from requirements
# pip install -r requirements.txt
# Deactivate
# deactivate
# Example requirements.txt
requirements = """
flask==3.0.0
requests>=2.28.0,<3.0.0
numpy~=1.24.0
python-dotenv
"""
print(requirements)
# Version specifiers:
# ==3.0.0 exact version
# >=2.28 minimum version
# <3.0.0 maximum version
# ~=1.24 compatible release (>=1.24, <2.0)
# !=1.5 exclude version
Virtual environments isolate dependencies per project. pip freeze captures exact versions for reproducibility. Always commit requirements.txt to version control.
# pyproject.toml (PEP 621) - modern Python project config
pyproject = """
[project]
name = "mypackage"
version = "1.0.0"
description = "My awesome package"
requires-python = ">=3.9"
dependencies = [
"requests>=2.28",
"flask>=3.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"mypy>=1.0",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
[tool.black]
line-length = 88
[tool.mypy]
strict = true
"""
print(pyproject)
# Install with optional dependencies:
# pip install -e . (just main deps)
# pip install -e ".[dev]" (main + dev deps)
# pip-tools for pinning:
# pip-compile pyproject.toml -> requirements.txt
# pip-sync requirements.txt
pyproject.toml replaces setup.py/setup.cfg as the standard project configuration. It configures the project metadata, dependencies, and tool settings in one file.
# 1. Always use virtual environments
# python -m venv .venv
# source .venv/bin/activate
# 2. Pin dependencies for production
# requirements.txt (pinned)
# flask==3.0.0
# requests==2.31.0
# gunicorn==21.2.0
# 3. Separate dev and prod dependencies
# requirements.txt -> production
# requirements-dev.txt -> development (includes testing, linting)
# requirements-dev.txt
dev_requirements = """
-r requirements.txt
pytest==7.4.0
black==23.7.0
flake8==6.1.0
mypy==1.5.0
"""
print(dev_requirements)
# 4. Check for vulnerabilities
# pip audit
# safety check
# 5. Modern tools comparison
tools = {
'pip + venv': 'Built-in, simple, manual pinning',
'Poetry': 'All-in-one: venv, deps, building, publishing',
'PDM': 'PEP 621 compliant, fast, modern',
'uv': 'Rust-based, extremely fast pip replacement',
'pip-tools': 'pip-compile + pip-sync for pinning',
}
for tool, desc in tools.items():
print(f"{tool:15} -> {desc}")
Pin exact versions for production reproducibility. Use pip-compile or Poetry lock files to generate pinned requirements from loose constraints.
venv, pip, requirements.txt, pyproject.toml, Dependency Management