Python Full Stack Web Developer Course

Week 2: Python Fundamentals (Part 1)

Friday Morning: Virtual Environments Best Practices

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
  • Built into Python 3.3+
  • Simple to use
  • No additional installation
  • Basic functionality only
  • No dependency management
  • Windows requires additional steps
Simple projects and learning Python
virtualenv
  • Works with Python 2 and 3
  • More features than venv
  • Faster environment creation
  • Requires separate installation
  • No dependency management
Projects supporting legacy Python
conda
  • Cross-platform package manager
  • Handles non-Python dependencies
  • Creates isolated environments
  • Larger installation footprint
  • Sometimes conflicts with pip
  • Different from standard Python tooling
Data science and scientific computing
pipenv
  • Combines pip and virtualenv
  • Automatic dependency management
  • Lock files for deterministic builds
  • Sometimes slower than alternatives
  • Learning curve for beginners
Application development with dependency locking
poetry
  • Modern dependency resolution
  • Package building and publishing
  • Intuitive commands
  • Newer tool, still evolving
  • More opinionated workflow
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:

Examples of good environment names:

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:

Cons:

Option 2: Central Environment Directory
~/.virtualenvs/
├── project1-env/
├── project2-env/
└── project3-env/

Pros:

Cons:

Environment Location Best Practices

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

Virtual Environment Workflows

Environment Setup Workflow

Follow these steps when setting up a new project:

  1. Create project directory
    mkdir myproject
    cd myproject
  2. Initialize version control
    git init
    echo "venv/" >> .gitignore    # Ignore virtual environment directory
  3. Create virtual environment
    python -m venv venv
  4. Activate the environment
    source venv/bin/activate  # macOS/Linux
    venv\Scripts\activate    # Windows
  5. Install initial dependencies
    pip install flask sqlalchemy
  6. Freeze dependencies
    pip freeze > requirements.txt
  7. Create initial project structure
    mkdir src tests
    touch README.md
  8. 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:

  1. Clone the repository
    git clone https://github.com/username/project.git
    cd project
  2. Create virtual environment
    python -m venv venv
  3. Activate the environment
    source venv/bin/activate  # macOS/Linux
    venv\Scripts\activate    # Windows
  4. Install dependencies
    pip install -r requirements.txt
  5. Verify installation
    pip list
    python -c "import flask; print(flask.__version__)"

Environment Maintenance Workflow

Regular maintenance tasks to keep your environment healthy:

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

Using Both Together

Many developers use both tools in their workflow:

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:

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

Virtual Environments in Team Settings

Establishing Team Conventions

Consistent practices are essential for team productivity:

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:

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:

  1. One Project, One Environment: Always create a dedicated virtual environment for each project.
  2. Documentation is Key: Document your environment setup process thoroughly.
  3. Version Pinning: Pin your dependency versions for reproducibility.
  4. Isolation is Protection: Never install project-specific packages globally.
  5. Environment Variables for Configuration: Keep sensitive or environment-specific settings in environment variables, not code.
  6. Regular Maintenance: Periodically update dependencies and rebuild environments.
  7. Use the Right Tool: Choose the appropriate virtual environment tool for your project's needs.
  8. Scripts and Automation: Automate repetitive environment tasks.
  9. Team Consistency: Establish and follow team conventions for environments.
  10. 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:

  1. Save it as env_check.py in your project directory
  2. Activate your virtual environment
  3. Run it with python env_check.py

The script will perform several checks on your virtual environment and provide recommendations for improvements.

Script Features

This script demonstrates practical application of the virtual environment concepts we've covered, and gives you a tool you can use in future projects.

Additional Resources