# Modern Python

> Version-gated modern Python guidance backed by Ruff. Use before writing, editing, fixing or refactoring any Python file: list the rules the project's target version allows, apply them, verify. /modern-python setup runs the per-project interview.

- Skill: `yogiraja/modern-python` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add yogiraja/modern-python`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yogiraja/modern-python/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: YogirajA (https://skillmd.com/u/yogiraja)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/yogiraja/modern-python

---


# Modern Python

Ruff is the rule database and the project's target Python version is the gate. The bundled tool
lists only the modernization rules whose own Ruff examples diagnose under that target, so nobody
curates a rule table and the guidance tracks Ruff. It corrects two things at once: training-data
lag (a feature newer than the model) and frequency bias (the older pattern the model reaches for
out of habit).

Tool: `python <skill-dir>/scripts/modern_python.py`, where `<skill-dir>` is this folder.
Subcommands: `list`, `explain`, `check`, `fix`, `probe`.

## Step 0: the project's answers

Read `.claude/modern-python.md` at the repo root. It holds the interview answers: `codebase`,
`convention`, `enforcement`, `target`, `target-source`, `ruff-fallback`, `profile`.

- Present: continue with its values.
- Absent: run the interview in [`references/interview.md`](references/interview.md) once, write
  the file, continue. If the user declines the interview, proceed with `enforcement: advisory`,
  `convention: preserve`, no target override, say so once, and do not ask again this session.
- The repo's `CLAUDE.md` carries `modern-python: off`: proceed as declined without asking. That
  is the line for a repo whose `.claude/` is not yours to write (onboard-light territory).
- `/modern-python setup` re-runs the interview and rewrites the file.

## Before editing Python

1. List the guidance that applies to the file you are about to touch:

   ```sh
   python <skill-dir>/scripts/modern_python.py list --file path/to/file.py --target-version <target> --profile <profile>
   ```

   Omit `--target-version` when `target-source: declared`. For a file that does not exist yet,
   pass `--target-version` from the answers file.

2. Exit 3 means the project's Ruff is too old to report rule metadata. Ask whether to update Ruff
   through the project's own manager (uv, Poetry, PDM, pre-commit). On yes, update and rerun. On
   no, rerun with `--allow-stale` and rely on the post-edit check.

3. Read the complete output. Do not pipe it through head, tail, grep, sed, or any other truncating
   or filtering command: a rule dropped there is a rule you will violate. Baseline rules diagnose
   under the target now; conditional ones need `from __future__ import annotations`.

4. For every listed rule that may touch the code you plan to write, read its explanation before
   editing:

   ```sh
   python <skill-dir>/scripts/modern_python.py explain --file path/to/file.py UP045 FURB123
   ```

   Skipping a relevant rule costs the same call: explain it first, then skip with the reason
   stated.

5. Apply the convention the answers file sets:
   - `convention: override`: follow a returned rule even when nearby code or repository
     convention uses the older pattern. Four exits only: it would not run on the target, it
     would change behaviour, it clearly does not match the edited code, or a fix-scoped skill is
     active (surgical-patch, a cavecrew builder), in which case rules apply only to the lines the
     fix already touches.
   - `convention: preserve`: inside existing files, match the surrounding pattern. Use the modern
     idiom in new files and new functions, and everywhere when the task is a modernization pass.

6. Write the code. The target version is the compatibility boundary: no syntax or stdlib API newer
   than it. When the tool reports `Ruff default (no project configuration found)` and the answers
   file carries no target, the target is unknown: avoid version-gated syntax and say so.

## After editing Python

`enforcement: advisory`: done after the edit. Report which rules you applied.

`enforcement: verified` or `enforced`:

1. Check the files you touched (exit 1 means findings remain). Add `--target-version <target>`
   when the answers file says `target-source: chosen`, since Ruff otherwise checks at its own
   default; `--concise` prints one line per finding instead of JSON:

   ```sh
   python <skill-dir>/scripts/modern_python.py check --concise path/to/file.py
   ```

2. `explain` each finding you do not already understand. Fix by hand, or apply Ruff's safe fixes
   with the same flags:

   ```sh
   python <skill-dir>/scripts/modern_python.py fix --concise path/to/file.py
   ```

3. `check` again until it exits 0, or name the remaining findings and the documented caveat that
   applies to each.

4. Run the project's own formatter, type checker and tests for the changed code. This check
   supplements them.

`enforced` additionally means a PostToolUse hook runs the same check on every edited `.py` file
and returns findings as context. That hook is installed only by onboard-repo Phase 4b, with its
`HARNESS.md` entry and a tested fire case; this skill never writes `settings.json`. Asked for
enforcement outside onboarding: say to run onboard-repo and re-run Phase 4b alone.

## Profiles

`core` is `UP, FURB, F401`. `modern` (default) adds `SIM, C4, PIE, PTH, FLY, PERF`. Preview
rules stay off unless the project opts in.

## Done when

The listed rules were read in full, every relevant one was applied or explained away, the edit
respects the target, and under verified or enforced the check exits 0 or the remaining findings
are named with their caveat.

