Python - Virtual Environments (venv)

Python packages install globally by default, creating a shared dependency pool across all projects. This causes three critical problems: dependency conflicts when projects require different versions...

Key Insights

  • Virtual environments isolate project dependencies, preventing version conflicts between different Python projects and protecting your system Python installation from corruption
  • Python’s built-in venv module provides a lightweight, standardized solution that works across platforms without requiring third-party tools
  • Proper virtual environment workflows involve creating isolated environments per project, activating them before work, and maintaining requirements files for reproducibility

Why Virtual Environments Matter

Python packages install globally by default, creating a shared dependency pool across all projects. This causes three critical problems: dependency conflicts when projects require different versions of the same package, contamination of the system Python installation with project-specific packages, and inability to reproduce environments across machines or deployment targets.

Virtual environments solve this by creating isolated Python installations per project. Each environment contains its own Python binary, pip, and installed packages, completely separate from the system Python and other virtual environments.

# Without virtual environments - global installation
$ pip install requests==2.28.0
$ pip install flask  # May require requests==2.31.0, causing conflict

# With virtual environments - isolated installations
$ python -m venv project1_env
$ source project1_env/bin/activate
(project1_env) $ pip install requests==2.28.0

$ python -m venv project2_env
$ source project2_env/bin/activate
(project2_env) $ pip install requests==2.31.0  # No conflict

Creating Virtual Environments

The venv module ships with Python 3.3+, requiring no additional installation. Create environments using python -m venv followed by the target directory name.

# Basic creation
python -m venv myenv

# Common naming conventions
python -m venv venv      # Most common
python -m venv .venv     # Hidden directory
python -m venv env       # Alternative

# Specify Python version explicitly
python3.11 -m venv myenv

# Create with system site-packages access (not recommended)
python -m venv --system-site-packages myenv

# Create without pip (minimal environment)
python -m venv --without-pip myenv

The created directory contains:

myenv/
├── bin/          # Scripts (activate, pip, python)
├── include/      # C headers for compiling packages
├── lib/          # Installed packages
└── pyvenv.cfg    # Configuration file

On Windows, the structure differs slightly with Scripts/ instead of bin/.

Activating and Deactivating

Activation modifies your shell environment to prioritize the virtual environment’s Python and packages. The process varies by operating system and shell.

# Linux/macOS - bash/zsh
source myenv/bin/activate

# Linux/macOS - fish shell
source myenv/bin/activate.fish

# Linux/macOS - csh
source myenv/bin/activate.csh

# Windows - Command Prompt
myenv\Scripts\activate.bat

# Windows - PowerShell
myenv\Scripts\Activate.ps1

After activation, your prompt changes to indicate the active environment:

$ source myenv/bin/activate
(myenv) $ which python
/path/to/project/myenv/bin/python

(myenv) $ python --version
Python 3.11.4

Deactivation returns your shell to the system Python:

(myenv) $ deactivate
$ which python
/usr/bin/python

Managing Dependencies

Track project dependencies using requirements.txt files. This enables environment reproduction across machines and deployment pipelines.

# Install packages in active environment
(myenv) $ pip install requests flask sqlalchemy

# Generate requirements file
(myenv) $ pip freeze > requirements.txt

The generated requirements.txt:

certifi==2023.7.22
charset-normalizer==3.2.0
click==8.1.6
Flask==2.3.2
greenlet==2.0.2
idna==3.4
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
requests==2.31.0
SQLAlchemy==2.0.19
typing_extensions==4.7.1
urllib3==2.0.4
Werkzeug==2.3.6

Recreate environments from requirements files:

# New machine or fresh environment
python -m venv newenv
source newenv/bin/activate
(newenv) $ pip install -r requirements.txt

Pin specific versions for reproducibility:

# requirements.txt with pinned versions
requests==2.31.0
flask==2.3.2
sqlalchemy==2.0.19

# Allow minor version updates
requests>=2.31.0,<3.0.0
flask~=2.3.0  # Equivalent to >=2.3.0,<2.4.0

Development vs Production Dependencies

Separate development tools from production dependencies using multiple requirements files:

# requirements.txt (production)
flask==2.3.2
sqlalchemy==2.0.19
psycopg2-binary==2.9.6

# requirements-dev.txt (development)
-r requirements.txt  # Include production dependencies
pytest==7.4.0
black==23.7.0
flake8==6.0.0
mypy==1.4.1

Install both sets during development:

(myenv) $ pip install -r requirements-dev.txt

Virtual Environment Workflows

Integrate virtual environments into project workflows with consistent patterns:

# Starting a new project
mkdir myproject && cd myproject
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install <initial-packages>
pip freeze > requirements.txt

# Joining an existing project
git clone <repository>
cd <project>
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Daily work
cd myproject
source .venv/bin/activate  # Always activate first
# ... do work ...
deactivate  # When switching projects

Add virtual environment directories to .gitignore:

# .gitignore
venv/
.venv/
env/
ENV/
*.pyc
__pycache__/

Automation with Scripts

Create activation scripts for consistent environment setup:

#!/bin/bash
# setup.sh

if [ ! -d ".venv" ]; then
    echo "Creating virtual environment..."
    python3 -m venv .venv
fi

echo "Activating virtual environment..."
source .venv/bin/activate

echo "Upgrading pip..."
pip install --upgrade pip

if [ -f "requirements.txt" ]; then
    echo "Installing dependencies..."
    pip install -r requirements.txt
fi

echo "Environment ready!"

Make executable and run:

chmod +x setup.sh
./setup.sh

Troubleshooting Common Issues

PowerShell Execution Policy Error:

# Error: cannot be loaded because running scripts is disabled
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Environment Not Activating:

# Verify Python installation
python --version

# Check venv module availability
python -m venv --help

# Recreate environment if corrupted
rm -rf myenv
python -m venv myenv

Package Installation Failures:

# Upgrade pip first
pip install --upgrade pip setuptools wheel

# Install with verbose output
pip install -v package-name

# Use specific Python version
python3.11 -m pip install package-name

Integration with IDEs

Configure IDEs to recognize virtual environments automatically.

VS Code - .vscode/settings.json:

{
    "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
    "python.terminal.activateEnvironment": true
}

PyCharm automatically detects virtual environments in project roots. Configure manually via Settings → Project → Python Interpreter → Add Interpreter → Existing Environment.

When to Use Alternatives

While venv handles most use cases, consider alternatives for specific scenarios:

  • Poetry: Dependency resolution, lock files, package publishing
  • Conda: Data science, non-Python dependencies, complex binary packages
  • virtualenv: Python 2.7 support, faster environment creation
  • pipenv: Combined dependency management and virtual environments

For standard Python development, venv provides sufficient isolation without additional dependencies or complexity. Use it as the default choice unless specific requirements demand alternative tools.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.