TechLead
Lesson 12 of 25
5 min read
Python

Virtual Environments and pip

Learn to manage Python dependencies with virtual environments, pip, and modern packaging tools

Why Virtual Environments?

Virtual environments create isolated Python installations for each project. Without them, all projects share the same global packages, leading to version conflicts. For example, Project A might need Django 4.2 while Project B needs Django 5.0. Virtual environments solve this by giving each project its own package directory.

The Dependency Problem

Without virtual environments, installing packages globally leads to:

  • Version conflicts: Two projects needing different versions of the same package
  • Reproducibility issues: Cannot guarantee the same environment on another machine
  • System pollution: Global packages cluttering your system Python
  • Permission issues: Needing sudo to install packages globally

Creating and Using Virtual Environments

# Create a virtual environment using venv (built into Python 3.3+)
# python3 -m venv myproject-env

# Activate the virtual environment
# On macOS/Linux:
# source myproject-env/bin/activate

# On Windows:
# myproject-env\Scripts\activate

# Your prompt changes to show the active environment:
# (myproject-env) $

# Install packages (they go into the virtual environment, not globally)
# pip install requests flask sqlalchemy

# Check where Python and pip are located
# which python  -> /path/to/myproject-env/bin/python
# which pip     -> /path/to/myproject-env/bin/pip

# Deactivate when done
# deactivate

# Common convention: name the venv directory .venv
# python3 -m venv .venv
# source .venv/bin/activate

# Add .venv to .gitignore!

pip - The Package Installer

# Install a package
# pip install requests

# Install a specific version
# pip install requests==2.31.0

# Install with version constraints
# pip install "requests>=2.28,<3.0"

# Upgrade a package
# pip install --upgrade requests

# Uninstall a package
# pip uninstall requests

# List installed packages
# pip list

# Show package details
# pip show requests

# Freeze current environment to requirements.txt
# pip freeze > requirements.txt

# Install from requirements.txt
# pip install -r requirements.txt

# Search for packages (use PyPI website instead)
# https://pypi.org/search/

requirements.txt

# requirements.txt - pin exact versions for reproducibility
# requests==2.31.0
# flask==3.0.2
# sqlalchemy==2.0.25
# pydantic==2.5.3

# requirements-dev.txt - development dependencies
# -r requirements.txt   # include production deps
# pytest==7.4.4
# black==24.1.1
# mypy==1.8.0
# ruff==0.1.14

# Use separate files for different environments:
# requirements.txt       - production
# requirements-dev.txt   - development
# requirements-test.txt  - testing

Modern Tools: uv and Poetry

While venv and pip work well, modern tools like uv (from Astral, the creators of Ruff) and Poetry provide better dependency resolution, lock files, and project management.

# uv - extremely fast Python package manager
# Install uv:
# curl -LsSf https://astral.sh/uv/install.sh | sh

# Create a project
# uv init myproject
# cd myproject

# Add dependencies
# uv add requests flask

# Add dev dependencies
# uv add --dev pytest ruff

# Run your project
# uv run python main.py

# Sync dependencies (install from lock file)
# uv sync

# uv is 10-100x faster than pip!

# Poetry - dependency management and packaging
# Install: curl -sSL https://install.python-poetry.org | python3 -

# Create a new project
# poetry new myproject

# Add dependencies
# poetry add requests
# poetry add --group dev pytest

# Install all dependencies
# poetry install

# Run commands in the virtual environment
# poetry run python main.py
# poetry shell  # Activate the virtual environment

pyproject.toml

The pyproject.toml file is the modern standard for Python project configuration. It replaces setup.py, setup.cfg, and requirements.txt with a single, standardized file.

# pyproject.toml
# [project]
# name = "myproject"
# version = "1.0.0"
# description = "My awesome Python project"
# requires-python = ">=3.11"
# dependencies = [
#     "requests>=2.28",
#     "flask>=3.0",
#     "sqlalchemy>=2.0",
# ]
#
# [project.optional-dependencies]
# dev = [
#     "pytest>=7.0",
#     "ruff>=0.1",
#     "mypy>=1.0",
# ]
#
# [tool.ruff]
# line-length = 100
# target-version = "py311"
#
# [tool.pytest.ini_options]
# testpaths = ["tests"]

Key Takeaways

  • Always use virtual environments: Never install packages globally
  • Pin versions: Use exact versions in production requirements
  • Try uv: It is dramatically faster than pip for package management
  • Use pyproject.toml: The modern standard for Python project configuration

Continue Learning