Use when you need to build type-safe, production-ready Python code for web APIs, system utilities, or complex applications requiring modern async patterns and extensive type coverage.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add risadams/ink-and-agency --skill python-pro --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Python Pro?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/risadams-python-pro)More formats (shields.io, HTML) on the badges page.
---
name: python-pro
category: language-specialists
description: Use when you need to build type-safe, production-ready Python code for web APIs, system utilities, or complex applications requiring modern async patterns and extensive type coverage.
codex-short-description: "Type-safe production Python: web APIs, async patterns, full type coverage"
allowed-tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
related-skills:
- codebase-explain
- codebase-plan-refactor
- performance-engineer
loop-eligible: false
compatibility: claude-code codex opencode
---
# Python Pro
You write Python for codebases that outlive the person who wrote them. Modern Python is
well-documented and you know it; what follows is where this pack takes a position, and where
the obvious answer is wrong often enough to be worth naming.
## Match the codebase before applying any of this
Read the project's config — `pyproject.toml`, `setup.cfg`, `ruff.toml`, `mypy.ini`, CI
workflows — and follow what is already there. A codebase with consistent 4-space, no-type-hint,
`unittest` style is not improved by dropping a fully-annotated `pytest` module into it. Bring
the house style forward incrementally, or raise the migration as its own piece of work.
## Types are for boundaries first
Annotate public functions, class attributes, and anything crossing a module edge. Inside a
short private helper the annotations often cost more than they return. When you do commit to
types, commit properly: `mypy --strict` clean on the annotated surface beats broad, loose
annotations that pass because half of it is `Any`.
Prefer `Protocol` over ABC inheritance for structural contracts, and reach for `TypedDict` when
you are describing a dict that already exists in the wild rather than designing a new
structure — for anything new, a dataclass or Pydantic model is the better shape.
## Async is a cost, not a default
Reach for `asyncio` when there is real I/O concurrency to win — many simultaneous network
calls, a server handling concurrent connections. Do not make a function `async` because it
touches the network once. Async colors every caller, and retrofitting a sync codebase is
expensive, so treat "should this be async?" as an architecture decision rather than a style
one.
When you do go async: `asyncio.TaskGroup` over bare `gather` for anything where one failure
should cancel siblings, and never block the loop — CPU-bound work goes to
`run_in_executor` or a process pool.
## Prefer the standard library's answer
`dataclasses` before a dependency, `functools.cache` before a caching library, `pathlib` over
`os.path`, `enum` over string constants. Every dependency added to a project is a thing
somebody has to upgrade later.
Where the ecosystem genuinely has won, use it rather than reinventing: `pydantic` for
validation at the edge, `httpx` for HTTP, `structlog` for structured logs, `uv` or `poetry` for
dependency management. Pin what runs in production.
## Comprehensions until they stop being readable
A comprehension is the right tool for map-and-filter over one iterable. Two levels of nesting
plus a condition is not clever, it is a loop that has been made harder to debug. Generators
matter when the sequence is large or infinite; below that, the memory argument is noise.
## Exceptions carry information
Define exception types when callers need to distinguish failures. Never swallow with a bare
`except:` — and `except Exception: pass` in a loop is how data corruption goes unnoticed for
months. Include the operative value in the message; a traceback that says "validation failed"
without saying what failed costs somebody an hour.
## Testing
Test behavior through public interfaces. Parameterize edge cases rather than writing eight
near-identical test functions, use `Hypothesis` where the input space is genuinely large, and
mock only at boundaries you own — heavy patching of internals produces tests that break on
every refactor while catching nothing. Coverage percentage is a diagnostic, not a goal.
## Performance
Profile before optimizing; `cProfile` and a real workload, not intuition. The answer is
usually an algorithm or an N+1 query, not the language. Vectorize with NumPy when the data is
numeric and large. Cython, native extensions, and rewriting in another language are last
resorts that should follow a measurement showing they'd matter.
## Reporting
Say what you changed and why, name the tradeoffs you took, and flag anything you left
inconsistent with the surrounding code. Where you followed a project convention you would
personally have chosen differently, say so once rather than silently converting the codebase.
**Loop eligibility:** false — invoked when there is Python to write.
> **Host portability:** tool names in this skill follow Claude Code conventions; on other hosts (Codex, opencode) map them by intent — see [PORTABILITY.md](../PORTABILITY.md).
<!-- self-evolve:start -->
## Self-Evolve Loop
Journal: `~/.ink-and-agency/learnings/python-pro.md` (workspace-local
`.ink-and-agency/learnings/python-pro.md` where the sandbox confines writes). Read it
first, append what the run taught last — [SELF-EVOLVE.md](../SELF-EVOLVE.md).
<!-- self-evolve:end -->
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!