Introduction to Virtual Environments
Welcome to our deep dive into Python virtual environments! While we've already set up virtual environments earlier this week, today we'll explore why they're so important and learn best practices for using them effectively in professional development.
Think of virtual environments as isolated workspaces for your Python projects. Just as a chef keeps ingredients for different dishes separate to avoid cross-contamination, virtual environments keep your project dependencies separate to avoid conflicts. This isolation is crucial for creating reproducible, reliable, and shareable Python applications.
Why Virtual Environments are Essential
Before diving into best practices, let's understand the core problems that virtual environments solve:
1. Dependency Isolation
Different projects often require different versions of the same package. Without virtual environments, this is impossible to manage in a single Python installation.
Real-World Scenario: Project A requires Django 2.2 (which supports Python 3.5+), while Project B needs Django 3.2 (which requires Python 3.6+). Without virtual environments, you'd need to constantly uninstall and reinstall different versions, or compromise on one project's requirements.
2. System Python Protection
Your operating system might rely on specific Python packages. Modifying these for your project could break system functionality.
Real-World Scenario: Many Linux distributions use Python for system utilities. Installing conflicting packages globally could render your package manager or other system tools inoperable.
3. Clean Dependency Tracking
Virtual environments make it easy to track exactly what packages your project needs.
Real-World Scenario: When a new developer joins your team, they can recreate your exact environment without guessing which packages to install.
4. Reproducible Builds
Ensures that your application works the same way everywhere—development, testing, and production.
Real-World Scenario: Avoiding the dreaded "it works on my machine" problem when deploying to production or sharing with teammates.
Virtual Environment Tools Comparison
Several tools exist for creating and managing Python virtual environments. Let's compare the most common ones:
| Tool | Pros | Cons | Best For |
|---|---|---|---|
venv |
|
|
Simple projects and learning Python |
virtualenv |
|
|
Projects supporting legacy Python |
conda |
|
|
Data science and scientific computing |
pipenv |
|
|
Application development with dependency locking |
poetry |
|
|
Library development and modern applications |
For the remainder of this course, we'll primarily use venv since it's built into Python and sufficient for our needs. However, we'll note where other tools might offer advantages in specific scenarios.
Analogy: Virtual environment tools are like different types of kitchen organization systems—some are simple drawer dividers (venv), some are specialized spice racks (virtualenv), and others are complete kitchen remodels with custom cabinets (conda, pipenv, poetry). The best choice depends on what you're cooking and how often.
Quick Review: Creating and Using Virtual Environments with venv
Let's quickly review the basic commands for working with venv:
Creating a Virtual Environment
# On Windows
python -m venv myenv
# On macOS/Linux
python3 -m venv myenv
Activating a Virtual Environment
# On Windows
myenv\Scripts\activate
# On macOS/Linux
source myenv/bin/activate
Deactivating a Virtual Environment
deactivate
Installing Packages in a Virtual Environment
# Make sure the environment is activated first
pip install package_name
# Install multiple packages
pip install package1 package2 package3
# Install with specific version
pip install package==1.2.3
# Install from requirements file
pip install -r requirements.txt
Capturing Dependencies
# Create or update requirements.txt
pip freeze > requirements.txt
Now, let's move beyond the basics to best practices that will make your virtual environment workflow more effective.
Virtual Environment Naming and Location
Naming Conventions
Consistent naming helps you and your team identify environments quickly:
- Project-Based Naming: Name environments after your project, possibly with a suffix indicating purpose or Python version.
- Version-Based Naming: Include Python version if you work with multiple versions.
Examples of good environment names:
myapp-env- Basic project environmentmyapp-3.9- Project environment for Python 3.9myapp-dev/myapp-prod- Separate environments for development/production settings
Environment Location
Where you place your virtual environments matters:
Option 1: Inside Project Directory (Recommended for Most Projects)
my_project/
├── .git/
├── venv/ # Virtual environment
├── src/
├── tests/
├── requirements.txt
└── README.md
Pros:
- Self-contained project directory
- Clear association between project and environment
- Easy to delete/recreate along with the project
Cons:
- Takes up space in version control (if not properly ignored)
- Can accidentally include environment files in project
Option 2: Central Environment Directory
~/.virtualenvs/
├── project1-env/
├── project2-env/
└── project3-env/
Pros:
- Keeps projects clean
- Easier to manage multiple environments
- No risk of accidentally versioning environment files
Cons:
- Environments can become "orphaned" when projects are deleted
- Less intuitive for beginners
- Requires remembering which environment goes with which project
Environment Location Best Practices
- For individual projects: Inside project directory, add to
.gitignore - For many related projects: Central directory with consistent naming
- For team environments: Document the convention you choose
Dependency Management Best Practices
Use Requirements Files Effectively
Requirements files (requirements.txt) are the standard way to capture and share dependencies. Here are best practices for using them:
1. Use Version Pinning
Always specify exact versions to ensure reproducible environments:
# Not recommended (loose dependencies)
requests
flask
sqlalchemy
# Recommended (pinned versions)
requests==2.28.1
flask==2.2.2
sqlalchemy==1.4.41
2. Organize Multiple Requirements Files
For complex projects, split requirements by environment:
# requirements/
# ├── base.txt - Core dependencies for all environments
# ├── development.txt - Additional tools for development
# ├── production.txt - Production-specific packages
# └── testing.txt - Testing-specific packages
# Example of development.txt
-r base.txt # Include base requirements
pytest==7.1.3
black==22.8.0
flake8==5.0.4
3. Include Comments
Document why certain packages are needed or specific versions are required:
# Web framework
flask==2.2.2
# ORM for database access
sqlalchemy==1.4.41
# JWT-based authentication
# Pinned to 2.4.0 because 2.5.0 has a security issue
pyjwt==2.4.0
# Date handling utilities
python-dateutil==2.8.2
4. Consider Requirements.in Files
For more control, separate direct dependencies from their sub-dependencies using pip-tools:
# requirements.in - Only direct dependencies
flask>=2.2.0
sqlalchemy>=1.4.0
requests>=2.27.0
# Generate full requirements.txt with pinned versions
# pip-compile requirements.in > requirements.txt
Beyond requirements.txt
Modern Python projects often use more sophisticated dependency management:
1. Pipenv's Pipfile and Pipfile.lock
# Install pipenv
pip install pipenv
# Create new environment and Pipfile
pipenv install flask sqlalchemy
# Add development dependencies
pipenv install --dev pytest black
# Generate lock file
pipenv lock
The resulting Pipfile separates direct dependencies from dev dependencies:
[packages]
flask = "*"
sqlalchemy = "*"
[dev-packages]
pytest = "*"
black = "*"
[requires]
python_version = "3.9"
2. Poetry's pyproject.toml
# Install poetry
pip install poetry
# Initialize a new project
poetry new myproject
cd myproject
# Add dependencies
poetry add flask sqlalchemy
# Add development dependencies
poetry add --dev pytest black
# The resulting pyproject.toml:
[tool.poetry.dependencies]
python = "^3.9"
flask = "^2.2.2"
sqlalchemy = "^1.4.41"
[tool.poetry.dev-dependencies]
pytest = "^7.1.3"
black = "^22.8.0"
Dependency Management Best Practices Summary
- Always pin versions for reproducible builds
- Document dependencies with comments
- Separate development dependencies from production ones
- Regularly update dependencies for security fixes
- Consider modern tools for complex projects
Virtual Environment Workflows
Environment Setup Workflow
Follow these steps when setting up a new project:
- Create project directory
mkdir myproject cd myproject - Initialize version control
git init echo "venv/" >> .gitignore # Ignore virtual environment directory - Create virtual environment
python -m venv venv - Activate the environment
source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows - Install initial dependencies
pip install flask sqlalchemy - Freeze dependencies
pip freeze > requirements.txt - Create initial project structure
mkdir src tests touch README.md - Make initial commit
git add . git commit -m "Initial project setup with virtual environment"
Joining an Existing Project Workflow
When joining a project that already has a defined environment:
- Clone the repository
git clone https://github.com/username/project.git cd project - Create virtual environment
python -m venv venv - Activate the environment
source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows - Install dependencies
pip install -r requirements.txt - Verify installation
pip list python -c "import flask; print(flask.__version__)"
Environment Maintenance Workflow
Regular maintenance tasks to keep your environment healthy:
- Adding new dependencies
pip install new-package pip freeze > requirements.txt git add requirements.txt git commit -m "Add new-package dependency" - Updating dependencies
# Careful approach: update one package at a time pip install --upgrade package-name pip freeze > requirements.txt # Bulk update (use with caution!) pip install --upgrade -r requirements.txt pip freeze > requirements.txt - Recreating from scratch (when things get messy)
deactivate rm -rf venv/ # macOS/Linux rmdir /s /q venv # Windows python -m venv venv source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows pip install -r requirements.txt
Virtual Environments and Docker
As projects grow, you may use Docker alongside or instead of virtual environments. Understanding how they relate is important:
Differences Between Virtual Environments and Docker
| Aspect | Virtual Environments | Docker |
|---|---|---|
| Isolation level | Python packages only | Full system isolation (OS, files, network) |
| System dependencies | Relies on host system | Self-contained, including OS libraries |
| Portability | Works only on compatible Python versions | Works the same on any Docker-compatible system |
| Resource usage | Very lightweight | More resource-intensive |
| Learning curve | Simple, Python-specific | Steeper, requires container knowledge |
When to Use Each
- Use virtual environments when:
- Working on simple Python projects
- You have a consistent development environment
- You want minimal overhead
- There are few or no system-level dependencies
- Use Docker when:
- Your application has complex system dependencies
- You need to match production environment exactly
- Your team uses different operating systems
- You have microservices or multi-container applications
Using Both Together
Many developers use both tools in their workflow:
- Virtual environment for daily development and testing
- Docker for integration testing and deployment
A common pattern is to maintain both a requirements.txt and a Dockerfile:
# Developer workflow
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python manage.py runserver
# Integration/deployment workflow
docker build -t myapp .
docker run -p 8000:8000 myapp
Example Dockerfile that uses the same requirements file:
FROM python:3.9-slim
WORKDIR /app
# Copy requirements first (for better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Run the application
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
Recommendation: As you progress in this course, you'll work with both virtual environments and Docker. Learn to use them appropriately for different scenarios, and understand how they complement each other in a professional development workflow.
Advanced Virtual Environment Practices
Environment Variables and Settings
Keep configuration out of your code using environment variables:
1. Use .env files (with python-dotenv)
# Install python-dotenv
pip install python-dotenv
# Create .env file (NEVER commit to version control)
echo "DEBUG=True
SECRET_KEY=development-key-only
DATABASE_URL=sqlite:///dev.db" > .env
# Add to .gitignore
echo ".env" >> .gitignore
Using the variables in your code:
# config.py
import os
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
SECRET_KEY = os.getenv("SECRET_KEY")
DATABASE_URL = os.getenv("DATABASE_URL")
2. Environment-specific settings
Create different settings files or environment variable sets for different environments:
environments/
├── .env.development
├── .env.testing
└── .env.production
Loading a specific environment:
# Load specific environment
from dotenv import load_dotenv
# Choose environment based on ENV variable
env_file = f".env.{os.getenv('ENV', 'development').lower()}"
load_dotenv(env_file)
Virtual Environment Helpers
Tools to make virtual environment usage more convenient:
1. direnv
Automatically activates environments when entering directories:
# Install direnv (macOS)
brew install direnv
# Add to shell configuration
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc # or .zshrc
# Create .envrc file in project directory
echo 'source venv/bin/activate' > .envrc
direnv allow
Now the environment activates automatically when you cd into the project directory!
2. virtualenvwrapper
Simplifies management of multiple virtual environments:
# Install virtualenvwrapper
pip install virtualenvwrapper
# Add to shell configuration
echo 'export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/projects
source /usr/local/bin/virtualenvwrapper.sh' >> ~/.bashrc
# Create and work on environments
mkvirtualenv myproject
workon myproject
deactivate
3. IDE Integration
Most modern IDEs have excellent virtual environment support:
- VS Code: Automatically detects environments and prompts to use them
- PyCharm: Built-in virtual environment management
- Jupyter: Can add environments as kernels with
ipykernel
Handling Python Versions
Sometimes you need to work with multiple Python versions:
1. pyenv for Python Version Management
# Install pyenv (macOS)
brew install pyenv
# Install different Python versions
pyenv install 3.8.12
pyenv install 3.9.7
pyenv install 3.10.2
# Set global default
pyenv global 3.9.7
# Set version for a specific project
cd myproject
pyenv local 3.8.12
# Create virtual environment with specific version
pyenv local 3.10.2
python -m venv venv # Uses Python 3.10.2
2. tox for Testing Multiple Python Versions
Automatically test your code against multiple Python versions:
# Install tox
pip install tox
# Create tox.ini
[tox]
envlist = py38,py39,py310
[testenv]
deps = pytest
commands = pytest
Run tests in all environments with a single command:
tox
Troubleshooting Virtual Environments
Common Issues and Solutions
1. "Command not found" after activation
Symptom: You activate an environment, but commands like pip or python aren't found.
Solution:
# Check if the environment has the expected structure
ls -la venv/bin # macOS/Linux
dir venv\Scripts # Windows
# If missing, recreate the environment
python3 -m venv --clear venv
2. Packages installed but not available
Symptom: You install a package with pip, but import fails.
Solution:
# Check if you have multiple Python installations
which python # macOS/Linux
where python # Windows
# Verify the environment is activated (look for "(venv)" in prompt)
# Check where pip is installing packages
pip --version
# Install explicitly to the environment
python -m pip install package_name
3. Environment activation doesn't work
Symptom: Activation script fails or doesn't change the environment.
Solution:
# Check activation script permissions
chmod +x venv/bin/activate # macOS/Linux
# Try absolute path
source /full/path/to/venv/bin/activate # macOS/Linux
C:\full\path\to\venv\Scripts\activate # Windows
# Check for script errors
cat venv/bin/activate # macOS/Linux
type venv\Scripts\activate # Windows
4. "pip install" fails for compiled packages
Symptom: Packages with C extensions fail to install with compiler errors.
Solution:
# Install development tools
# On Ubuntu/Debian
sudo apt-get install python3-dev build-essential
# On macOS
xcode-select --install
# On Windows
# Install Visual Studio Build Tools
# Try binary wheels instead of source
pip install --only-binary=:all: package_name
5. Virtual environment gets huge
Symptom: Environment directory takes up gigabytes of space.
Solution:
# Clear pip cache
pip cache purge
# Remove __pycache__ directories
find venv -name __pycache__ -type d -exec rm -rf {} + # macOS/Linux
# Sometimes it's faster to recreate
pip freeze > requirements.txt
deactivate
rm -rf venv
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Debugging Techniques
- Check environment paths
python -c "import sys; print(sys.path)" - Verify package installation location
pip show package_name - Check environment interpreter
python -c "import sys; print(sys.executable)" - Run pip with verbosity
pip install -v package_name - Verify active Python version
python --version
Virtual Environments in Team Settings
Establishing Team Conventions
Consistent practices are essential for team productivity:
- Document environment setup in README.md
# Development Setup ## Prerequisites - Python 3.9+ - Git ## Environment Setup 1. Clone the repository ``` git clone https://github.com/team/project.git cd project ``` 2. Create a virtual environment ``` python -m venv venv source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows ``` 3. Install dependencies ``` pip install -r requirements.txt ``` 4. Set up environment variables ``` cp .env.example .env # Edit .env with your local settings ``` 5. Run initialization script ``` python setup.py ``` - Use setup scripts for complex initialization
# setup.py #!/usr/bin/env python """Project setup script""" import os import subprocess def main(): """Run setup steps""" print("Setting up development environment...") # Initialize database print("Creating database...") if not os.path.exists("instance"): os.mkdir("instance") subprocess.run(["python", "manage.py", "db", "init"], check=True) subprocess.run(["python", "manage.py", "db", "migrate"], check=True) subprocess.run(["python", "manage.py", "db", "upgrade"], check=True) # Create sample data print("Creating sample data...") subprocess.run(["python", "manage.py", "seed"], check=True) print("Setup complete! Run 'python manage.py run' to start the application.") if __name__ == "__main__": main() - Standardize linting and formatting with configuration files
# .flake8 [flake8] max-line-length = 100 exclude = venv,.git,__pycache__,build,dist # pyproject.toml (for black) [tool.black] line-length = 100 target-version = ['py39'] include = '\.pyi?$' exclude = ''' /( \.git | \.venv | venv | _build | build | dist )/ '''
Git Hooks for Environment Consistency
Use Git hooks to enforce standards:
# .git/hooks/pre-commit (make executable with chmod +x)
#!/bin/bash
# Check that we're using the project's virtual environment
if [[ $VIRTUAL_ENV != *$(pwd)* ]]; then
echo "ERROR: Virtual environment not activated or doesn't match project!"
echo "Activate with: source venv/bin/activate"
exit 1
fi
# Run linters and tests
echo "Running linters..."
flake8 src tests || exit 1
black --check src tests || exit 1
echo "Running tests..."
pytest -xvs tests/unit || exit 1
echo "Pre-commit checks passed!"
Continuous Integration (CI) Environment
Configure CI systems to replicate your virtual environment setup:
# .github/workflows/ci.yml
name: Python CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Lint
run: |
flake8 src tests
black --check src tests
- name: Test
run: |
pytest -xvs tests/
Real-World Example: Flask Project Structure with Best Practices
Let's bring together everything we've learned with a practical example of a Flask web application that follows best practices:
Project Structure
flask_app/
├── .env.example # Example environment variables (template)
├── .flake8 # Linting configuration
├── .gitignore # Files to exclude from Git
├── .pre-commit-config.yaml # Pre-commit hooks configuration
├── README.md # Project documentation
├── pyproject.toml # Python project configuration (black, etc.)
├── requirements/
│ ├── base.txt # Core dependencies
│ ├── development.txt # Development tools
│ ├── production.txt # Production-specific requirements
│ └── testing.txt # Testing tools
├── setup.py # Package installation script
├── src/
│ └── myapp/
│ ├── __init__.py # Package initialization
│ ├── app.py # Application factory
│ ├── config.py # Configuration management
│ ├── models/ # Database models
│ ├── routes/ # API and view routes
│ ├── services/ # Business logic
│ ├── static/ # Static assets (CSS, JS)
│ └── templates/ # Jinja2 templates
├── tests/
│ ├── conftest.py # Test fixtures
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
└── venv/ # Virtual environment (not in version control)
Environment Setup Guide
Here's how the README.md would guide environment setup:
# Flask App Example
A modern Flask application demonstrating Python best practices.
## Development Setup
### Prerequisites
- Python 3.9+
- Git
### Setting Up Your Development Environment
1. Clone the repository:
```
git clone https://github.com/username/flask_app.git
cd flask_app
```
2. Create and activate a virtual environment:
```
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
```
3. Install development dependencies:
```
pip install -r requirements/development.txt
```
4. Set up your environment variables:
```
cp .env.example .env
# Edit .env with your settings
```
5. Set up pre-commit hooks:
```
pre-commit install
```
6. Initialize the database:
```
flask db init
flask db migrate
flask db upgrade
```
7. Run the development server:
```
flask run
```
The application should now be running at http://127.0.0.1:5000/
### Testing
Run the test suite:
```
pytest
```
Run with coverage report:
```
pytest --cov=src/myapp
```
Configuration Management
Example of the application's config.py:
"""Application configuration."""
import os
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables from .env file
env_path = Path(".") / ".env"
load_dotenv(dotenv_path=env_path)
class Config:
"""Base configuration."""
# Flask
SECRET_KEY = os.getenv("SECRET_KEY", "dev-key-only-for-development")
FLASK_ENV = os.getenv("FLASK_ENV", "development")
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
# Database
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///app.db")
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Security
SESSION_COOKIE_SECURE = os.getenv("SESSION_COOKIE_SECURE", "False").lower() == "true"
REMEMBER_COOKIE_SECURE = True
class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
SQLALCHEMY_ECHO = True
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""Production configuration."""
DEBUG = False
SESSION_COOKIE_SECURE = True
# Override this in production environment
if not os.getenv("SECRET_KEY"):
raise ValueError("SECRET_KEY environment variable is required in production")
# Configuration dictionary
config = {
"development": DevelopmentConfig,
"testing": TestingConfig,
"production": ProductionConfig,
"default": DevelopmentConfig
}
def get_config():
"""Get the correct configuration based on environment."""
env = os.getenv("FLASK_ENV", "development")
return config.get(env, config["default"])
Application Factory
The app.py module uses our configuration system:
"""Flask application factory."""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from myapp.config import get_config
# Initialize extensions
db = SQLAlchemy()
migrate = Migrate()
def create_app():
"""Create and configure the Flask application."""
app = Flask(__name__)
app.config.from_object(get_config())
# Initialize extensions with app
db.init_app(app)
migrate.init_app(app, db)
# Register blueprints
from myapp.routes import main_bp, api_bp
app.register_blueprint(main_bp)
app.register_blueprint(api_bp, url_prefix='/api')
# Register error handlers
from myapp.errors import register_error_handlers
register_error_handlers(app)
return app
This example demonstrates:
- Proper virtual environment setup and documentation
- Environment-specific configuration
- Dependency management with separate requirement files
- Application structure that supports clean architecture
- Environment variables for configuration
Conclusion: Virtual Environment Principles to Live By
As we conclude our exploration of virtual environments, let's distill the key principles that will serve you well throughout your Python development career:
- One Project, One Environment: Always create a dedicated virtual environment for each project.
- Documentation is Key: Document your environment setup process thoroughly.
- Version Pinning: Pin your dependency versions for reproducibility.
- Isolation is Protection: Never install project-specific packages globally.
- Environment Variables for Configuration: Keep sensitive or environment-specific settings in environment variables, not code.
- Regular Maintenance: Periodically update dependencies and rebuild environments.
- Use the Right Tool: Choose the appropriate virtual environment tool for your project's needs.
- Scripts and Automation: Automate repetitive environment tasks.
- Team Consistency: Establish and follow team conventions for environments.
- Clean Up Regularly: Delete unused environments to save disk space.
By internalizing these principles, you'll develop clean, reproducible Python projects that are easier to maintain, share, and deploy.
In our next sessions, we'll build on this foundation as we dive into module organization, packages, and more advanced Python development concepts.
Exercise: Environment Health Check Tool
Let's apply what we've learned by creating a virtual environment "health check" script that you can use to validate your environment setup.
Create a file named env_check.py with the following content:
#!/usr/bin/env python
"""
Virtual Environment Health Check
A utility to verify your virtual environment is properly configured.
"""
import os
import sys
import site
import subprocess
import platform
from pathlib import Path
def header(text):
"""Print a section header."""
print("\n" + "=" * 60)
print(f" {text}")
print("=" * 60)
def check_mark(condition):
"""Return a check mark or X based on condition."""
return "✅" if condition else "❌"
def main():
"""Run the environment health check."""
header("VIRTUAL ENVIRONMENT HEALTH CHECK")
# Check if running in a virtual environment
in_venv = sys.prefix != sys.base_prefix
print(f"{check_mark(in_venv)} Running in a virtual environment")
if not in_venv:
print("\n⚠️ WARNING: Not running in a virtual environment!")
print(" Please activate your virtual environment and try again.")
print(" Example: source venv/bin/activate")
sys.exit(1)
# Get environment details
venv_path = sys.prefix
python_version = platform.python_version()
pip_version = subprocess.run(
[sys.executable, "-m", "pip", "--version"],
capture_output=True, text=True
).stdout.split()[1]
print(f"\nEnvironment path: {venv_path}")
print(f"Python version: {python_version}")
print(f"Pip version: {pip_version}")
# Check site-packages location
site_packages = site.getsitepackages()[0]
correct_site_packages = Path(site_packages).is_relative_to(Path(venv_path))
print(f"{check_mark(correct_site_packages)} Site-packages directory: {site_packages}")
# Check activation script
if os.name == 'nt': # Windows
activate_script = Path(venv_path) / "Scripts" / "activate.bat"
else: # Unix
activate_script = Path(venv_path) / "bin" / "activate"
script_exists = activate_script.exists()
print(f"{check_mark(script_exists)} Activation script: {activate_script}")
# Check for key packages
header("INSTALLED PACKAGES")
# Get installed packages
installed_packages = subprocess.run(
[sys.executable, "-m", "pip", "list"],
capture_output=True, text=True
).stdout.strip().split('\n')[2:] # Skip header lines
print(f"Total installed packages: {len(installed_packages)}")
# Show top packages (first 10)
if installed_packages:
print("\nTop packages:")
for i, package_line in enumerate(installed_packages[:10]):
print(f" {package_line}")
if len(installed_packages) > 10:
print(f" ...and {len(installed_packages) - 10} more")
# Check requirements file
header("PROJECT CONFIGURATION")
# Find requirements files
req_files = list(Path().glob("requirements*.txt")) + list(Path().glob("requirements/*.txt"))
if req_files:
print(f"{check_mark(True)} Found {len(req_files)} requirements files:")
for req_file in req_files:
print(f" - {req_file}")
else:
print(f"{check_mark(False)} No requirements files found")
# Check for common environment files
env_files = list(Path().glob(".env*"))
if env_files:
print(f"{check_mark(True)} Found {len(env_files)} environment files:")
for env_file in env_files:
print(f" - {env_file}")
else:
print(f"{check_mark(False)} No environment files found")
# Check for .gitignore
gitignore = Path(".gitignore")
gitignore_exists = gitignore.exists()
print(f"{check_mark(gitignore_exists)} .gitignore file")
if gitignore_exists:
venv_ignored = False
gitignore_content = gitignore.read_text()
venv_patterns = ["venv/", "/venv", "venv", "env/", "ENV/"]
for pattern in venv_patterns:
if pattern in gitignore_content:
venv_ignored = True
break
print(f"{check_mark(venv_ignored)} Virtual environment ignored in .gitignore")
# Summary
header("SUMMARY")
if in_venv and correct_site_packages and script_exists:
print("✅ Your virtual environment appears to be configured correctly!")
else:
print("⚠️ Your virtual environment may have issues that need attention.")
if not req_files:
print("\n⚠️ Recommendation: Create a requirements.txt file to track dependencies.")
if not env_files:
print("\n⚠️ Recommendation: Consider using .env files for environment variables.")
if gitignore_exists and not venv_ignored:
print("\n⚠️ Recommendation: Add your virtual environment directory to .gitignore.")
print("\nFor additional checks, consider tools like 'pip-audit' for security vulnerabilities")
print("or 'pip check' to verify package dependencies.")
if __name__ == "__main__":
main()
Using the Script
To use this script:
- Save it as
env_check.pyin your project directory - Activate your virtual environment
- Run it with
python env_check.py
The script will perform several checks on your virtual environment and provide recommendations for improvements.
Script Features
- Verifies you're running in a virtual environment
- Displays key environment information
- Lists installed packages
- Checks for requirements files
- Verifies .gitignore configuration
- Provides recommendations for best practices
This script demonstrates practical application of the virtual environment concepts we've covered, and gives you a tool you can use in future projects.