Skip to content
Skills Library
Programming · Building & coding

python

/python

Use when the task is Python itself, in any framework or none: PEP 695 generics, mypy --strict typing, dataclass/Protocol/TypedDict/Enum choices, asyncio.TaskGroup, stdlib idioms, src/ layout + pyproject.toml with uv, ruff+mypy+pytest gate. NOT a FastAPI/ASGI service (that is fastapi), NOT a deep pytest suite (that is testing-py).

Repository
rsc-harness ↗
Licence
MIT
Last repository change
25.10.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
7
Scripts
1
HTML comments
0
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 25.10.2026, without references to retired models or APIs.

Licence

MIT, open: use and modify while retaining author attribution.

What to check before installing
  • 1 scripts in this package. Review them before installing, as Claude may execute them.

Scripts: scripts/verify.sh

What it does and when to use it

Use when the task is Python itself, in any framework or none: PEP 695 generics, mypy --strict typing, dataclass/Protocol/TypedDict/Enum choices, asyncio.TaskGroup, stdlib idioms, src/ layout + pyproject.toml with uv, ruff+mypy+pytest gate. NOT a FastAPI/ASGI service (that is fastapi), NOT a deep pytest suite (that is testing-py).

Original author description

Use when the task is Python itself, in any framework or none: PEP 695 generics, mypy --strict typing, dataclass/Protocol/TypedDict/Enum choices, asyncio.TaskGroup, stdlib idioms, src/ layout + pyproject.toml with uv, ruff+mypy+pytest gate. NOT a FastAPI/ASGI service (that is fastapi), NOT a deep pytest suite (that is testing-py).

How it works

  1. Scope — what this skill delegates
  2. Decision rules
  3. Typing
  4. Data modeling
  5. Stdlib idioms
  6. Errors & resources
  7. Async (language level)
  8. Project layout & packaging (uv)
  9. Quality gate + verify.sh
  10. pytest baseline
Original SKILL.md headings · 314 lines, 1,999 words
SKILL.md preview
---
name: python
description: "Use when the task is Python itself, in any framework or none: PEP 695 generics, mypy --strict typing, dataclass/Protocol/TypedDict/Enum choices, asyncio.TaskGroup, stdlib idioms, src/ layout + pyproject.toml with uv, ruff+mypy+pytest gate. NOT a FastAPI/ASGI service (that is fastapi), NOT a deep pytest suite (that is testing-py)."
tags: [python, typing, async, packaging, uv]
recommends: [fastapi, secure-coding, deployment]
origin: risco
---

# Modern Python at the language level

Write, review, modernize, type, and package Python that reads like a typed,
flat-control-flow, stdlib-first program whose dependencies and tooling all live in one
`pyproject.toml`. Types are part of the design, not decoration; the stdlib is large and you
reach for it before a dependency; correctness is `ruff` + a type checker + `pytest` in one gate.

Targets **Python 3.12+ (floor) / 3.14 (current**, released 7 Oct 2025): PEP 695 inline
type parameters (`class Box[T]:`, `type Alias = ...`), `asyncio.TaskGroup`, and — in 3.14 —
deferred annotation evaluation by default (PEP 649/749, no more `from __future__ import
annotations`), PEP 750 template strings (`t"..."`), and `compression.zstd`. Tooling pins:
**uv 0.11** (project + package manager), **ruff 0.15** (lint + format), **mypy 1.20
`--strict`** (or Astral's `ty`, still preview — default to mypy), **pytest 8**.

## Scope — what this skill delegates

- Building a FastAPI / ASGI service (routes, Pydantic models, SQLAlchemy, uvicorn) ->
  [`fastapi`](../fastapi/SKILL.md). That skill owns the service shape; this one owns the
  language it is written in.
- A pytest suite as the primary task — fixtures, parametrize matrices, mocking, coverage,
  property-based testing -> `testing-py` (this skill keeps only the *baseline*: a few tests
  so `verify.sh` has something to run, then hands off).
- Language-agnostic threat modeling / authz / OWASP review ->
  [`secure-coding`](../secure-coding/SKILL.md) (this skill keeps Python-specific safety: no
  `eval`/`pickle` of untrusted data, `subprocess` without `shell=True`, `secrets` over `random`).
- Containerfile / CI pipeline / deploy mechanics -> [`deployment`](../deployment/SKILL.md)
  (this skill ships only a uv-based CI note).
- Another language -> `go`, `typescript`, `rust`, etc. Django ORM/models/migrations -> `django`.

Python typing, async language semantics, and uv packaging live **here**, not in a separate
skill — this skill is the canonical authority for the language substrate under any Python program.

## Decision rules

Apply on every Python edit:

1. **Type the boundary.** Every public function, method, and module-level name gets an
   annotation; let inference handle locals. Untyped public API defeats `--strict`.
2. **Flat control flow.** Guard-clause and `return`/`raise` early; keep the happy path
   unindented — arrow code hides the logic.
3. **Stdlib before a dependency.** `pathlib`, `itertools`, `functools`, `dataclasses`,
   `collections` cover most needs; a new dep is a maintenance liability you must justify.
4. **Prefer immutability.** `@dataclass(frozen=True, slots=True)` for value objects; mutate
   only where you must — shared mutable state is the bug you debug at 2am.
5. **Everything passes the gate.** `ruff check` + `ruff format --check` + `mypy --strict` +
   `pytest` — green locally via `scripts/verify.sh` before you call it done.

## Typing

Type the boundary; run `mypy --strict` so untyped code and implicit `Any` are errors, not
silent gaps. Use **PEP 695** inline syntax for all new generic code — no explicit `TypeVar`
objects:

```python
# Good (3.12+): inline type parameter and the `type` alias statement.
def first[T](xs: list[T]) -> T:
    return xs[0]

class Box[T]: ...
type UserId = int                 # `type` statement: a real alias, lazily evaluated
# Bad (legacy): `T = TypeVar("T")` then a Generic[T] — fine in old code, don't write it new.
```

Core choices: `Protocol` (structural, no inheritance) over an ABC when you only need "has these
methods"; `X | None` (not `Optional[X]`); `Literal`/`Enum` for closed value sets; `Final` for
constants; `Self` for fluent returns; `@overload` for signature families. Narrow with
`isinstance`, `assert`, or an `is None` guard — mypy follows the flow. In **3.14** annotations
are lazy by default (PEP 649/749), so forward references resolve without `from __future__ import
annotations`.

Full PEP 695 bounds/constraints/variance, `Protocol` vs ABC, `TypedDict`
`Required`/`NotRequired`, `ParamSpec`/`TypeVarTuple`, `TypeGuard`/`TypeIs`, `cast`, and common
`--strict` errors with fixes -> [`references/typing.md`](references/typing.md).

## Data modeling

Pick the carrier by what the data *is*, not by habit:

| Need | Use | Why |
| --- | --- | --- |
| Immutable value object, typed fields, methods | `@dataclass(frozen=True, slots=True)` | Hashable, no `__dict__` overhead, real types |
| Small fixed tuple, positional + named, immutable | `NamedTuple` | Tuple semantics + field names; cheap |
| Shape of an external/JSON dict, no runtime class | `TypedDict` | Types a plain `dict` without wrapping it |
| Closed set of named constants | `Enum` / `StrEnum` / `IntEnum` | One source of truth; `Literal`-friendly |
| Mutable bag of related state with behavior | plain class / `@dataclass` | When you genuinely need mutation |
| Runtime-validated I/O model (parse untrusted data) | Pydantic -> [`fastapi`](../fastapi/SKILL.md) | Validation is a service concern, not language |

```python
from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Point:
    x: float
    y: float
    def translated(self, dx: float, dy: float) -> "Point":
        return Point(self.x + dx, self.y + dy)   # returns a new value, never mutates
```

Frozen-slots dataclass when you want methods + hashability + clear types; `NamedTuple` when
the thing genuinely *is* a small tuple you also unpack positionally.

## Stdlib idioms

Reach into the stdlib before adding a dependency.

- **`pathlib`** for all filesystem paths: `Path("data") / name`, `p.read_text()`,
  `p.glob("*.json")`, `p.with_suffix(".bak")` — typed and composable, never `os.path.join`.
- **`collections`**: `defaultdict(list)`, `Counter(words)`, `deque(maxlen=100)` for ring buffers.
- **`itertools`**: `chain`, `groupby`, `islice`, `batched` (3.12+) instead of hand-rolled loops.
- **`functools`**: `@cache` / `@lru_cache` for pure memoization, `@cached_property`, `partial`.
- **`contextlib`**: `@contextmanager`, `ExitStack` for dynamic resource sets, `suppress(FileNotFoundError)`.
- **`logging`**, never `print` in a library: `print` writes to a caller's stdout you do not own;
  `logging.getLogger(__name__)` lets them configure it.

Prefer comprehensions over `map`/`filter`+`lambda`; prefer a generator (`(... for ...)`) when
you only iterate once. Use `match` for structural dispatch over a chain of `isinstance`:

```python
from functools import cache
from pathlib import Path

@cache
def config_dir() -> Path:                 # computed once, memoized
    return Path.home() / ".config" / "myapp"

def area(shape: object) -> float:
    match shape:                          # structural dispatch, captures by attribute/key
        case {"kind": "circle", "r": float(r)}:
            return 3.14159 * r * r
        case _:
            raise TypeError(f"unknown shape: {shape!r}")
```

f-strings for formatting; note 3.14's PEP 750 `t"..."` template strings yield a `Template`
(not a `str`) for *safe custom interpolation* (e.g. escaping) — use them when an f-string would
inject untrusted text. Full cookbook (itertools/functools/collections recipes, `match`
patterns, dataclass `field`/`default_factory`/`__post_init__`, `Enum`/`StrEnum`/`IntFlag`) ->
[`references/stdlib.md`](references/stdlib.md).

## Errors & resources

Define a small exception hierarchy rooted in one base so callers can catch broadly or
narrowly; chain causes with `raise ... from`; never write a bare `except:`.

```python
class AppError(Exception): ...
class NotFoundError(AppError): ...

def load(path: Path) -> str:
    try:
        return path.read_text()
    except FileNotFoundError as e:
        raise NotFoundError(f"missing {path}") from e   # preserves the cause chain
```

Use `except*` to handle an `ExceptionGroup` (what a `TaskGroup` raises) by member type.
Prefer **EAFP** (try the operation, handle the failure) over LBYL race-prone pre-checks.
Always release resources with `with` (a context manager), not manual try/finally close.

## Async (language level)

Use `asyncio.run(main())` as the single entry point. **`asyncio.TaskGroup` (3.11+) over bare
`gather`** — it is the structured-concurrency primitive: a child failure cancels its siblings
and surfaces as an `ExceptionGroup`, and no task outlives the block.

```python
import asyncio

# Bad: gather leaks the other tasks on first failure and loses structure.
async def fetch_all_bad(ids: list[int]) -> list[bytes]:
    return await asyncio.gather(*(fetch(i) for i in ids))

# Good: TaskGroup — sibling cancellation on error, bounded lifetime, real grouping.
async def fetch_all(ids: list[int]) -> list[bytes]:
    async with asyncio.TaskGroup() as tg:
        tasks = [tg.create_task(fetch(i)) for i in ids]
    return [t.result() for t in tasks]                  # block exited => all done or raised
```

Bound every wait with `async with asyncio.timeout(5.0):`. On `CancelledError`, clean up and
**re-raise** — swallowing it breaks cancellation for the whole tree. Async is for **IO-bound**
concurrency only; CPU-bound work blocks the loop — push it to `asyncio.to_thread` / a
`ProcessPoolExecutor` (or 3.14's free-threaded build). **HTTP servers belong to
[`fastapi`](../fastapi/SKILL.md), not here.** Runtime model, `ExceptionGroup`/`except*`, queues
with backpressure, cancellation discipline, and sync<->async bridging ->
[`references/async.md`](references/async.md).

## Project layout & packaging (uv)

Use a `src/` layout so tests import the installed package, not the source tree by accident:

```text
myapp/
  src/myapp/__init__.py
  src/myapp/core.py
  tests/test_core.py
  pyproject.toml
  uv.lock              # committed
  scripts/verify.sh
```

`pyproject.toml` is the single config — PEP 621 metadata, dependency groups, and tool config.
Never hand-edit a `requirements.txt`; `uv add` writes the dep and updates `uv.lock`, which you commit.

```toml
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["httpx>=0.27"]

[project.scripts]
myapp = "myapp.core:main"          # console entry point

[dependency-groups]
dev = ["ruff>=0.15", "mypy>=1.13", "pytest>=8"]

[build-system]
requires = ["uv_build>=0.11"]
build-backend = "uv_build"         # uv's own backend, stable since July 2025

[tool.ruff]
line-length = 100

[tool.mypy]
strict = true
```

Core uv verbs (each updates `uv.lock`, which you commit):

```bash
uv init --package myapp        # scaffold pyproject.toml + src/ + .venv
uv add httpx                   # add a runtime dep
uv add --dev ruff mypy pytest  # add to the dev group
uv sync --frozen               # install exactly from the lockfile (CI + fresh clones)
uv run pytest -q               # run inside the managed venv
uv python install 3.14         # pin/install an interpreter
```

## Quality gate + verify.sh

One local gate, mirroring CI: `ruff check --fix .`, `ruff format .`, `mypy --strict src`
(or `ty check`), `pytest -q`. `scripts/verify.sh` runs all of them, skips a missing tool/dir
with a warning, and exits non-zero on any real failure — run `./scripts/verify.sh` from the
project root before declaring done. CI is `astral-sh/setup-uv` + `uv sync --frozen` + the same
four commands; full pipeline -> [`deployment`](../deployment/SKILL.md).

## pytest baseline

Carry just enough to make `verify.sh` meaningful — plain `test_*` functions, `assert`,
`pytest.raises`, one `parametrize`, `tmp_path` for files:

```python
import pytest
from myapp.core import head

@pytest.mark.parametrize("xs, expected", [(["a", "b"], "a"), (["x"], "x")])
def test_head_returns_first(xs: list[str], expected: str) -> None:
    assert head(xs) == expected

def test_head_rejects_empty() -> None:
    with pytest.raises(ValueError):
        head([])

def test_writes_file(tmp_path) -> None:
    (tmp_path / "f.txt").write_text("hi")
    assert (tmp_path / "f.txt").read_text() == "hi"
```

Deep fixtures, mocking, coverage gates, and property-based testing belong to `testing-py` —
stop at the baseline and hand off.

## Security (embedded, Python-specific)

Generic threat modeling and authz live in [`secure-coding`](../secure-coding/SKILL.md); these Python-specific controls stay here:

```python
# Bad                                          # Good
eval(user_input)                                ast.literal_eval(user_input)   # never eval/exec input
pickle.loads(network_bytes)                     json.loads(network_bytes)      # never unpickle untrusted data
subprocess.run(cmd, shell=True)                 subprocess.run(["ls", path])   # list args, no shell=True
random.random()  # tokens                       secrets.token_urlsafe(32)      # secrets, not random, for secrets
```

Keep deps locked (`uv.lock`) and audited (`pip-audit` / `uv` resolution); read secrets from
env or a secret manager, never hardcode or log them.

## Anti-patterns -> STOP

| Tempting move | Reality / do instead |
| --- | --- |
| "`def f(xs=[])` is fine, it's empty" | One list shared across all calls; use `= None` then `xs = xs or []`. |
| "bare `except:` to be safe" | Swallows `KeyboardInterrupt`/bugs; catch a specific type. |
| "`from module import *`" | Pollutes the namespace, breaks tooling; import names explicitly. |
| "`print()` to debug this library" | Writes to a stdout you don't own; use `logging.getLogger(__name__)`. |
| "`os.path.join` is what I know" | `pathlib.Path` is typed and composable; use `/`. |
| "I'll add types later" | Untyped public API defeats `--strict`; type the boundary now. |
| "edit `requirements.txt` by hand" | Drifts from the lock; `uv add` / `uv remove` and commit `uv.lock`. |
| "`asyncio.gather` is simpler" | Leaks siblings on failure; `TaskGroup` for structured concurrency. |
| "swallow `CancelledError`, it's noise" | Breaks cancellation for the whole tree; clean up and re-raise. |
| "`time.sleep` inside this coroutine" | Blocks the event loop; `await asyncio.sleep(...)`. |
| "`pickle.loads` the cache, it's ours" | Any untrusted byte = code execution; use `json`. |
| "explicit `TypeVar` everywhere" | New code uses PEP 695 `def f[T]` / `class C[T]` / `type X`. |

## Project grounding (02-DOCS)

In a project that has the [`harness`](../harness/SKILL.md) wiki, record this project's Python
conventions in `02-DOCS/wiki/stack/python.md` and index it in `02-DOCS/wiki/index.md` — the
interpreter floor, `src/` layout, uv workflow, ruff/mypy config, async-vs-sync stance, and
data-modeling defaults. Read it first on every use and bump its `Updated` date when a convention
changes. This is *recorded, not gated*: never block the task on it, and skip silently when there
is no `02-DOCS/` layer.
Files in this package (7)
  • SKILL.md 15 KB
  • evals/README.md 1 KB
  • evals/cases.yaml 3 KB
  • references/async.md 5 KB
  • references/stdlib.md 5 KB
  • references/typing.md 6 KB
  • scripts/verify.sh 4 KB

Versions

RepositoryScoreLicence
This page · ericrisco/rsc-harness100MIT
xobotyi/cc-foundry100MIT
nimadorostkar/Claude-Skills-collection100MIT
Aadarsh-x54/deep-agents-with-langchain100MIT
ahgraber/skills100CC0-1.0
appwrite/claude-plugin100BSD-3-Clause
claude-dev-suite/claude-dev-suite100MIT
G1Joshi/Agent-Skills100MIT
chrishuffman5/domain-expert100MIT

Database snapshot: 13.11.2026 · package version 48af84cf0add (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100python-standards
    Python coding standards for modern (3.11+) projects - strict typing, ruff/mypy config, uv packaging, pytest testing, async patterns, error handling, security, docstrings, Docker, and FastAPI/Pydantic/SQLAlchemy/Django/Flask conventions. Use this skill whenever the user writes,…
  • 100sota-python
    State-of-the-art Python engineering for both writing new Python and auditing existing Python code. Covers uv-based tooling and project setup, strict typing, idioms and pitfalls, asyncio structured concurrency, security (injection, deserialization, supply chain), performance, and…
  • 90modern-python
    Modern Python tooling and best practices using uv, ruff, ty, and pytest. Covers project setup with pyproject.toml (PEP 735), src layout, linting/formatting with ruff, type checking with ty, testing with pytest and coverage, pre-commit with prek, and security (pip-audit,…
  • 90python-services
    Python patterns for CLI tools, async concurrency, and backend services. Use when working with Python code, building CLI apps, FastAPI services, async with asyncio, background jobs, or configuring uv, ruff, ty, pytest, or pyproject.toml.
  • 100python-helper
    Current Python development guidance for versions, uv and pip, packaging, typing, asyncio, pytest, Ruff, security, and runtime boundaries. Use when writing or reviewing Python, pyproject.toml, Python CI, tests, dependency workflows, or Python upgrades.
  • 100python-project-setup
    Sets up Python projects with modern tooling including pyproject.toml, linting with ruff, formatting, type checking with mypy or pyright, testing with pytest, and pre-commit hooks. Triggers on: "setup Python project", "create Python package", "python project structure", "pyproject.toml".

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.