Skip to content
Back to skills

Python Aware

ASecurity

This skill should be used when writing, testing, or reviewing Python code — in any repository. It is the Python language layer: toolchain habits, coding rules, and pytest/mock conventions that hold regardless of the repo. Repo-aware skills such as airflow-aware and commitizen-aware compose it; strict-review reads it when a diff contains Python.

  • 14 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 11, 2026
testingpythongoexpresstestinggitapi

Works with

  • api

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned November 11, 2026

npx -y skills add Lee-W/maigo --skill python-aware --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Aware?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Python Aware
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lee-w-python-aware/badge)](https://www.skillsdirectory.com/skills/lee-w-python-aware)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-aware
description: This skill should be used when writing, testing, or reviewing Python code — in any repository. It is the Python language layer: toolchain habits, coding rules, and pytest/mock conventions that hold regardless of the repo. Repo-aware skills such as airflow-aware and commitizen-aware compose it; strict-review reads it when a diff contains Python.
---

<!-- mkdocs-include-start -->

# Python-Aware

**Loaded by**: composed by repo-aware skills (`airflow-aware`, `commitizen-aware`); read by `strict-review` when a diff contains Python; may also trigger from its description during any Python work.
**Applies to**: Writing, testing, or reviewing Python code in any repository.

## When to apply

This is a **knowledge layer**, not a review checklist. It holds Python language and
toolchain conventions that stay true no matter which repo you are in. Anything that only
holds for one repository (its class names, directory layout, CI matrix, custom tooling)
belongs in that repo's `<repo>-aware` skill, not here.

Once loaded, treat every convention as background — do not re-read each task.

## Layering & precedence

1. The repo's own `AGENTS.md` / `CLAUDE.md` (and contributor docs) — always wins.
2. The repo-aware skill (`airflow-aware`, `commitizen-aware`, …).
3. This skill — the default when nothing above says otherwise.

A repo-aware skill that wants to change a default here must say so explicitly
(an "override: …" line); silence means the default below applies.

## Toolchain

- **Use the repo-managed environment** (`uv run …`, or whatever the repo specifies) — never the
  host's globally installed `pytest`, `mypy`, or `ruff`; they run a different version than CI.
- **Hook runner**: if the repo uses `prek`, run `prek`, not `pre-commit`. Hook-runner behavior
  that produces false greens (stash, `git add -N`, `rebase --continue`, wrong `files:` regex) is
  language-neutral — see
  [`hook-runner-verification.md`](https://github.com/Lee-W/maigo/blob/main/skills/git-workflow/references/hook-runner-verification.md).
- **Ruff**: `ruff format` then `ruff check --fix`, with the repo's own configuration
  (line length, rule selection) — do not impose defaults.
- **`uv.lock` diff you did not cause**: if it returns after `git checkout HEAD -- uv.lock` plus
  the next `uv sync`, suspect lockfile drift already on `main` (someone edited a
  `pyproject.toml` without re-running `uv lock`), not your environment. Keep the re-lock out
  of a feature PR.
- **pytest config**: prefer `[tool.pytest]` (pytest 9.0+ reads it natively) over
  `[tool.pytest.ini_options]`; this needs `requires-python >= 3.10`.
- **Fixing a vulnerability with `uv lock --upgrade`**: the lock forks per Python version, so check every
  version of the package in `uv.lock`, not the first — `references/tooling.md`.

## References index

| Reference | Read when |
|---|---|
| `references/code-style.md` | Writing or reviewing Python style: import placement and aliasing, naming and constants, raise-vs-document validation, `ContextVar` in async generators, mypy narrowing, docstring conventions, subprocess `returncode` checks |
| `references/test-conventions.md` | Writing or reviewing tests and one of the named patterns below applies (mock assertions, parametrize, reject-path shapes, tripwires, class-attribute / stub-shape / codespell traps, unix-socket path length…) — full rationale and recipes |
| `references/runtime-gotchas.md` | Code that passes a type checker and a casual read but misbehaves at runtime: pydantic `Annotated` constraints, nested enums, subscripted generics, `Decimal` → `int`, chained `.append()` on an ORM query result (weak identity map) |
| `references/tooling.md` | Dependency-floor bumps that make a stale venv go red, several agents sharing one worktree `.venv`, a script a hook runner executes directly, measuring coverage with `--cov`, or a `uv lock --upgrade` vulnerability fix that leaves an old-Python fork on the bad version |

## Coding rules

- **No `assert` in production code** — raise a real exception (`ValueError`, `RuntimeError`, …);
  `assert` disappears under `python -O`.
- **`time.monotonic()` for elapsed time** — not `time.time()`, which can jump.
- **Imports go at the top of the file** by default, including in `__init__` and any function
  that runs on every public touch of a class. Use the function-body form for exactly four cases:
  (1) a `TYPE_CHECKING` block, (2) breaking a known circular-import cycle, (3) a multi-process
  worker-isolation path, (4) a deferred-execution callback (a body not called at module import
  time). Say which case it is in a comment.
  Self-check: "does this function run on every public touch of the class?" If yes, top-level.
  Never re-import, inside a function, a name the module already imports at top level; a test
  file is not an exception (`references/code-style.md` → Imports).
- **Name threshold constants for the bound they encode**, not the use site, cite the value's source in a comment, and never reuse one constant as two thresholds — `references/code-style.md`.

## Testing

- **pytest only** — do not subclass `unittest.TestCase`.
- **Every mock carries `spec=` or `autospec=True`** — a bare `MagicMock()` is allowed only for a
  logger attribute (`instance._log = mock.MagicMock()`); exact rule in `references/test-conventions.md`.
- **Time-dependent tests use the repo's existing time-travel library** (`time_machine`,
  `freezegun`, …) — do not monkey-patch `datetime.now` by hand.
- **Mock assertions: prefer `.mock_calls` equality** — `m.method.mock_calls == [mock.call(...)]`
  expresses "called exactly once, with these arguments, and nothing else" in one assertion, and
  pytest diffs it directly. Use the child-mock form (`m.method.mock_calls`), not the parent's.
  A repo that standardizes on `assert_called_once_with` can say so as an explicit override.
- **Parametrize by default** — stacked asserts or near-duplicate test methods are must-fix in review;
  split into `@pytest.mark.parametrize` with `pytest.param(..., id=...)` naming each case, and extract
  repeated setup/cleanup into a fixture.
- **Full-sequence assertions** — assert the whole iterable/list, not head + tail + length.
- **Cap-boundary pairs** — a numeric cap needs both an at-cap (allowed) and a one-over-cap (trips) test.
- **No thin test helpers** — assert only the field the function under test decides; skip one-use wrapper helpers.
- **Minimize forced test churn** — keep an existing test's diff to the forced minimum; add a new test for new behavior.
- **Build the fixture, don't defer** — a missing example fixture is not "can't test"; build the smallest purpose-built one.
- **Drift guard green in its own commit** — the commit that adds an "every X needs an entry" test
  must add the existing entries and doc rows too, or it fails when checked out alone.
- **Private SDK path: fail, don't skip** — a test that depends on a third-party SDK's private
  module `pytest.fail`s when the path is missing; a skip hides the gap behind a green CI.
- **Tripwire tests: prove which assertion fails, name the component** — mutate one deny-list
  entry and name the assertion that turns red; a test name must not credit a rejection that only
  a downstream function performs.
- **Log assertion when the log IS the observable** — a repo rule against `caplog` targets
  scraping raw log text, not asserting an operator-visible log call via a mocked logger.
- **A reviewer's concrete repro becomes a named parametrize case** even if it already passes (check it goes red on the base, else say which mutation it guards), and **a test that needs `# type: ignore[arg-type]` to feed a documented value means the signature and docs disagree** — fix annotation, docstring and docs together; both in `references/test-conventions.md`.

Worked examples and the rest of the named patterns (parametrize + shared fixtures,
reject-path shapes, prose-guard windows) are in `references/test-conventions.md`.

## Composing with other skills

- **`strict-review`**: when the diff contains Python, the Testing and Coding rules sections above
  strengthen base review items 3–6. Raise a violation as a finding with the rule named; the recipe
  detail lives in `references/test-conventions.md`.
- **Repo-aware skills** (`airflow-aware`, `commitizen-aware`): they declare "read python-aware first"
  and list only what differs. If a repo-aware rule and a rule here conflict, the repo-aware rule
  wins when it is written as an explicit override; the repo's `AGENTS.md` beats both.
- Layering guidance for adding another language layer:
  [`docs/reference/skills.md`](https://github.com/Lee-W/maigo/blob/main/docs/reference/skills.md).

Files in this skill

  • SKILL.md8.6 KB
  • references/code-style.md19.4 KB
  • references/runtime-gotchas.md7.1 KB
  • references/test-conventions.md46.6 KB
  • references/tooling.md5.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in testing →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.