Virtual assistance

Python Best Practices

Master Python best practices including PEP 8 style guide, code organization, testing, documentation, performance optimization, and security principles.

Python Best Practices

PEP 8 Style Guide

PEP 8 is Python's official style guide. Following it ensures your code is readable and consistent:

# Good: Clear, readable code following PEP 8
def calculate_total_price(items, tax_rate=0.08):
    """
    Calculate total price including tax.
    
    Args:
        items (list): List of item prices
        tax_rate (float): Tax rate as decimal (default 8%)
    
    Returns:
        float: Total price including tax
    """
    subtotal = sum(items)
    tax = subtotal * tax_rate
    total = subtotal + tax
    return round(total, 2)

# Bad: Poor naming, no documentation, inconsistent spacing
def calc(items,tax=0.08):
    s=sum(items)
    t=s*tax
    return s+t

# Naming conventions
# Variables and functions: snake_case
user_name = "john_doe"
def get_user_data():
    pass

# Classes: PascalCase
class UserProfile:
    pass

# Constants: UPPER_CASE
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30

# Private attributes/methods: leading underscore
class DatabaseConnection:
    def __init__(self):
        self._connection = None
    
    def _validate_connection(self):
        pass

# Indentation: 4 spaces (never tabs)
def example_function():
    if True:
        print("This is properly indented")
        if False:
            print("Nested indentation")

# Line length: Maximum 79 characters for code, 72 for docstrings
# Bad (too long):
result = some_very_long_function_name_that_makes_this_line_exceed_the_recommended_length(parameter_one, parameter_two)

# Good (broken into multiple lines):
result = some_very_long_function_name(
    parameter_one,
    parameter_two
)

# Or use backslashes for line continuation:
result = some_very_long_function_name( \
    parameter_one, \
    parameter_two \
)

# Blank lines: Use blank lines to separate logical sections
class Calculator:
    def __init__(self):
        self.result = 0
    
    def add(self, value):
        self.result += value
        return self.result
    
    def multiply(self, value):
        self.result *= value
        return self.result

# Import organization
# Standard library imports first
import os
import sys
from pathlib import Path

# Then third-party imports
import requests
from flask import Flask

# Finally local imports
from .models import User
from ..utils import helper_function

# Avoid wildcard imports
# Bad:
from math import *

# Good:
from math import sqrt, pi, sin, cos

# String quotes: Use double quotes for strings, single for characters
name = "John Doe"
initial = 'J'

# But be consistent within a project

Code Organization and Structure

Organizing your Python projects for maintainability:

# Project structure example
my_project/
├── README.md                    # Project description and setup instructions
├── requirements.txt             # Python dependencies
├── setup.py                     # Package setup (for distribution)
├── .gitignore                   # Git ignore rules
├── .env                         # Environment variables (don't commit)
├── docs/                        # Documentation
│   ├── index.md
│   └── api.md
├── tests/                       # Unit and integration tests
│   ├── __init__.py
│   ├── test_models.py
│   └── test_views.py
├── src/                         # Source code
│   ├── __init__.py
│   ├── main.py                  # Entry point
│   ├── config.py                # Configuration management
│   ├── database.py              # Database connections/utilities
│   ├── models/                  # Data models
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── product.py
│   ├── services/                # Business logic
│   │   ├── __init__.py
│   │   ├── user_service.py
│   │   └── payment_service.py
│   ├── utils/                   # Utility functions
│   │   ├── __init__.py
│   │   ├── validators.py
│   │   └── formatters.py
│   └── api/                     # API endpoints
│       ├── __init__.py
│       ├── routes.py
│       └── middleware.py
└── scripts/                     # Utility scripts
    ├── setup_database.py
    └── migrate_data.py
# config.py - Configuration management
import os
from dataclasses import dataclass
from typing import Optional

@dataclass
class DatabaseConfig:
    host: str
    port: int
    name: str
    user: str
    password: str
    
    @property
    def connection_string(self) -> str:
        return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.name}"

@dataclass
class AppConfig:
    debug: bool
    secret_key: str
    database: DatabaseConfig
    
    @classmethod
    def from_env(cls) -> 'AppConfig':
        return cls(
            debug=os.getenv('DEBUG', 'False').lower() == 'true',
            secret_key=os.getenv('SECRET_KEY', 'dev-secret-key'),
            database=DatabaseConfig(
                host=os.getenv('DB_HOST', 'localhost'),
                port=int(os.getenv('DB_PORT', '5432')),
                name=os.getenv('DB_NAME', 'myapp'),
                user=os.getenv('DB_USER', 'postgres'),
                password=os.getenv('DB_PASSWORD', '')
            )
        )

# Usage
config = AppConfig.from_env()

# main.py - Entry point
from src.config import AppConfig
from src.database import init_database
from src.api.routes import create_app

def main():
    config = AppConfig.from_env()
    
    # Initialize database
    init_database(config.database)
    
    # Create and run application
    app = create_app(config)
    app.run(
        host='0.0.0.0',
        port=8000,
        debug=config.debug
    )

if __name__ == '__main__':
    main()

# models/user.py - Model definition
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from email_validator import validate_email, EmailNotValidError

@dataclass
class User:
    id: Optional[int]
    email: str
    username: str
    full_name: str
    created_at: datetime
    updated_at: datetime
    is_active: bool = True
    
    def __post_init__(self):
        # Validate email
        try:
            validate_email(self.email)
        except EmailNotValidError:
            raise ValueError(f"Invalid email: {self.email}")
        
        # Validate username
        if not self.username or len(self.username) < 3:
            raise ValueError("Username must be at least 3 characters long")
    
    @property
    def display_name(self) -> str:
        return self.full_name or self.username
    
    def to_dict(self) -> dict:
        return {
            'id': self.id,
            'email': self.email,
            'username': self.username,
            'full_name': self.full_name,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat(),
            'is_active': self.is_active,
        }

# services/user_service.py - Business logic
from typing import List, Optional
from src.models.user import User
from src.database import DatabaseConnection

class UserService:
    def __init__(self, db: DatabaseConnection):
        self.db = db
    
    def create_user(self, email: str, username: str, full_name: str) -> User:
        """Create a new user."""
        # Check if user already exists
        if self.get_user_by_email(email):
            raise ValueError("User with this email already exists")
        
        if self.get_user_by_username(username):
            raise ValueError("Username already taken")
        
        user = User(
            id=None,
            email=email,
            username=username,
            full_name=full_name,
            created_at=datetime.utcnow(),
            updated_at=datetime.utcnow()
        )
        
        # Save to database
        user.id = self.db.insert_user(user)
        return user
    
    def get_user_by_id(self, user_id: int) -> Optional[User]:
        """Get user by ID."""
        return self.db.get_user_by_id(user_id)
    
    def get_user_by_email(self, email: str) -> Optional[User]:
        """Get user by email."""
        return self.db.get_user_by_email(email)
    
    def get_user_by_username(self, username: str) -> Optional[User]:
        """Get user by username."""
        return self.db.get_user_by_username(username)
    
    def update_user(self, user_id: int, **updates) -> Optional[User]:
        """Update user information."""
        user = self.get_user_by_id(user_id)
        if not user:
            return None
        
        # Apply updates
        for key, value in updates.items():
            if hasattr(user, key):
                setattr(user, key, value)
        
        user.updated_at = datetime.utcnow()
        self.db.update_user(user)
        return user
    
    def delete_user(self, user_id: int) -> bool:
        """Delete a user."""
        return self.db.delete_user(user_id)
    
    def list_users(self, limit: int = 50, offset: int = 0) -> List[User]:
        """List users with pagination."""
        return self.db.list_users(limit, offset)

# utils/validators.py - Utility functions
import re
from typing import Optional

def validate_email_format(email: str) -> bool:
    """Validate email format using regex."""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

def validate_password_strength(password: str) -> tuple[bool, Optional[str]]:
    """Validate password strength."""
    if len(password) < 8:
        return False, "Password must be at least 8 characters long"
    
    if not re.search(r'[A-Z]', password):
        return False, "Password must contain at least one uppercase letter"
    
    if not re.search(r'[a-z]', password):
        return False, "Password must contain at least one lowercase letter"
    
    if not re.search(r'\d', password):
        return False, "Password must contain at least one digit"
    
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False, "Password must contain at least one special character"
    
    return True, None

def sanitize_input(text: str) -> str:
    """Sanitize user input to prevent XSS."""
    # Remove potentially dangerous characters
    dangerous_chars = ['<', '>', '&', '"', "'"]
    for char in dangerous_chars:
        text = text.replace(char, '')
    return text.strip()

Testing Best Practices

Writing comprehensive tests for your Python code:

# tests/test_models.py
import pytest
from datetime import datetime
from src.models.user import User

class TestUser:
    def test_user_creation_valid_data(self):
        """Test creating a user with valid data."""
        user = User(
            id=None,
            email="john@example.com",
            username="johndoe",
            full_name="John Doe",
            created_at=datetime(2023, 1, 1, 12, 0, 0),
            updated_at=datetime(2023, 1, 1, 12, 0, 0)
        )
        
        assert user.email == "john@example.com"
        assert user.username == "johndoe"
        assert user.display_name == "John Doe"
        assert user.is_active == True
    
    def test_user_creation_invalid_email(self):
        """Test that invalid email raises ValueError."""
        with pytest.raises(ValueError, match="Invalid email"):
            User(
                id=None,
                email="invalid-email",
                username="johndoe",
                full_name="John Doe",
                created_at=datetime(2023, 1, 1, 12, 0, 0),
                updated_at=datetime(2023, 1, 1, 12, 0, 0)
            )
    
    def test_user_creation_short_username(self):
        """Test that short username raises ValueError."""
        with pytest.raises(ValueError, match="Username must be at least 3 characters"):
            User(
                id=None,
                email="john@example.com",
                username="jo",
                full_name="John Doe",
                created_at=datetime(2023, 1, 1, 12, 0, 0),
                updated_at=datetime(2023, 1, 1, 12, 0, 0)
            )
    
    def test_user_to_dict(self):
        """Test converting user to dictionary."""
        user = User(
            id=1,
            email="john@example.com",
            username="johndoe",
            full_name="John Doe",
            created_at=datetime(2023, 1, 1, 12, 0, 0),
            updated_at=datetime(2023, 1, 1, 12, 0, 0)
        )
        
        user_dict = user.to_dict()
        assert user_dict['id'] == 1
        assert user_dict['email'] == "john@example.com"
        assert user_dict['username'] == "johndoe"
        assert 'created_at' in user_dict
        assert 'updated_at' in user_dict

# tests/test_user_service.py
import pytest
from unittest.mock import Mock, MagicMock
from src.services.user_service import UserService
from src.models.user import User

class TestUserService:
    @pytest.fixture
    def mock_db(self):
        """Create a mock database connection."""
        db = Mock()
        db.get_user_by_email.return_value = None
        db.get_user_by_username.return_value = None
        db.insert_user.return_value = 1
        return db
    
    @pytest.fixture
    def user_service(self, mock_db):
        """Create a UserService instance with mock database."""
        return UserService(mock_db)
    
    def test_create_user_success(self, user_service, mock_db):
        """Test successful user creation."""
        user = user_service.create_user(
            email="john@example.com",
            username="johndoe",
            full_name="John Doe"
        )
        
        assert user.email == "john@example.com"
        assert user.username == "johndoe"
        assert user.id == 1
        mock_db.insert_user.assert_called_once()
    
    def test_create_user_duplicate_email(self, user_service, mock_db):
        """Test creating user with duplicate email."""
        # Setup mock to return existing user
        existing_user = Mock()
        mock_db.get_user_by_email.return_value = existing_user
        
        with pytest.raises(ValueError, match="User with this email already exists"):
            user_service.create_user(
                email="john@example.com",
                username="johndoe",
                full_name="John Doe"
            )
    
    def test_get_user_by_id(self, user_service, mock_db):
        """Test getting user by ID."""
        expected_user = Mock()
        mock_db.get_user_by_id.return_value = expected_user
        
        result = user_service.get_user_by_id(1)
        
        assert result == expected_user
        mock_db.get_user_by_id.assert_called_once_with(1)

# conftest.py - Shared test fixtures
import pytest
from src.config import AppConfig, DatabaseConfig

@pytest.fixture
def test_config():
    """Test configuration."""
    return AppConfig(
        debug=True,
        secret_key="test-secret-key",
        database=DatabaseConfig(
            host="localhost",
            port=5432,
            name="test_db",
            user="test_user",
            password="test_password"
        )
    )

# pytest.ini - Pytest configuration
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = 
    --verbose
    --tb=short
    --cov=src
    --cov-report=html
    --cov-report=term-missing
    --cov-fail-under=80

# Integration tests
# tests/test_integration.py
import pytest
from src.services.user_service import UserService
from src.database import DatabaseConnection

@pytest.mark.integration
class TestUserServiceIntegration:
    @pytest.fixture
    def db_connection(self, test_config):
        """Real database connection for integration tests."""
        db = DatabaseConnection(test_config.database)
        # Setup test database
        db.create_tables()
        yield db
        # Cleanup
        db.drop_tables()
    
    def test_full_user_lifecycle(self, db_connection):
        """Test complete user lifecycle."""
        service = UserService(db_connection)
        
        # Create user
        user = service.create_user(
            email="integration@example.com",
            username="integration_user",
            full_name="Integration Test User"
        )
        assert user.id is not None
        
        # Retrieve user
        retrieved = service.get_user_by_id(user.id)
        assert retrieved.email == user.email
        
        # Update user
        updated = service.update_user(user.id, full_name="Updated Name")
        assert updated.full_name == "Updated Name"
        
        # Delete user
        result = service.delete_user(user.id)
        assert result == True
        
        # Verify deletion
        deleted = service.get_user_by_id(user.id)
        assert deleted is None

# Performance tests
# tests/test_performance.py
import time
import pytest
from src.services.user_service import UserService

@pytest.mark.performance
class TestUserServicePerformance:
    def test_bulk_user_creation_performance(self, user_service):
        """Test performance of bulk user creation."""
        start_time = time.time()
        
        users = []
        for i in range(100):
            user = user_service.create_user(
                email=f"user{i}@example.com",
                username=f"user{i}",
                full_name=f"User {i}"
            )
            users.append(user)
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Should complete within reasonable time
        assert duration < 5.0  # 5 seconds max
        assert len(users) == 100

Documentation Best Practices

Writing comprehensive documentation:

# Example of well-documented code
"""
User Management System

This module provides functionality for managing users in the application.
It includes user creation, authentication, and profile management.

Classes:
    UserManager: Main class for user operations
    AuthenticationService: Handles user authentication

Functions:
    create_user: Create a new user account
    authenticate_user: Verify user credentials

Examples:
    >>> manager = UserManager()
    >>> user = manager.create_user("john@example.com", "password123")
    >>> authenticated = authenticate_user("john@example.com", "password123")
    True
"""

from typing import Optional, Dict, Any
from datetime import datetime

class UserManager:
    """
    Manages user accounts and operations.
    
    This class provides methods for creating, updating, and managing
    user accounts in the system.
    
    Attributes:
        db_connection: Database connection object
        cache: Optional cache for performance optimization
    """
    
    def __init__(self, db_connection, cache=None):
        """
        Initialize the UserManager.
        
        Args:
            db_connection: Database connection instance
            cache: Optional cache instance for performance
        """
        self.db_connection = db_connection
        self.cache = cache
    
    def create_user(self, email: str, password: str, **user_data) -> Dict[str, Any]:
        """
        Create a new user account.
        
        This method creates a new user with the provided email and password,
        along with any additional user data.
        
        Args:
            email (str): User's email address
            password (str): User's password (will be hashed)
            **user_data: Additional user information (name, etc.)
        
        Returns:
            Dict[str, Any]: User data including generated ID
            
        Raises:
            ValueError: If email is invalid or already exists
            DatabaseError: If database operation fails
            
        Example:
            >>> user = manager.create_user(
            ...     "john@example.com", 
            ...     "secure_password",
            ...     full_name="John Doe"
            ... )
            >>> print(user['id'])
            123
        """
        # Validate email format
        if not self._is_valid_email(email):
            raise ValueError("Invalid email format")
        
        # Check if user already exists
        if self.get_user_by_email(email):
            raise ValueError("User with this email already exists")
        
        # Hash password
        hashed_password = self._hash_password(password)
        
        # Create user record
        user_record = {
            'email': email,
            'password_hash': hashed_password,
            'created_at': datetime.utcnow(),
            'is_active': True,
            **user_data
        }
        
        # Save to database
        user_id = self.db_connection.insert('users', user_record)
        user_record['id'] = user_id
        
        # Cache user data
        if self.cache:
            self.cache.set(f"user:{user_id}", user_record)
        
        return user_record
    
    def get_user_by_email(self, email: str) -> Optional[Dict[str, Any]]:
        """
        Retrieve user by email address.
        
        Args:
            email (str): Email address to search for
            
        Returns:
            Optional[Dict[str, Any]]: User data if found, None otherwise
        """
        # Check cache first
        if self.cache:
            cached_user = self.cache.get(f"user:email:{email}")
            if cached_user:
                return cached_user
        
        # Query database
        user = self.db_connection.query_one(
            "SELECT * FROM users WHERE email = %s", 
            (email,)
        )
        
        # Cache result
        if self.cache and user:
            self.cache.set(f"user:email:{email}", user)
        
        return user
    
    def _is_valid_email(self, email: str) -> bool:
        """
        Validate email format using regex.
        
        Args:
            email (str): Email to validate
            
        Returns:
            bool: True if valid, False otherwise
        """
        import re
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return bool(re.match(pattern, email))
    
    def _hash_password(self, password: str) -> str:
        """
        Hash password using secure hashing algorithm.
        
        Args:
            password (str): Plain text password
            
        Returns:
            str: Hashed password
        """
        from werkzeug.security import generate_password_hash
        return generate_password_hash(password)

def authenticate_user(email: str, password: str) -> bool:
    """
    Authenticate a user with email and password.
    
    Args:
        email (str): User's email address
        password (str): User's password
        
    Returns:
        bool: True if authentication successful, False otherwise
        
    Note:
        This function uses constant-time comparison to prevent
        timing attacks.
    """
    manager = UserManager.get_instance()
    user = manager.get_user_by_email(email)
    
    if not user or not user.get('is_active', False):
        return False
    
    from werkzeug.security import check_password_hash
    return check_password_hash(user['password_hash'], password)

# README.md example
# My Python Project

A comprehensive user management system built with Python.

## Features

- User registration and authentication
- Profile management
- Password hashing with Werkzeug
- Database abstraction layer
- Comprehensive test suite
- RESTful API endpoints

## Installation

```bash
pip install -r requirements.txt
```

## Usage

```python
from user_manager import UserManager

# Create a user manager instance
manager = UserManager(database_connection)

# Create a new user
user = manager.create_user("john@example.com", "secure_password")
print(f"Created user with ID: {user['id']}")
```

## API Documentation

See [docs/api.md](docs/api.md) for detailed API documentation.

## Testing

Run the test suite:

```bash
pytest
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Write tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## License

MIT License - see [LICENSE](LICENSE) file for details.

Performance Optimization

Optimizing Python code for better performance:

# Performance optimization techniques

# 1. Use built-in functions and data structures
# Bad: Manual loop
def sum_list_bad(numbers):
    total = 0
    for num in numbers:
        total += num
    return total

# Good: Use built-in sum()
def sum_list_good(numbers):
    return sum(numbers)

# 2. List comprehensions vs loops
# Bad: Traditional loop
squares_bad = []
for i in range(1000):
    squares_bad.append(i ** 2)

# Good: List comprehension
squares_good = [i ** 2 for i in range(1000)]

# Even better: Generator expression for memory efficiency
squares_generator = (i ** 2 for i in range(1000))

# 3. String concatenation
# Bad: Using + in loop
result = ""
for word in words:
    result += word + " "

# Good: Use join()
result = " ".join(words)

# 4. Dictionary lookups vs list searches
# Bad: Linear search in list
def find_user_bad(users, user_id):
    for user in users:
        if user['id'] == user_id:
            return user
    return None

# Good: Dictionary lookup
def find_user_good(users_dict, user_id):
    return users_dict.get(user_id)

# 5. Caching expensive operations
from functools import lru_cache
import time

@lru_cache(maxsize=128)
def expensive_computation(n):
    """Expensive computation with caching."""
    time.sleep(0.1)  # Simulate expensive operation
    return n * n

# 6. Use appropriate data structures
# For membership testing: set > list
# For ordered data: list > set
# For key-value pairs: dict

# 7. Avoid global variables in performance-critical code
# Bad: Global variable
counter = 0
def increment_bad():
    global counter
    counter += 1

# Good: Local variables or class attributes
class Counter:
    def __init__(self):
        self.value = 0
    
    def increment(self):
        self.value += 1

# 8. Profile your code to find bottlenecks
import cProfile
import pstats

def profile_function():
    # Code to profile
    result = sum(i ** 2 for i in range(10000))
    return result

# Profile the function
profiler = cProfile.Profile()
profiler.enable()
result = profile_function()
profiler.disable()

# Print statistics
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats()

# 9. Use multiprocessing for CPU-bound tasks
import multiprocessing as mp
from concurrent.futures import ProcessPoolExecutor

def cpu_intensive_task(n):
    """CPU-intensive computation."""
    return sum(i * i for i in range(n))

def parallel_processing():
    numbers = [1000000] * 4  # 4 tasks
    
    # Sequential (slow)
    start = time.time()
    results_seq = [cpu_intensive_task(n) for n in numbers]
    seq_time = time.time() - start
    
    # Parallel (fast)
    start = time.time()
    with ProcessPoolExecutor() as executor:
        results_par = list(executor.map(cpu_intensive_task, numbers))
    par_time = time.time() - start
    
    print(f"Sequential: {seq_time:.2f}s")
    print(f"Parallel: {par_time:.2f}s")
    print(f"Speedup: {seq_time/par_time:.2f}x")

# 10. Memory optimization
# Use __slots__ for memory-efficient classes
class MemoryEfficientClass:
    __slots__ = ['x', 'y', 'z']
    
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

# Use generators for large datasets
def read_large_file(file_path):
    """Read large file line by line."""
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()

# Process large file without loading into memory
for line in read_large_file('large_file.txt'):
    process_line(line)

# 11. Database optimization
# Use select_related and prefetch_related for Django
# Use database indexes appropriately
# Batch database operations

# 12. Algorithm optimization
# Choose appropriate algorithms and data structures
# O(n²) vs O(n log n) vs O(n)

# 13. Use compiled extensions for performance-critical code
# Consider using NumPy for numerical computations
# Use Cython for CPU-intensive Python code

# 14. Asynchronous programming for I/O-bound tasks
import asyncio
import aiohttp

async def fetch_url(session, url):
    """Asynchronously fetch a URL."""
    async with session.get(url) as response:
        return await response.text()

async def fetch_multiple_urls(urls):
    """Fetch multiple URLs concurrently."""
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        return await asyncio.gather(*tasks)

# 15. Code profiling decorators
def timing_decorator(func):
    """Decorator to time function execution."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(1)
    return "Done"

Security Best Practices

Writing secure Python code:

# Security best practices

# 1. Input validation and sanitization
def validate_and_sanitize_input(user_input):
    """Validate and sanitize user input."""
    # Remove potentially dangerous characters
    sanitized = user_input.replace('<', '<').replace('>', '>')
    
    # Validate length
    if len(sanitized) > 1000:
        raise ValueError("Input too long")
    
    # Validate against whitelist pattern
    import re
    if not re.match(r'^[a-zA-Z0-9\s\-_.]+$', sanitized):
        raise ValueError("Invalid characters in input")
    
    return sanitized

# 2. Secure password handling
import hashlib
import secrets
import bcrypt

def hash_password(password):
    """Hash password securely."""
    # Use bcrypt for password hashing
    salt = bcrypt.gensalt()
    return bcrypt.hashpw(password.encode('utf-8'), salt)

def verify_password(password, hashed):
    """Verify password against hash."""
    return bcrypt.checkpw(password.encode('utf-8'), hashed)

def generate_secure_token():
    """Generate a secure random token."""
    return secrets.token_urlsafe(32)

# 3. SQL injection prevention
# Bad: String formatting (vulnerable to SQL injection)
def get_user_bad(cursor, user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)

# Good: Parameterized queries
def get_user_good(cursor, user_id):
    query = "SELECT * FROM users WHERE id = %s"
    cursor.execute(query, (user_id,))

# Even better: Use ORM like SQLAlchemy
from sqlalchemy import create_engine, text

engine = create_engine('postgresql://user:password@localhost/db')

def get_user_safe(user_id):
    with engine.connect() as conn:
        result = conn.execute(
            text("SELECT * FROM users WHERE id = :user_id"),
            {"user_id": user_id}
        )
        return result.fetchone()

# 4. XSS prevention
from html import escape

def render_user_profile(user_data):
    """Render user profile safely."""
    return f"""
    

{escape(user_data['name'])}

{escape(user_data['bio'])}

""" # 5. CSRF protection from flask import Flask, session, request from werkzeug.security import generate_password_hash app = Flask(__name__) app.secret_key = secrets.token_hex(32) @app.before_request def csrf_protect(): """CSRF protection middleware.""" if request.method == "POST": token = session.get('_csrf_token') if not token or token != request.form.get('_csrf_token'): abort(403) def generate_csrf_token(): """Generate CSRF token.""" if '_csrf_token' not in session: session['_csrf_token'] = secrets.token_hex(16) return session['_csrf_token'] @app.route('/form', methods=['GET', 'POST']) def protected_form(): if request.method == 'POST': # Form processing pass return render_template('form.html', csrf_token=generate_csrf_token()) # 6. Secure file uploads import os from werkzeug.utils import secure_filename ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} def allowed_file(filename): """Check if file extension is allowed.""" return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def handle_file_upload(file): """Handle file upload securely.""" if not file or not allowed_file(file.filename): raise ValueError("Invalid file") filename = secure_filename(file.filename) # Check file size (max 10MB) file.seek(0, os.SEEK_END) size = file.tell() file.seek(0) if size > 10 * 1024 * 1024: raise ValueError("File too large") # Save file file.save(os.path.join('uploads', filename)) return filename # 7. Environment variable management import os from dotenv import load_dotenv load_dotenv() # Never hardcode secrets DATABASE_URL = os.getenv('DATABASE_URL') SECRET_KEY = os.getenv('SECRET_KEY') API_KEY = os.getenv('API_KEY') if not all([DATABASE_URL, SECRET_KEY, API_KEY]): raise ValueError("Missing required environment variables") # 8. Logging security import logging # Configure secure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', filename='app.log' ) # Never log sensitive information def authenticate_user(username, password): # Bad: Logging password logging.info(f"Authenticating user: {username} with password: {password}") # Good: Log without sensitive data logging.info(f"Authenticating user: {username}") # 9. Rate limiting from flask_limiter import Limiter from flask_limiter.util import get_remote_address limiter = Limiter( app, key_func=get_remote_address, default_limits=["200 per day", "50 per hour"] ) @app.route("/login", methods=["POST"]) @limiter.limit("5 per minute") def login(): # Login logic pass # 10. HTTPS enforcement from flask_sslify import SSLify app = Flask(__name__) sslify = SSLify(app) # Redirect HTTP to HTTPS # 11. Secure headers from flask import make_response @app.after_request def add_security_headers(response): """Add security headers to response.""" response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-Frame-Options'] = 'DENY' response.headers['X-XSS-Protection'] = '1; mode=block' response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' response.headers['Content-Security-Policy'] = "default-src 'self'" return response # 12. Dependency security # Keep dependencies updated # Use tools like safety to check for vulnerabilities # pip install safety # safety check # 13. Code review and security testing # Use static analysis tools # bandit - Python security linter # pip install bandit # bandit -r . # 14. Data encryption from cryptography.fernet import Fernet def encrypt_data(data): """Encrypt sensitive data.""" key = Fernet.generate_key() f = Fernet(key) return f.encrypt(data.encode()), key def decrypt_data(encrypted_data, key): """Decrypt data.""" f = Fernet(key) return f.decrypt(encrypted_data).decode() # 15. Secure random number generation # Never use random module for security import secrets def generate_session_id(): """Generate secure session ID.""" return secrets.token_hex(16) def generate_password_reset_token(): """Generate password reset token.""" return secrets.token_urlsafe(32)

Error Handling and Logging

Proper error handling and logging practices:

# Error handling best practices

# 1. Use specific exception types
class ValidationError(Exception):
    """Raised when validation fails."""
    pass

class DatabaseError(Exception):
    """Raised when database operations fail."""
    pass

class AuthenticationError(Exception):
    """Raised when authentication fails."""
    pass

# 2. Proper exception handling
def process_user_data(user_data):
    """Process user data with proper error handling."""
    try:
        # Validate input
        if not user_data.get('email'):
            raise ValidationError("Email is required")
        
        # Process data
        user = create_user(user_data)
        
        # Log success
        logger.info(f"Successfully processed user: {user.id}")
        
        return user
    
    except ValidationError as e:
        logger.warning(f"Validation failed: {e}")
        raise  # Re-raise validation errors
    
    except DatabaseError as e:
        logger.error(f"Database error: {e}")
        # Attempt recovery or cleanup
        rollback_transaction()
        raise
    
    except Exception as e:
        logger.error(f"Unexpected error: {e}", exc_info=True)
        # Don't expose internal errors to user
        raise InternalServerError("An unexpected error occurred")

# 3. Context managers for resource management
class DatabaseConnection:
    def __enter__(self):
        self.connection = create_connection()
        return self.connection
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.connection:
            self.connection.close()

def process_with_db():
    with DatabaseConnection() as conn:
        # Database operations
        result = conn.execute("SELECT * FROM users")
        return result.fetchall()
    # Connection automatically closed

# 4. Logging configuration
import logging
import logging.config

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'standard',
            'level': 'INFO'
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'app.log',
            'formatter': 'detailed',
            'level': 'DEBUG'
        },
        'error_file': {
            'class': 'logging.FileHandler',
            'filename': 'error.log',
            'formatter': 'detailed',
            'level': 'ERROR'
        }
    },
    'loggers': {
        '': {  # Root logger
            'handlers': ['console', 'file', 'error_file'],
            'level': 'DEBUG'
        },
        'requests': {  # Third-party library logger
            'handlers': ['file'],
            'level': 'WARNING',
            'propagate': False
        }
    }
}

logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger(__name__)

# 5. Structured logging
import json
from datetime import datetime

class StructuredLogger:
    def __init__(self, name):
        self.logger = logging.getLogger(name)
    
    def log_event(self, event_type, **data):
        """Log structured event data."""
        log_entry = {
            'timestamp': datetime.utcnow().isoformat(),
            'event_type': event_type,
            'data': data
        }
        
        self.logger.info(json.dumps(log_entry))

# Usage
structured_logger = StructuredLogger(__name__)
structured_logger.log_event('user_login', user_id=123, ip_address='192.168.1.1')
structured_logger.log_event('payment_processed', amount=99.99, currency='USD')

# 6. Custom exception hierarchy
class ApplicationError(Exception):
    """Base exception for application errors."""
    def __init__(self, message, error_code=None):
        super().__init__(message)
        self.error_code = error_code or self.__class__.__name__

class BusinessLogicError(ApplicationError):
    """Errors related to business logic violations."""
    pass

class ExternalServiceError(ApplicationError):
    """Errors from external service calls."""
    pass

# 7. Error recovery and retry logic
import time
import random

def retry_with_backoff(func, max_attempts=3, backoff_factor=2):
    """Retry function with exponential backoff."""
    for attempt in range(max_attempts):
        try:
            return func()
        except Exception as e:
            if attempt == max_attempts - 1:
                raise
            
            wait_time = backoff_factor ** attempt + random.uniform(0, 1)
            logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait_time:.2f}s")
            time.sleep(wait_time)

# Usage
@retry_with_backoff
def call_external_api():
    # API call that might fail
    pass

# 8. Graceful degradation
def get_user_preferences(user_id, default_preferences=None):
    """Get user preferences with fallback."""
    try:
        return load_user_preferences_from_db(user_id)
    except DatabaseError:
        logger.warning(f"Could not load preferences for user {user_id}, using defaults")
        return default_preferences or {}
    except Exception as e:
        logger.error(f"Unexpected error loading preferences: {e}")
        return default_preferences or {}

# 9. Error boundaries (similar to try-catch in other languages)
class ErrorBoundary:
    def __init__(self, fallback_function=None):
        self.fallback_function = fallback_function
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                logger.error(f"Error in {func.__name__}: {e}", exc_info=True)
                if self.fallback_function:
                    return self.fallback_function(*args, **kwargs)
                raise
        return wrapper

@ErrorBoundary(fallback_function=lambda: "Default response")
def risky_operation():
    # Operation that might fail
    pass

# 10. Monitoring and alerting
def send_alert(message, severity='error'):
    """Send alert to monitoring system."""
    # Implementation depends on monitoring system
    # Could send email, Slack message, etc.
    logger.error(f"ALERT [{severity}]: {message}")

def monitor_function(func):
    """Decorator to monitor function performance and errors."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        try:
            result = func(*args, **kwargs)
            duration = time.time() - start_time
            logger.info(f"{func.__name__} completed in {duration:.2f}s")
            return result
        except Exception as e:
            duration = time.time() - start_time
            logger.error(f"{func.__name__} failed after {duration:.2f}s: {e}")
            send_alert(f"Function {func.__name__} failed: {e}")
            raise
    return wrapper

Version Control and Collaboration

Best practices for version control and team collaboration:

# .gitignore for Python projects
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# Git commit message guidelines
# Format: type(scope): description
#
# Types:
# feat: A new feature
# fix: A bug fix
# docs: Documentation only changes
# style: Changes that do not affect the meaning of the code
# refactor: A code change that neither fixes a bug nor adds a feature
# test: Adding missing tests or correcting existing tests
# chore: Changes to the build process or auxiliary tools
#
# Examples:
# feat(auth): add user registration endpoint
# fix(api): handle null values in user data
# docs(readme): update installation instructions
# style: format code with black
# refactor(db): optimize query performance
# test: add unit tests for user service
# chore: update dependencies

# Branch naming conventions
# feature/feature-name
# bugfix/bug-description
# hotfix/critical-fix
# release/v1.2.3

# Pull request template
## Description
Brief description of the changes made.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to break)
- [ ] Documentation update

## How Has This Been Tested?
Describe the tests that you ran to verify your changes.

## Checklist:
- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

# Code review checklist
## Functionality
- [ ] Code compiles without errors
- [ ] Code runs without exceptions
- [ ] All tests pass
- [ ] New functionality works as expected
- [ ] Edge cases are handled properly

## Code Quality
- [ ] Code follows PEP 8 style guidelines
- [ ] Code is well-documented
- [ ] Variable and function names are descriptive
- [ ] No hardcoded values
- [ ] No unused imports or variables

## Security
- [ ] Input validation is implemented
- [ ] SQL injection prevention
- [ ] XSS prevention
- [ ] Authentication and authorization checks
- [ ] Sensitive data is not logged

## Performance
- [ ] No obvious performance issues
- [ ] Database queries are optimized
- [ ] Memory usage is reasonable
- [ ] Caching is implemented where appropriate

## Testing
- [ ] Unit tests are included
- [ ] Integration tests are included
- [ ] Edge cases are tested
- [ ] Error conditions are tested

## Documentation
- [ ] Code is documented
- [ ] README is updated if necessary
- [ ] API documentation is updated
- [ ] Comments explain complex logic

Final Thoughts

Following these best practices will help you write clean, maintainable, secure, and efficient Python code. Remember that best practices evolve over time, so stay updated with the latest developments in the Python community.