| name | python |
| description | Master modern Python development with production-grade tooling and idioms.
Use when writing Python code, configuring project structure, managing
dependencies with uv, linting with ruff, adding type hints, writing pytest
tests, or building FastAPI/Django/Flask applications. Triggers on "Python",
"FastAPI", "Django", "Flask", "pytest", "uv", "ruff", "pyproject.toml". Do NOT trigger for generic 'build a server' requests unless the platform/language is explicitly specified as Python (e.g. FastAPI, Django, Flask).
|
Python Engineering Standards
This skill codifies 2026 Python best practices — modern tooling (uv, ruff), strict typing, and idiomatic patterns that produce maintainable, performant code.
1. Project Setup & Structure
Tooling: uv (Not pip/pipenv/poetry)
uv is the standard package manager for Python in 2026. It replaces pip, pipenv, and poetry with a single, fast Rust binary.
uv init <project-name>
uv add <package>
uv add --dev <package>
uv run <command>
uv sync
uv python install 3.13
- ALWAYS commit both
pyproject.toml AND uv.lock to version control.
- NEVER use
requirements.txt for new projects — it is a legacy pattern.
- NEVER use
pip install directly — use uv add to keep the lock file in sync.
Directory Layout: src/ Layout
my-project/
├── .python-version # Pin Python version (e.g., 3.13)
├── pyproject.toml # Single source of truth for config
├── uv.lock # Deterministic lock file
├── src/
│ └── my_package/
│ ├── __init__.py
│ ├── main.py
│ └── models.py
└── tests/
├── __init__.py
├── conftest.py # Shared fixtures
└── test_main.py
- Use
src/ layout for packages and production apps — it prevents tests from accidentally importing the local source instead of the installed package.
- Group by domain, not file type — prefer
users/, billing/, auth/ over models/, utils/, services/.
- Use
kebab-case for directory names, snake_case for Python modules.
pyproject.toml Configuration (PEP 621)
See references/config-templates.md for the standard pyproject.toml template.
2. Type Hints (Mandatory)
Type hints are not optional — they are foundational design tools.
Rules
- Use built-in generics (Python 3.9+):
list[str], dict[str, int], tuple[int, ...]
- Use union syntax (Python 3.10+):
str | None instead of Optional[str]
- Avoid
Any — use object, Unknown, or narrow with type guards
- Use
TypedDict for dict-shaped data with known keys
- Use
Protocol for structural subtyping instead of ABCs
- Run static analysis:
mypy --strict or pyright
Examples
def get_user(user_id: int) -> User | None:
...
class Config(TypedDict):
host: str
port: int
debug: bool
def get_user(user_id):
...
def process(data: Any):
...
3. Code Quality: ruff
Ruff is the all-in-one linter and formatter for Python. It replaces Flake8, Black, isort, pyupgrade, and more.
ruff check --fix .
ruff format .
Configuration in pyproject.toml
See references/config-templates.md for the standard Ruff configuration.
- NEVER use
# noqa to suppress linter errors unless the suppression is explicitly justified by a comment explaining why.
- NEVER use
# type: ignore — fix the type error or use a proper type guard.
4. Error Handling
Patterns
- Use specific exception types — never catch bare
except: or except Exception:
- Create custom exceptions inheriting from a base project exception
- Use
from for exception chaining: raise ValueError("msg") from original_error
- Never swallow exceptions silently — at minimum, log them
class AppError(Exception):
"""Base exception for the application."""
class NotFoundError(AppError):
"""Raised when a resource is not found."""
try:
user = get_user(user_id)
except DatabaseError as e:
raise NotFoundError(f"User {user_id} not found") from e
try:
user = get_user(user_id)
except:
pass
5. Testing with pytest
Structure
- AAA pattern: Arrange, Act, Assert — one concern per test
- Use fixtures for setup/teardown, not classes with
setUp()/tearDown()
- Use
conftest.py for shared fixtures across test modules
- Use
@pytest.mark.parametrize for data-driven tests
@pytest.fixture
def client(tmp_path: Path) -> TestClient:
"""Create a test client with a temporary database."""
app = create_app(db_path=tmp_path / "test.db")
return TestClient(app)
@pytest.mark.parametrize("status_code,expected", [
(200, True),
(404, False),
(500, False),
])
def test_is_success(status_code: int, expected: bool) -> None:
assert is_success(status_code) == expected
Running Tests
uv run pytest
uv run pytest tests/test_main.py
uv run pytest -k "test_user"
uv run pytest --cov=src --cov-report=term-missing
6. Advanced Patterns & Security
For advanced guidance on asyncio concurrency, Pydantic v2 data validation, and Python security best practices, see the reference file:
Read: references/advanced-patterns.md
9. Anti-Patterns (Never Do These)
| Anti-Pattern | Why It's Wrong | Do This Instead |
|---|
import * | Pollutes namespace, breaks tooling | Explicit imports |
Mutable default args (def f(x=[])) | Shared across calls | Use None + assign |
| Global mutable state | Thread-unsafe, untestable | Dependency injection |
os.path for path manipulation | Platform-inconsistent | Use pathlib.Path |
print() for logging | No levels, no rotation | Use logging or structlog |
== None / == True | Wrong semantics | is None / is True |
10. Web Frameworks Quick Reference
| Framework | Best For | Key Pattern |
|---|
| FastAPI | REST APIs, async, auto-docs | Pydantic models, dependency injection, async def routes |
| Django | Full-stack, admin, ORM | Models → Views → Templates, manage.py, migrations |
| Flask | Lightweight APIs, prototypes | Blueprints, application factory pattern |