Python Skill
Load with: base.md
Type Hints
- •Use type hints on all function signatures
- •Use
typingmodule for complex types - •Run
mypy --strictin CI
python
def process_user(user_id: int, options: dict[str, Any] | None = None) -> User:
...
Project Structure
code
project/ ├── src/ │ └── package_name/ │ ├── __init__.py │ ├── core/ # Pure business logic │ │ ├── __init__.py │ │ ├── models.py # Pydantic models / dataclasses │ │ └── services.py # Pure functions │ ├── infra/ # Side effects │ │ ├── __init__.py │ │ ├── api.py # FastAPI routes │ │ └── db.py # Database operations │ └── utils/ # Shared utilities ├── tests/ │ ├── unit/ │ └── integration/ ├── pyproject.toml └── CLAUDE.md
Tooling (Required)
toml
# pyproject.toml [tool.ruff] line-length = 100 select = ["E", "F", "I", "N", "W", "UP"] [tool.mypy] strict = true [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--cov=src --cov-report=term-missing --cov-fail-under=80"
Testing with Pytest
python
# tests/unit/test_services.py
import pytest
from package_name.core.services import calculate_total
class TestCalculateTotal:
def test_returns_sum_of_items(self):
# Arrange
items = [{"price": 10}, {"price": 20}]
# Act
result = calculate_total(items)
# Assert
assert result == 30
def test_returns_zero_for_empty_list(self):
assert calculate_total([]) == 0
def test_raises_on_invalid_item(self):
with pytest.raises(ValueError):
calculate_total([{"invalid": "item"}])
GitHub Actions
yaml
name: Python Quality Gate
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -e ".[dev]"
- name: Lint (Ruff)
run: ruff check .
- name: Format Check (Ruff)
run: ruff format --check .
- name: Type Check (mypy)
run: mypy src/
- name: Test with Coverage
run: pytest
Pre-Commit Hooks
yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [pydantic]
args: [--strict]
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest tests/unit -x --tb=short
language: system
pass_filenames: false
always_run: true
Install and setup:
bash
pip install pre-commit pre-commit install
Patterns
Pydantic for Data Validation
python
from pydantic import BaseModel, Field
class CreateUserRequest(BaseModel):
email: str = Field(..., min_length=5)
name: str = Field(..., max_length=100)
Dependency Injection
python
# Don't import dependencies directly in business logic
# Pass them in
# Bad
from .db import database
def get_user(user_id: int) -> User:
return database.fetch(user_id)
# Good
def get_user(user_id: int, db: Database) -> User:
return db.fetch(user_id)
Result Pattern (No Exceptions in Core)
python
from dataclasses import dataclass
@dataclass
class Result[T]:
value: T | None
error: str | None
@property
def is_ok(self) -> bool:
return self.error is None
Python Anti-Patterns
- •❌
from module import * - •❌ Mutable default arguments
- •❌ Bare
except:clauses - •❌ Using
type: ignorewithout explanation - •❌ Global variables for state
- •❌ Classes when functions suffice