Why Virtual Environments
Without virtual environments, all projects share the same global Python installation. This causes problems:
# Project A needs requests 2.28
# Project B needs requests 2.31
# You can't have both installed globally!
# Also: installing packages globally requires admin/sudo
# and can conflict with system Python packages on Linux/macOS
Virtual environments solve this by giving each project its own isolated Python with its own packages:
project-a/
.venv/ # has requests 2.28
src/
requirements.txt
project-b/
.venv/ # has requests 2.31
src/
requirements.txt
venv Basics
Create and activate a virtual environment - the pattern is the same on all platforms:
# Create a virtual environment named .venv
python3 -m venv .venv
# Activate
source .venv/bin/activate
# Your prompt changes to show (.venv)
# (.venv) $ which python
# /path/to/project/.venv/bin/python
# Deactivate
deactivate
# Create
python -m venv .venv
# Activate (PowerShell)
.venv\Scripts\Activate.ps1
# Activate (cmd.exe)
.venv\Scripts\activate.bat
# Deactivate
deactivate
Specify a Python version when creating the environment:
# Use a specific Python version (must be installed)
python3.12 -m venv .venv
# Verify
python --version # Python 3.12.x
# Create without pip (faster, add pip manually if needed)
python -m venv .venv --without-pip
# Create with system site-packages access
python -m venv .venv --system-site-packages
The conventional name is .venv (or venv). Many tools (VS Code, PyCharm, GitHub Copilot) detect .venv automatically. Add it to .gitignore immediately: echo ".venv/" >> .gitignore. Never commit the virtual environment directory.
pip and Packages
With your virtual environment activated, pip installs into it - not the global Python:
# Install a package
pip install requests
# Install a specific version
pip install requests==2.31.0
# Install minimum version
pip install "requests>=2.28"
# Install multiple packages
pip install flask sqlalchemy pytest
# Upgrade a package
pip install --upgrade requests
# Uninstall
pip uninstall requests
# List installed packages
pip list
# Show package details
pip show requests
# Search PyPI (deprecated in pip 21+, use pypi.org instead)
# pip search requests
# Install from git repository
pip install git+https://github.com/user/repo.git
# Install from local directory (editable/development mode)
pip install -e .
# Install from a wheel file
pip install mypackage-1.0.0-py3-none-any.whl
# Upgrade pip itself
python -m pip install --upgrade pip
requirements.txt
requirements.txt records your project's dependencies so others can recreate the exact environment:
# Snapshot current environment (pinned versions)
pip freeze > requirements.txt
# Install from requirements.txt
pip install -r requirements.txt
# Install and upgrade all packages from file
pip install --upgrade -r requirements.txt
A well-organised requirements setup separates production and dev dependencies:
# requirements.txt - production dependencies
flask==3.0.3
sqlalchemy==2.0.30
requests==2.32.3
# requirements-dev.txt - dev/test dependencies
-r requirements.txt # include production deps
pytest==8.2.1
pytest-cov==5.0.0
mypy==1.10.0
black==24.4.2
# Production install
pip install -r requirements.txt
# Development install
pip install -r requirements-dev.txt
pip freeze includes ALL installed packages including transitive dependencies - making it hard to see which are your direct dependencies. pip-tools separates this: write direct deps in requirements.in, run pip-compile to produce a pinned requirements.txt. This makes upgrading individual packages easier.
pyenv - Multiple Python Versions
pyenv lets you install and switch between multiple Python versions on one machine:
# Install pyenv (macOS via Homebrew)
brew install pyenv
# Install pyenv (Linux)
curl https://pyenv.run | bash
# Add to shell profile (.bashrc / .zshrc)
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
# List available Python versions
pyenv install --list
# Install a specific Python version
pyenv install 3.12.4
pyenv install 3.11.9
# List installed versions
pyenv versions
# Set global Python version (affects all shells)
pyenv global 3.12.4
# Set local version for current directory (creates .python-version file)
pyenv local 3.11.9
# Set version just for current shell
pyenv shell 3.10.14
# Check active version
python --version
pyenv version
Combine pyenv with venv for per-project environments with specific Python versions:
cd my-project
pyenv local 3.12.4 # use Python 3.12 in this directory
python -m venv .venv # create venv using the local Python
source .venv/bin/activate
python --version # Python 3.12.4
Poetry
Poetry is a modern dependency manager that combines venv creation, dependency resolution, and packaging in one tool:
# Install Poetry (Linux/macOS/WSL)
curl -sSL https://install.python-poetry.org | python3 -
# Windows (PowerShell)
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
# Create a new project
poetry new my-project
# Or initialise in an existing directory
cd my-project
poetry init
Poetry creates and manages pyproject.toml and poetry.lock:
# Add a dependency (updates pyproject.toml + poetry.lock)
poetry add requests
poetry add "requests>=2.28"
# Add a dev dependency
poetry add --group dev pytest mypy
# Install all dependencies (creates .venv automatically)
poetry install
# Install only production dependencies
poetry install --without dev
# Update all dependencies
poetry update
# Show installed packages
poetry show
# Run a command inside the venv
poetry run python script.py
poetry run pytest
Sample pyproject.toml managed by Poetry:
[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "A sample project"
authors = ["Alice "]
[tool.poetry.dependencies]
python = "^3.12"
requests = "^2.31"
click = "^8.1"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
mypy = "^1.10"
black = "^24.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
| Tool | Best for |
|---|---|
| venv + pip | Simple projects, scripts, learning - built-in, no extra install |
| pip-tools | Production apps needing pinned transitive deps without Poetry overhead |
| Poetry | Libraries and applications with publishing workflow |
| uv | Extremely fast (Rust-based) drop-in for pip + venv (2024+) |
| pyenv | Managing multiple Python versions on one machine |