Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.
Use When
- Use when writing or reviewing any Python code in our SaaS projects — defines Python version, project layout, tooling (uv, ruff, mypy), typing, Pydantic v2, logging, configuration, async rules, error handling, testing, and security baseline. Load this before any other Python skill.
- The task needs reusable judgment, domain constraints, or a proven workflow rather than ad hoc advice.
Do Not Use When
- The task is unrelated to
python-modern-standardsor would be better handled by a more specific companion skill. - The request only needs a trivial answer and none of this skill's constraints or references materially help.
Required Inputs
- Gather relevant project context, constraints, and the concrete problem to solve; load
referencesonly as needed. - Confirm the desired deliverable: design, code, review, migration plan, audit, or documentation.
Workflow
- Read this
SKILL.mdfirst, then load only the referenced deep-dive files that are necessary for the task. - For Python sidecars, FastAPI services, workers, queue consumers, or API integrations, load
references/api-container-sidecar-engineering.md. - For containerized Python work, pair with
docker-development. - Apply the ordered guidance, checklists, and decision rules in this skill instead of cherry-picking isolated snippets.
- Produce the deliverable with assumptions, risks, and follow-up work made explicit when they matter.
Quality Standards
- Keep outputs execution-oriented, concise, and aligned with the repository's baseline engineering standards.
- Preserve compatibility with existing project conventions unless the skill explicitly requires a stronger standard.
- Prefer deterministic, reviewable steps over vague advice or tool-specific magic.
Anti-Patterns
- Treating examples as copy-paste truth without checking fit, constraints, or failure modes.
- Loading every reference file by default instead of using progressive disclosure.
Outputs
- A concrete result that fits the task: implementation guidance, review findings, architecture decisions, templates, or generated artifacts.
- Clear assumptions, tradeoffs, or unresolved gaps when the task cannot be completed from available context alone.
- References used, companion skills, or follow-up actions when they materially improve execution.
Evidence Produced
| Category | Artifact | Format | Example |
|---|---|---|---|
| Correctness | Test plan | Markdown doc per skill-composition-standards/references/test-plan-template.md covering pytest layout, type checks, and coverage targets |
docs/python/test-plan.md |
References
- Use the
references/directory for deep detail after reading the core workflow below. - Use
references/api-container-sidecar-engineering.mdwhen Python participates in APIs, workers, queues, sidecars, or Dockerized service delivery.
The house style for Python in our PHP + Android + iOS SaaS stack. Every Python file in our projects must follow this skill. Other Python skills (saas-integration, data-analytics, document-generation, ml-predictive, data-pipelines) assume you have read this first.
When this skill applies
- Starting any Python project, service, script, or job worker.
- Adding Python to an existing PHP-backed SaaS.
- Reviewing or refactoring Python code.
- Setting up CI for a Python codebase.
Non-negotiables
- Python 3.11+ (we target 3.12 unless a dependency forces 3.11).
src/layout withpyproject.toml. Nosetup.py. No flat layout.- uv for package management, lockfile committed.
- ruff for formatting + linting (replaces black, isort, flake8).
- Type hints on every function signature. mypy --strict or pyright in CI.
- Pydantic v2 at every external boundary (API I/O, queue payloads, config, DB DTOs).
- structlog with JSON output in production.
- Configuration via pydantic-settings, never bare
os.environ[...]. - No bare
except:— ever. Use a custom exception hierarchy. - Tests in pytest. Coverage threshold enforced in CI.
Python version
Use 3.12 as the baseline. 3.11 is acceptable when a server can't upgrade yet. Do not target <3.11 — we rely on TypeAlias, match, exception groups, faster CPython, and PEP 695 type parameter syntax (3.12).
Pin the version in pyproject.toml:
[project]
requires-python = ">=3.11,<3.13"
Project layout
service-name/
|-- pyproject.toml
|-- uv.lock
|-- README.md
|-- .env.example
|-- .gitignore
|-- src/
| `-- service_name/
| |-- __init__.py
| |-- main.py # entrypoint (FastAPI app, worker bootstrap)
| |-- config.py # pydantic-settings Settings
| |-- logging_config.py # structlog setup
| |-- exceptions.py # custom exception hierarchy
| |-- api/ # FastAPI routers (if sidecar)
| |-- workers/ # worker tasks (if queue consumer)
| |-- domain/ # pure business logic, no I/O
| |-- adapters/ # DB, HTTP, file system — anything with I/O
| |-- schemas/ # Pydantic models
| `-- utils/
|-- tests/
| |-- unit/
| |-- integration/
| `-- conftest.py
`-- scripts/ # one-off CLI scripts
See references/project-layout.md for the full pyproject.toml template, monorepo considerations, and when to split a service into multiple packages.
Package management — uv
Use uv (Astral). Fast, drop-in replacement for pip + pip-tools + virtualenv. Commit the lockfile.
uv init # bootstrap a project
uv add fastapi # add a dependency
uv add --dev pytest ruff # add a dev dependency
uv sync # install exact versions from lockfile
uv run pytest # run a command in the venv
uv lock --upgrade # upgrade lockfile
Never mix uv with pip, poetry, or pipenv in the same project. See references/tooling-uv-ruff.md.
Formatting + linting — ruff
Ruff is the only formatter/linter. It replaces black, isort, flake8, pyupgrade, bugbear, and more.
# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = [
"E", "F", "W", # pycodestyle + pyflakes
"I", # isort
"UP", # pyupgrade
"B", # bugbear
"S", # bandit (security)
"C4", # comprehensions
"SIM", # simplify
"RET", # return
"PL", # pylint
"RUF", # ruff-specific
]
ignore = ["E501"] # line length handled by formatter
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # allow assert in tests
Pre-commit hook runs ruff format + ruff check --fix. CI runs ruff check (no auto-fix).
Typing — mypy --strict or pyright
Every function signature has types. No untyped def. Use mypy --strict or pyright --strict in CI. Pick one per project, not both.
# GOOD
def compute_mrr(subscriptions: list[Subscription], as_of: date) -> Decimal:
return sum((s.monthly_price for s in subscriptions if s.is_active(as_of)), Decimal(0))
# BAD
def compute_mrr(subscriptions, as_of):
...
For complex typing (generics, Protocols, TypedDict, overloads, exhaustive matching), see references/typing-mypy-pyright.md.
Pydantic v2 — at every boundary
Use Pydantic v2 models for:
- FastAPI request/response bodies
- Queue message payloads (RQ, Celery)
- Configuration (via pydantic-settings)
- DTOs crossing module boundaries when validation matters
- External API responses (validate before trusting)
from pydantic import BaseModel, Field, EmailStr
from decimal import Decimal
class InvoiceCreate(BaseModel):
tenant_id: int = Field(..., gt=0)
customer_email: EmailStr
amount: Decimal = Field(..., gt=0, decimal_places=2)
currency: str = Field(..., pattern=r"^[A-Z]{3}$")
model_config = {"frozen": True, "extra": "forbid"}
Never pass raw dicts across module boundaries when you can use a Pydantic model. Do not use Pydantic v1 syntax (@validator, Config class, .dict()). See references/pydantic-v2-patterns.md.
Logging — structlog, JSON in production
Use structlog. Bind request/tenant/correlation IDs to every log line. JSON output in production, plain console in development.
import structlog
logger = structlog.get_logger()
logger.info("invoice_created", invoice_id=inv.id, tenant_id=inv.tenant_id, amount=str(inv.amount))
Never use print(). Never use f-strings inside log calls — pass structured kwargs so fields are queryable. See references/logging-structlog.md.
Configuration — pydantic-settings
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
database_url: str
redis_url: str = "redis://localhost:6379/0"
php_app_base_url: str
internal_shared_secret: str = Field(..., min_length=32)
environment: str = Field(default="development", pattern=r"^(development|staging|production)$")
settings = Settings() # fails fast on startup if anything is missing/invalid
Never call os.environ[...] outside config.py. Never hardcode secrets.
Async vs sync rules
- Sync by default for scripts, workers, data jobs.
- Async for FastAPI — but consistent: if a handler is
async def, everything it awaits must be async. Never call blocking I/O inside an async handler (useasyncio.to_threadif you must). - Never mix in one path. If a codepath is sync, don't sprinkle
async defin it. If it's async, don't call.sync()wrappers. - pandas, numpy, scikit-learn, and most ORMs are sync. Treat that as a signal.
See references/async-vs-sync.md.
Error handling — custom exception hierarchy
Define a root app exception and subclass by category. Translate at boundaries (infra exception → domain exception → HTTP response).
class AppError(Exception):
"""Base for all application errors."""
class ValidationError(AppError): ...
class NotFoundError(AppError): ...
class AuthorizationError(AppError): ...
class ExternalServiceError(AppError): ...
class ConfigurationError(AppError): ...
Never except: or except Exception: without re-raising. Log with context, then raise. See references/error-handling.md.
Testing — pytest
- Tests in
tests/unit/andtests/integration/mirroringsrc/layout. conftest.pyfor shared fixtures; one per package level.- Use
pytest.mark.parametrizefor table-driven tests. - Never mock what you own. Mock only external boundaries (HTTP, filesystem, time).
- Coverage threshold 80% for domain code, 60% overall; enforced in CI.
- Separate unit from integration:
pytest -m 'not integration'for fast local loops.
See references/testing-pytest.md.
Security baseline
- Secrets only via env → pydantic-settings. Never in code, never in logs.
- SQL always parameterized (SQLAlchemy Core/ORM or
cursor.execute(sql, params)). Nof"SELECT ... {user_input}". - Validate all external input with Pydantic at the boundary.
- Dependency scanning:
pip-auditorsafetyin CI, weekly schedule. - SAST:
ruffwithSrules (bandit);semgrepoptional for deeper checks. - Never
eval,exec,pickle.loadfrom untrusted sources. Nevershell=Truewith user input. - File paths validated against a safe base directory.
- See
references/security-baseline.mdfor the full checklist, and cross-reference withvibe-security-skillfor web-app concerns when the Python service exposes HTTP.
Anti-patterns (do not do these)
- Mutable default arguments (
def f(x=[])) — useNoneand initialize inside. from x import *— explicit imports only.- Blocking I/O in
async defhandlers — will deadlock the event loop. time.sleepin workers — useasyncio.sleepor scheduler delays.except Exception: pass— silences bugs. Always log and re-raise or handle specifically.- Catching
BaseException— catchesKeyboardInterrupt,SystemExit. Don't. - Building SQL with f-strings — SQL injection.
os.system()/shell=Truewith user data — command injection.- Global mutable state (module-level lists/dicts mutated at runtime) — race conditions in async/threaded code.
datetime.now()without tz — usedatetime.now(UTC).Decimalvsfloatconfusion for money — alwaysDecimalfor currency, neverfloat.
See references/anti-patterns.md.
CI gates (what must pass before merge)
ruff format --check .
ruff check .
mypy --strict src/
pytest --cov=src --cov-fail-under=80
pip-audit
Read next
When the task requires it, load:
references/python-saas-integration.md— how Python plugs into PHP + mobile SaaS.python-data-analytics— pandas, KPI computation, financial math.python-document-generation— Excel, Word, PDF output.python-ml-predictive— forecasting, classification, anomaly detection.python-data-pipelines— ETL, OCR, image processing, API syncs.
References
references/project-layout.mdreferences/tooling-uv-ruff.mdreferences/typing-mypy-pyright.mdreferences/pydantic-v2-patterns.mdreferences/logging-structlog.mdreferences/async-vs-sync.mdreferences/error-handling.mdreferences/testing-pytest.mdreferences/security-baseline.mdreferences/anti-patterns.mdreferences/api-container-sidecar-engineering.md