Python Best Practices
Domain-Specific References
Load the relevant reference when the task involves these domains:
- FastAPI applications: Defer to the
fastapi skill for project setup, endpoints, error handling, and Pydantic integration
- Dataframe / data engineering: Read references/dataframe.md — columnar thinking, vectorization over row loops, method chaining, Pandas → Polars → DuckDB → Spark
- Python data model: Read references/datamodel.md —
__iter__/__next__, __enter__/__exit__, descriptors, @property, native-feeling APIs
- Dependency & supply chain security: Read references/security.md — CVE checking, supply chain attack prevention, dependency auditing, emergency response
Code Style
- Python 3.12+ with pyproject.toml configuration
- Follow PEP 8; use Ruff for linting and formatting
- After writing or modifying Python files, run Ruff to lint, auto-fix, and format:
ruff check --fix . && ruff format .
- 4 spaces indentation, 120-char line limit
- Type hints required for all public APIs using built-in generics (
list[str], dict[str, int]), not typing.List/typing.Dict
- Provide PEP 257 docstrings for all public functions and classes
- Break complex functions into smaller, well-named functions
Naming Conventions
# Files: snake_case
mcp_server.py
# Classes: PascalCase
class CustomerQueryTool:
# Functions/variables: snake_case
async def analyze_customer_query():
server_config = get_config()
# Constants: SCREAMING_SNAKE_CASE
MAX_RETRY_ATTEMPTS = 3
Project Structure
Use this layout for new Python projects:
project-name/
├── pyproject.toml # Project metadata, dependencies, tool config
├── src/
│ └── package_name/
│ ├── __init__.py
│ ├── main.py
│ └── models.py
├── tests/
│ ├── conftest.py # Shared fixtures
│ └── test_main.py
└── README.md
- Use
src/ layout to prevent accidental imports of uninstalled code
- Configure all tools (ruff, pytest, mypy) in
pyproject.toml
- Prefer
uv for dependency management; fall back to pip
Configuration & Environment Variables
Always use python-dotenv for environment variable management. Include it in project dependencies and call load_dotenv() at the application entry point before any os.environ access.
- Add
python-dotenv to [project.dependencies] in pyproject.toml
- Call
load_dotenv() once at the top of the entry point (main.py), never inside library code
- Add
.env to .gitignore; commit a .env.example with placeholder values for documentation
- Use
os.environ["KEY"] (not os.getenv) to fail fast on missing required values
- For typed configuration, prefer
pydantic-settings (BaseSettings with env_file)
# main.py — entry point
from dotenv import load_dotenv
load_dotenv() # must precede any os.environ access
import os
DATABASE_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["API_KEY"]
Testing
- Use pytest with
conftest.py for shared fixtures
- Use
@pytest.mark.parametrize for input variation
- Use
Faker() for generating realistic test data
- Use
hypothesis for property-based testing of pure functions
- Use
schemathesis for property-based testing of API endpoints
- Use
pytest-snapshot for snapshot testing API responses
- Measure coverage with
pytest-cov; write tests for uncovered paths
- Handle edge cases: empty inputs, invalid types, boundary values, large datasets
# Example: parametrized test with fixture
@pytest.fixture
def sample_user(faker):
return {"name": faker.name(), "email": faker.email()}
@pytest.mark.parametrize("quantity,expected", [(0, 0), (5, 50), (-1, ValueError)])
def test_calculate_total(quantity, expected):
if isinstance(expected, type) and issubclass(expected, Exception):
with pytest.raises(expected):
calculate_total(price=10, quantity=quantity)
else:
assert calculate_total(price=10, quantity=quantity) == expected
Dependency & Supply Chain Security
Run pip-audit before adding or upgrading any dependency. Pin exact versions in production. Treat every third-party package as an attack surface — see references/security.md for CVE checking workflow, supply chain attack patterns, CI automation, and emergency response.
Error Handling
- Prefer specific exceptions over generic
Exception
- Use custom exception classes for domain errors
- Document raised exceptions in docstrings
- Handle cleanup with context managers (
with), not bare try/finally
1---2name: python-best-practices3description: Apply modern Python best practices, conventions, and architectural patterns to production-ready code. Use when writing, reviewing, or refactoring Python to follow PEP 8, type hints, pytest and hypothesis testing, dataframe workflows (Pandas, Polars, DuckDB, Spark), and Python data model patterns (dunder methods, iterators, context managers, descriptors). For Python 3.12+ with pyproject.toml and Ruff. DO NOT use for FastAPI (use fastapi skill).4---56# Python Best Practices78## Domain-Specific References910Load the relevant reference when the task involves these domains:1112- **FastAPI applications**: Defer to the `fastapi` skill for project setup, endpoints, error handling, and Pydantic integration13- **Dataframe / data engineering**: Read [references/dataframe.md](references/dataframe.md) — columnar thinking, vectorization over row loops, method chaining, Pandas → Polars → DuckDB → Spark14- **Python data model**: Read [references/datamodel.md](references/datamodel.md) — `__iter__`/`__next__`, `__enter__`/`__exit__`, descriptors, `@property`, native-feeling APIs15- **Dependency & supply chain security**: Read [references/security.md](references/security.md) — CVE checking, supply chain attack prevention, dependency auditing, emergency response1617## Code Style1819- Python 3.12+ with pyproject.toml configuration20- Follow PEP 8; use Ruff for linting and formatting21- After writing or modifying Python files, run Ruff to lint, auto-fix, and format: `ruff check --fix . && ruff format .`22- 4 spaces indentation, 120-char line limit23- Type hints required for all public APIs using built-in generics (`list[str]`, `dict[str, int]`), not `typing.List`/`typing.Dict`24- Provide PEP 257 docstrings for all public functions and classes25- Break complex functions into smaller, well-named functions2627### Naming Conventions2829```python30# Files: snake_case31mcp_server.py3233# Classes: PascalCase34class CustomerQueryTool:3536# Functions/variables: snake_case37async def analyze_customer_query():38server_config = get_config()3940# Constants: SCREAMING_SNAKE_CASE41MAX_RETRY_ATTEMPTS = 342```4344## Project Structure4546Use this layout for new Python projects:4748```49project-name/50├── pyproject.toml # Project metadata, dependencies, tool config51├── src/52│ └── package_name/53│ ├── __init__.py54│ ├── main.py55│ └── models.py56├── tests/57│ ├── conftest.py # Shared fixtures58│ └── test_main.py59└── README.md60```6162- Use `src/` layout to prevent accidental imports of uninstalled code63- Configure all tools (ruff, pytest, mypy) in `pyproject.toml`64- Prefer `uv` for dependency management; fall back to `pip`6566## Configuration & Environment Variables6768Always use `python-dotenv` for environment variable management. Include it in project dependencies and call `load_dotenv()` at the application entry point before any `os.environ` access.6970- Add `python-dotenv` to `[project.dependencies]` in `pyproject.toml`71- Call `load_dotenv()` once at the top of the entry point (`main.py`), never inside library code72- Add `.env` to `.gitignore`; commit a `.env.example` with placeholder values for documentation73- Use `os.environ["KEY"]` (not `os.getenv`) to fail fast on missing required values74- For typed configuration, prefer `pydantic-settings` (`BaseSettings` with `env_file`)7576```python77# main.py — entry point78from dotenv import load_dotenv7980load_dotenv() # must precede any os.environ access8182import os8384DATABASE_URL = os.environ["DATABASE_URL"]85API_KEY = os.environ["API_KEY"]86```8788## Testing8990- Use pytest with `conftest.py` for shared fixtures91- Use `@pytest.mark.parametrize` for input variation92- Use `Faker()` for generating realistic test data93- Use `hypothesis` for property-based testing of pure functions94- Use `schemathesis` for property-based testing of API endpoints95- Use `pytest-snapshot` for snapshot testing API responses96- Measure coverage with `pytest-cov`; write tests for uncovered paths97- Handle edge cases: empty inputs, invalid types, boundary values, large datasets9899```python100# Example: parametrized test with fixture101@pytest.fixture102def sample_user(faker):103 return {"name": faker.name(), "email": faker.email()}104105@pytest.mark.parametrize("quantity,expected", [(0, 0), (5, 50), (-1, ValueError)])106def test_calculate_total(quantity, expected):107 if isinstance(expected, type) and issubclass(expected, Exception):108 with pytest.raises(expected):109 calculate_total(price=10, quantity=quantity)110 else:111 assert calculate_total(price=10, quantity=quantity) == expected112```113114## Dependency & Supply Chain Security115116Run `pip-audit` before adding or upgrading any dependency. Pin exact versions in production. Treat every third-party package as an attack surface — see [references/security.md](references/security.md) for CVE checking workflow, supply chain attack patterns, CI automation, and emergency response.117118## Error Handling119120- Prefer specific exceptions over generic `Exception`121- Use custom exception classes for domain errors122- Document raised exceptions in docstrings123- Handle cleanup with context managers (`with`), not bare `try/finally`