UNPKG

metacoding

Version:

Guided Development Workflow for GitHub Copilot - Transform your coding experience with AI-guided standards, structured workflows, and quality practices

340 lines (261 loc) 9.83 kB
--- description: 'Python-specific coding standards and best practices' applyTo: '**/*.py' language: 'python' --- # Python Coding Standards and Best Practices ## Language and Framework Preferences - **Primary Language:** Python 3.9+ for all Python projects - **Code Style:** Follow PEP 8 with Black formatter for consistent formatting - **Type Hints:** Use type hints for all function signatures and complex variables - **Target Compatibility:** Python 3.9+ (use modern Python features appropriately) ## Code Quality Guidelines - **Readability:** Follow "The Zen of Python" - explicit is better than implicit - **Functions:** Keep functions focused, ideally under 30 lines for better readability - **Magic Numbers:** Use named constants or configuration files instead of magic numbers - **Error Handling:** Use specific exception types, avoid bare `except:` clauses - **Memory Management:** Be mindful of memory usage, use generators for large datasets - **Async Patterns:** Use `asyncio` for I/O-bound operations, avoid blocking operations ## Naming Conventions - **Files:** Use snake_case for file names (e.g., `user_service.py`) - **Classes:** PascalCase (e.g., `UserService`, `DatabaseConnection`) - **Functions/Methods:** snake_case (e.g., `get_user_by_id`, `validate_input`) - **Variables:** snake_case (e.g., `user_id`, `is_valid`) - **Constants:** SCREAMING_SNAKE_CASE (e.g., `MAX_RETRY_ATTEMPTS`, `DEFAULT_TIMEOUT`) - **Private Attributes:** Single underscore prefix (e.g., `_internal_method`) - **Name Mangling:** Double underscore prefix only when necessary (e.g., `__private_attr`) ## Code Organization - **Single Responsibility:** One class per file for complex classes, related utilities can be grouped - **Imports:** Follow PEP 8 import order (standard library, third-party, local imports) - **Module Structure:** Use `__init__.py` files for package initialization and clean imports - **Entry Points:** Use `if __name__ == "__main__":` for script entry points ## Python-Specific Best Practices ### Type Hints and Documentation ```python from typing import List, Dict, Optional, Union from dataclasses import dataclass def process_user_data( users: List[Dict[str, Union[str, int]]], active_only: bool = True ) -> List[str]: """Process user data and return list of usernames. Args: users: List of user dictionaries containing user information active_only: If True, only include active users Returns: List of usernames matching the criteria Raises: ValueError: If user data format is invalid """ pass ``` ### Error Handling Patterns ```python # Good: Specific exception handling try: user = get_user_by_id(user_id) except UserNotFoundError as e: logger.warning(f"User {user_id} not found: {e}") return None except DatabaseConnectionError as e: logger.error(f"Database connection failed: {e}") raise # Good: Use custom exceptions class ValidationError(Exception): """Raised when data validation fails.""" pass class UserNotFoundError(Exception): """Raised when requested user cannot be found.""" pass ``` ### Resource Management ```python # Good: Use context managers with open('data.json', 'r') as file: data = json.load(file) # Good: Create custom context managers when needed from contextlib import contextmanager @contextmanager def database_transaction(): conn = get_connection() trans = conn.begin() try: yield conn trans.commit() except Exception: trans.rollback() raise finally: conn.close() ``` ### Performance Considerations ```python # Good: Use generators for large datasets def process_large_file(filename: str): with open(filename, 'r') as file: for line in file: yield process_line(line) # Good: Use list comprehensions for simple transformations active_users = [user for user in users if user.is_active] # Good: Use appropriate data structures from collections import defaultdict, deque user_groups = defaultdict(list) ``` ## Testing Standards ### Test Framework Preferences - **Primary Framework:** pytest for all testing - **Fixtures:** Use pytest fixtures for test data and setup - **Parametrized Tests:** Use `pytest.mark.parametrize` for multiple test cases - **Mocking:** Use `unittest.mock` or `pytest-mock` for mocking dependencies ### Test File Organization ```python # tests/test_user_service.py import pytest from unittest.mock import Mock, patch from src.services.user_service import UserService from src.exceptions import UserNotFoundError class TestUserService: @pytest.fixture def user_service(self): return UserService(db_connection=Mock()) @pytest.mark.parametrize("user_id,expected", [ (1, True), (999, False), ]) def test_user_exists(self, user_service, user_id, expected): # Test implementation pass def test_get_user_not_found_raises_exception(self, user_service): with pytest.raises(UserNotFoundError): user_service.get_user_by_id(999) ``` ## Dependency Management ### Package Management - **Primary Tool:** Poetry for dependency management and packaging - **Requirements:** Maintain both `pyproject.toml` and `requirements.txt` - **Development Dependencies:** Separate dev dependencies (testing, linting, formatting) - **Version Pinning:** Pin exact versions for applications, use ranges for libraries ### Virtual Environment Management ```bash # Create and activate virtual environment python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # Or use Poetry poetry install poetry shell ``` ## Code Quality Tools ### Linting and Formatting - **Black:** Code formatting with line length 88 - **isort:** Import sorting and organization - **flake8:** Linting and style checking - **mypy:** Static type checking - **pylint:** Additional code analysis ### Configuration Examples ```toml # pyproject.toml [tool.black] line-length = 88 target-version = ['py39'] [tool.isort] profile = "black" multi_line_output = 3 [tool.mypy] python_version = "3.9" strict = true ignore_missing_imports = true ``` ## Documentation Standards ### Docstring Format Use Google-style docstrings: ```python def calculate_user_score( user_data: Dict[str, Any], weights: Optional[Dict[str, float]] = None ) -> float: """Calculate a user's composite score based on various metrics. This function computes a weighted score based on user activity, engagement, and other factors. Args: user_data: Dictionary containing user metrics and information weights: Optional custom weights for score calculation. Defaults to standard weights if not provided. Returns: Calculated score as a float between 0.0 and 100.0 Raises: ValueError: If user_data is missing required fields TypeError: If weights contain non-numeric values Example: >>> user = {"activity": 85, "engagement": 92} >>> calculate_user_score(user) 88.5 """ pass ``` ## Security Considerations ### Input Validation ```python import re from typing import Any def validate_email(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 sanitize_user_input(user_input: str) -> str: """Sanitize user input to prevent injection attacks.""" # Remove potentially dangerous characters safe_input = re.sub(r'[<>"\';]', '', user_input) return safe_input.strip() ``` ### Environment Configuration ```python import os from dataclasses import dataclass @dataclass class Config: """Application configuration from environment variables.""" database_url: str = os.getenv('DATABASE_URL', 'sqlite:///default.db') secret_key: str = os.getenv('SECRET_KEY', 'dev-key-change-in-production') debug: bool = os.getenv('DEBUG', 'False').lower() == 'true' def __post_init__(self): if self.secret_key == 'dev-key-change-in-production' and not self.debug: raise ValueError("SECRET_KEY must be set in production") ``` ## Common Anti-Patterns to Avoid - **Mutable Default Arguments:** Use `None` and check inside function - **Broad Exception Catching:** Avoid bare `except:` clauses - **Global Variables:** Minimize global state, use dependency injection - **String Concatenation in Loops:** Use `join()` for multiple strings - **Not Using Context Managers:** Always use `with` for file operations - **Ignoring PEP 8:** Follow Python style guidelines consistently - **Missing Type Hints:** Add type hints for better code documentation - **Circular Imports:** Structure modules to avoid circular dependencies ## Performance Optimization ### Memory Efficiency ```python # Use generators for large datasets def read_large_file(filename: str): with open(filename, 'r') as file: for line in file: yield line.strip() # Use __slots__ for classes with many instances class Point: __slots__ = ['x', 'y'] def __init__(self, x: float, y: float): self.x = x self.y = y ``` ### Concurrency Patterns ```python import asyncio from concurrent.futures import ThreadPoolExecutor async def process_data_async(data_list: List[str]) -> List[str]: """Process data asynchronously for I/O bound operations.""" tasks = [process_item_async(item) for item in data_list] return await asyncio.gather(*tasks) def process_cpu_intensive_data(data_list: List[str]) -> List[str]: """Use thread pool for CPU-intensive operations.""" with ThreadPoolExecutor(max_workers=4) as executor: results = list(executor.map(cpu_intensive_process, data_list)) return results ```