Split Python style skill into focused modules

This commit is contained in:
2026-03-16 14:18:05 +08:00
parent 0bb3ec31a4
commit 19b12dbd17
9 changed files with 260 additions and 115 deletions
-111
View File
@@ -1,111 +0,0 @@
---
name: python-style-preferences
description: Apply the user's preferred Python engineering style for new modules, refactors, reviews, and project scaffolding. Use when working on Python CLIs, async services, libraries, typed data pipelines, or tensor-heavy code that should standardize on uv, anyio, click, full type annotations, basedpyright, jaxtyping, and beartype.
---
# Python Style Preferences
## Overview
Follow these conventions whenever the task is primarily Python:
- Use `uv` for dependency management, virtualenv creation, locking, and command execution.
- Prefer `anyio` over raw `asyncio` APIs.
- Build CLIs with `click`.
- Keep type annotations comprehensive enough for `basedpyright` to be useful by default.
## Working Style
1. Start with typed domain shapes.
- Prefer `TypedDict` for reusable mapping-shaped payloads.
- Prefer `@dataclass(slots=True)` for reusable owned records and value objects.
- Avoid reusable bare `dict[str, object]` structures; keep ad hoc dicts local and short-lived.
2. Keep entrypoints thin.
- Put parsing and CLI wiring in `click` commands.
- Put business logic in typed functions and classes.
- For async CLIs, use a synchronous `click` entrypoint that calls `anyio.run(...)`.
```python
from pathlib import Path
import anyio
import click
@click.command()
@click.argument("config_path", type=click.Path(path_type=Path))
def main(config_path: Path) -> None:
anyio.run(run, config_path)
async def run(config_path: Path) -> None:
...
```
3. Use `anyio` primitives consistently.
- Prefer `anyio.create_task_group()`, `anyio.fail_after()`, streams, and cancellation scopes.
- Do not mix `asyncio.create_task`, `asyncio.gather`, or manual event-loop management into `anyio` code unless an external integration forces it.
- When an `asyncio` boundary is unavoidable, isolate it and leave a short note.
4. Treat typing as enforcement, not decoration.
- Annotate public functions, methods, return values, and reusable internal helpers.
- Run `basedpyright` as the default type-checking pass and keep new code clean.
- Allow `cast(...)` only at trusted boundaries where runtime guarantees exist but inference cannot express them. Leave a short reason comment immediately above it.
- Allow `# type: ignore[...]` only when it is narrow and justified. Leave a short reason comment immediately above it.
```python
# basedpyright cannot infer the validated plugin registry value type here.
runner = cast(Runner, registry[name])
# The vendor stub is wrong for this overload; runtime input is validated above.
value = vendor_api.load(path) # type: ignore[call-overload]
```
5. Use `jaxtyping` for arrays and tensors.
Short summary:
- Use `DType[ArrayType, "shape names"]`, for example `Float[np.ndarray, "batch channels"]`.
- Reuse axis names to express shared dimensions across arguments and returns.
- Use `...` for arbitrary extra axes and fixed integers for exact sizes.
- Prefer reusable aliases for common tensor shapes.
- Prefer concrete array types after normalization; use `ArrayLike` only at loose input boundaries.
See `references/jaxtyping-summary.md` for the fuller cheat sheet.
```python
import numpy as np
from beartype import beartype
from jaxtyping import Float, jaxtyped
Batch = Float[np.ndarray, "batch channels"]
@jaxtyped(typechecker=beartype)
def normalize(x: Batch) -> Batch:
...
```
6. Use runtime type checking where it pays for itself.
- Prefer `beartype` together with `@jaxtyped(typechecker=beartype)` on stable boundaries, data adapters, numerical helpers, and test-targeted functions.
- Avoid decorating the hottest inner loops unless the cost is clearly acceptable.
- Avoid `from __future__ import annotations` or stringized annotations when runtime checking must inspect annotations directly.
## Verification
Use this order unless the project already has a stricter local workflow:
1. `uv sync --dev`
2. `uv run basedpyright`
3. `uv run pytest`
4. `uv run python -m <package_or_module>` or a CLI smoke test when relevant
## Reference Files
- Read `references/jaxtyping-summary.md` when writing or reviewing array/tensor annotations.
-4
View File
@@ -1,4 +0,0 @@
interface:
display_name: "Python Style Preferences"
short_description: "Typed Python with uv, anyio, click"
default_prompt: "Apply the user's Python style preferences, typing rules, and validation workflow."
+69
View File
@@ -0,0 +1,69 @@
---
name: python-anyio-async
description: Use when working on Python async code in repositories that already use or are explicitly standardizing on anyio. Apply task-group, cancellation, timeout, and async-boundary patterns without migrating established asyncio, trio, or synchronous code unless requested.
---
# Python AnyIO Async
Use this skill for `anyio`-based async implementation work. It is not a general license to rewrite a repository's async model.
## Priority Order
1. Explicit user instructions
2. Existing repository async conventions and verification workflow
3. This skill
Only apply this skill when the repository already uses `anyio` or the user explicitly asks to standardize on it.
## Before Applying This Skill
Check the local project first:
- existing async imports and helpers
- current task runner and verification commands
- whether the code is `anyio`, `asyncio`, `trio`, or sync-only
- whether the CLI entrypoint is already established
If the repository is built around `asyncio`, `trio`, or synchronous code, keep that model unless the user asks for a migration.
## Defaults
- Prefer `anyio.create_task_group()` over manual task orchestration.
- Prefer `anyio.fail_after()` or cancellation scopes for time-bounded work.
- Use `anyio` streams and synchronization primitives instead of mixing in raw `asyncio` APIs.
- For async CLIs that already use `anyio`, keep a synchronous entrypoint that calls `anyio.run(...)`.
- When an `asyncio` boundary is unavoidable, isolate it and leave a short note.
```python
from pathlib import Path
import anyio
import click
@click.command()
@click.argument("config_path", type=click.Path(path_type=Path))
def main(config_path: Path) -> None:
anyio.run(run, config_path)
async def run(config_path: Path) -> None:
async with await anyio.open_file(config_path) as fh:
_ = await fh.read()
```
## Verification
Use the repository's existing verification workflow first.
If no local workflow exists and the repository is already aligned with this stack:
1. run the configured type checker
2. run the async test suite or `pytest`
3. run a CLI or module smoke test that exercises the async boundary when relevant
## Anti-Goals
- Do not replace an established `asyncio` or `trio` codebase with `anyio` unless asked.
- Do not mix manual event-loop management into `anyio` code without a forced integration boundary.
- Do not use this skill for synchronous code just because it contains a small async helper.
+4
View File
@@ -0,0 +1,4 @@
interface:
display_name: "Python AnyIO Async"
short_description: "Repo-aligned AnyIO patterns"
default_prompt: "Apply AnyIO task-group, cancellation, timeout, and async-boundary patterns while preserving the repository's existing async model. Only use this skill when the repo already uses AnyIO or the user explicitly asks to standardize on it."
+111
View File
@@ -0,0 +1,111 @@
---
name: python-project-defaults
description: Apply repo-aligned Python defaults for greenfield modules, scaffolding, CLIs, libraries, and typed implementation work in projects that already use or are explicitly standardizing on uv, click, and comprehensive typing. Do not use for general code review or to migrate existing projects away from their current tooling unless requested.
---
# Python Project Defaults
Use this skill to apply the user's preferred Python defaults without turning an implementation task into a tooling migration.
## Priority Order
1. Explicit user instructions
2. Existing repository conventions and verification workflow
3. This skill
These are defaults, not mandatory migrations.
Do not switch a repository to `uv`, `click`, or `basedpyright` unless:
- the repository already uses it
- the task is greenfield or scaffolding
- the user explicitly asks to standardize on it
Skip this skill for general code review unless the user explicitly wants style-standardization feedback.
## Before Applying This Skill
Check the local project first:
- packaging and environment: `pyproject.toml`, `requirements*`, lockfiles, existing env tooling
- task runner and verification flow: `Makefile`, `justfile`, `tox.ini`, `noxfile.py`, CI commands
- CLI framework: `click`, `typer`, `argparse`, or no CLI
- type checker: `basedpyright`, `pyright`, `mypy`, or no configured checker
- data modeling patterns: `TypedDict`, `dataclass`, `pydantic`, `attrs`, ORM models
If the repository already uses an alternative, keep it. Mirror the repo before introducing new tooling.
## Core Defaults
Use these defaults when the repository is already aligned with them or the task is greenfield.
1. Start with typed domain shapes.
- Prefer `TypedDict` for reusable mapping-shaped payloads when the repo does not already favor another model layer.
- Prefer `@dataclass(slots=True)` for reusable owned records and value objects when that matches local conventions.
- Avoid reusable bare `dict[str, object]` structures; keep ad hoc dicts local and short-lived.
2. Keep entrypoints thin.
- Put parsing and CLI wiring in the repo's existing CLI framework.
- Put business logic in typed functions and classes.
- If you are creating a new CLI with no established framework, default to `click`.
```python
from pathlib import Path
import click
@click.command()
@click.argument("config_path", type=click.Path(path_type=Path))
def main(config_path: Path) -> None:
run(config_path)
def run(config_path: Path) -> None:
...
```
3. Treat typing as enforcement, not decoration.
- Annotate public functions, methods, return values, and reusable internal helpers.
- Use the repository's configured type checker. If the repo has no established checker and the task is greenfield, default to `basedpyright`.
- Allow `cast(...)` only at trusted boundaries where runtime guarantees exist but inference cannot express them. Leave a short reason comment immediately above it.
- Allow `# type: ignore[...]` only when it is narrow and justified. Leave a short reason comment immediately above it.
```python
# basedpyright cannot infer the validated plugin registry value type here.
runner = cast(Runner, registry[name])
# The vendor stub is wrong for this overload; runtime input is validated above.
value = vendor_api.load(path) # type: ignore[call-overload]
```
## Verification
Use the repository's existing verification workflow first.
Examples:
- `make test`
- `just test`
- `tox -e py`
- `nox -s tests`
- `poetry run pytest`
- `hatch run test`
- the project's documented CI wrapper commands
If no local workflow exists and the task is greenfield or already aligned with this stack, use this fallback order:
1. `uv sync --dev`
2. `uv run basedpyright`
3. `uv run pytest`
4. `uv run python -m <package_or_module>` or a CLI smoke test when relevant
## Anti-Goals
- Do not change a repository's package manager or lockfile format unless asked.
- Do not replace an existing CLI framework with `click` unless asked.
- Do not let style preferences override correctness, bug fixing, or the user's explicit task.
- Do not treat this skill as a mandate to rewrite working code that already matches local conventions.
@@ -0,0 +1,4 @@
interface:
display_name: "Python Project Defaults"
short_description: "Repo-aligned Python defaults"
default_prompt: "Apply the user's Python defaults while preserving repository conventions. Only introduce uv, click, or basedpyright when the repo already uses them, the task is greenfield, or the user explicitly asks to standardize on them."
+68
View File
@@ -0,0 +1,68 @@
---
name: python-tensor-typing
description: Use when working on tensor-heavy or numerical Python code in repositories that already use or are explicitly standardizing on jaxtyping and beartype. Apply shape and dtype annotations plus boundary-focused runtime validation without introducing these tools to unrelated code unless requested.
---
# Python Tensor Typing
Use this skill for tensor-heavy or numerical code that benefits from explicit shape and dtype contracts. It should not leak into unrelated Python work.
## Priority Order
1. Explicit user instructions
2. Existing repository tensor and verification conventions
3. This skill
Only apply this skill when the task is numerical or tensor-heavy and the repository already uses `jaxtyping` and `beartype`, or the user explicitly asks for shape-typed numerics.
## Before Applying This Skill
Check the local project first:
- whether the task actually involves arrays, tensors, or numerical kernels
- which array types are already used: NumPy, PyTorch, JAX, TensorFlow, MLX
- whether `jaxtyping` and `beartype` are already present
- what verification commands already exist
If the repository does not already use this stack and the task is not explicitly about numerical typing, do not introduce it.
## Defaults
- Use `DType[ArrayType, "shape names"]`, for example `Float[np.ndarray, "batch channels"]`.
- Reuse axis names to express shared dimensions across arguments and returns.
- Prefer reusable aliases for common tensor shapes.
- Prefer concrete array types after normalization; use broader input types only at ingestion boundaries.
- Use `@jaxtyped(typechecker=beartype)` on stable boundaries and test-targeted helpers when the runtime cost is acceptable.
- Avoid applying runtime checking blindly to hot inner loops.
- Only avoid `from __future__ import annotations` in modules that rely on runtime annotation inspection.
```python
import numpy as np
from beartype import beartype
from jaxtyping import Float, jaxtyped
Batch = Float[np.ndarray, "batch channels"]
@jaxtyped(typechecker=beartype)
def normalize(x: Batch) -> Batch:
...
```
Read `references/jaxtyping-summary.md` when writing or reviewing array or tensor annotations.
## Verification
Use the repository's existing verification workflow first.
If no local workflow exists and the repository is already aligned with this stack:
1. run the configured type checker
2. run the numerical test suite or `pytest`
3. run a module smoke test that exercises the typed tensor boundary when relevant
## Anti-Goals
- Do not introduce `jaxtyping` or `beartype` into non-numerical work just because this skill is loaded.
- Do not annotate every local scratch tensor when the extra ceremony does not improve clarity.
- Do not add runtime checking to hot loops unless the cost is acceptable.
+4
View File
@@ -0,0 +1,4 @@
interface:
display_name: "Python Tensor Typing"
short_description: "Tensor typing with jaxtyping"
default_prompt: "Apply jaxtyping and beartype patterns for tensor-heavy Python code while preserving repository conventions. Only use this skill when the repo already uses these tools or the user explicitly asks to standardize on them."