feat(skills): add coding-dev.md and per-language rules under code-languages/
Generic coding rules and preferences distilled from existing agent rule files, project AGENTS.md/.rules, and saved memories: - strictness maximal; suppressions must be reported to the user - never filter build/lint/test output through grep/tail/head - reuse > fork > self-implement; generic code becomes its own package - 1000-line file / 100-line function limits, split by concern - regression test every fixed bug; never test against real user data - events over polling; scope discipline; honest reporting Per-language: rust, js-typescript, bash, python, go, csharp-dotnet, c-cpp. Each extends coding-dev.md rather than repeating it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba95f171b2
commit
57968904bf
9 files changed
+1499
-1
No files matched your search
@@ -0,0 +1,153 @@
|
||||
---
|
||||
name: python
|
||||
description: >-
|
||||
Use this skill when writing, linting, type-checking, testing, or packaging Python —
|
||||
including async code, CLI tools, bot cogs, and library packages. Covers strict typing with
|
||||
mypy, ruff configuration, virtualenv discipline, async patterns, and packaging.
|
||||
---
|
||||
|
||||
# Python — Rules & Preferences
|
||||
|
||||
Extends [`../coding-dev.md`](../coding-dev.md). Everything there applies; this adds Python specifics.
|
||||
|
||||
---
|
||||
|
||||
## 1. The Gate
|
||||
|
||||
```bash
|
||||
ruff format --check .
|
||||
ruff check .
|
||||
mypy --strict .
|
||||
pytest
|
||||
```
|
||||
|
||||
All four clean before anything is called done. Configure them in `pyproject.toml` so the
|
||||
editor, the CLI, and CI all read the same settings.
|
||||
|
||||
---
|
||||
|
||||
## 2. Typing — Strict
|
||||
|
||||
**Every function gets annotated**, including `-> None`. Type hints are not optional
|
||||
documentation; they are the checker's input.
|
||||
|
||||
```toml
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
warn_unreachable = true
|
||||
warn_return_any = true
|
||||
disallow_untyped_defs = true
|
||||
```
|
||||
|
||||
- **Never bare `Any`.** Use a `Protocol`, a `TypeVar`, or `object` + narrowing.
|
||||
- `X | None` over `Optional[X]`; `list[str]` over `List[str]` (modern syntax, no `typing` import).
|
||||
- `TypedDict` or a dataclass for structured dicts — not `dict[str, Any]`.
|
||||
- `Literal` and `Enum` for closed sets of values, not bare strings.
|
||||
- `@dataclass(frozen=True, slots=True)` for value objects.
|
||||
- Third-party library missing stubs: install `types-*` if it exists, otherwise a narrowly
|
||||
scoped `# type: ignore[import-untyped]` with a comment — never a blanket module-level ignore.
|
||||
|
||||
---
|
||||
|
||||
## 3. Linting
|
||||
|
||||
`ruff` is the linter and formatter (it replaces flake8, isort, black, pyupgrade, and most
|
||||
plugins). Enable a broad rule set rather than the default minimum:
|
||||
|
||||
```toml
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "SIM", "RUF", "ASYNC", "S", "PTH"]
|
||||
```
|
||||
|
||||
`S` (bandit security rules) and `ASYNC` are worth the noise. As always: fix, don't suppress —
|
||||
and when you must suppress, use a specific code (`# noqa: S603`) with a reason.
|
||||
|
||||
---
|
||||
|
||||
## 4. Environments
|
||||
|
||||
- **Always a virtualenv or project-managed environment** (`uv`, `poetry`, `pdm`, `venv`).
|
||||
Never install into the system interpreter, and never `pip install --user` as a workaround
|
||||
for an immutable OS — use a container or a venv.
|
||||
- `uv` is the preferred installer/resolver where free choice exists; it is dramatically faster
|
||||
and lockfile-native.
|
||||
- Commit the lockfile. Pin the Python version in `pyproject.toml` (`requires-python`).
|
||||
- `pathlib.Path` over `os.path` everywhere (ruff's `PTH` rules enforce this).
|
||||
|
||||
---
|
||||
|
||||
## 5. Errors & Logging
|
||||
|
||||
- **Specific exceptions only.** `except Exception:` needs a reason; bare `except:` is never
|
||||
acceptable (it catches `KeyboardInterrupt` and `SystemExit`).
|
||||
- **Never swallow silently.** If you catch and continue, log at `warning` or above.
|
||||
- `raise ... from err` to preserve the chain.
|
||||
- Custom exception types deriving from a single package-level base, so callers can catch the
|
||||
whole family.
|
||||
- **`logging` module, never `print`,** in anything that isn't a CLI writing to stdout as its
|
||||
actual output. One `logger = logging.getLogger(__name__)` per module, at module scope.
|
||||
- **Never log secrets** — tokens, auth headers, message content, personal data.
|
||||
|
||||
---
|
||||
|
||||
## 6. Async
|
||||
|
||||
- `asyncio` with `async`/`await`. **Never block the event loop** — no `time.sleep`, no
|
||||
synchronous `requests`, no blocking file I/O in a coroutine. Use `asyncio.to_thread` for
|
||||
genuinely blocking calls.
|
||||
- `httpx` (or the framework's own client) over `requests` in async code.
|
||||
- **`asyncio.TaskGroup`** (3.11+) over bare `create_task` — it propagates exceptions and
|
||||
cancels siblings instead of losing them to an unawaited task.
|
||||
- **Every `create_task` result must be held in a reference**, or the garbage collector can
|
||||
cancel it mid-flight.
|
||||
- Cancellation is cooperative: clean up in `finally`, and never swallow `CancelledError`.
|
||||
- **Events over polling.** Subscribe to the library's event/callback API; do not loop on a
|
||||
timer. Where polling is genuinely unavoidable (no event source exists), use a **staggered
|
||||
backoff** rather than a fixed interval, and say why polling was necessary.
|
||||
|
||||
---
|
||||
|
||||
## 7. Rate Limiting & External Services
|
||||
|
||||
When talking to a rate-limited API (a chat platform, a store, a tracker):
|
||||
|
||||
- **Funnel outbound calls through a single queue/worker** rather than calling from arbitrary
|
||||
call sites. It is the only place a limit can actually be enforced.
|
||||
- **Backoff with jitter** on retry; never a tight retry loop.
|
||||
- Respect `Retry-After` and documented limits; assume undocumented ones exist.
|
||||
- **Be extremely careful scraping real consumer services.** Conservative delays, realistic
|
||||
headers, plenty of failsafes, and abort fast when a response looks unexpected. Assume you
|
||||
are one mistake away from a ban.
|
||||
|
||||
---
|
||||
|
||||
## 8. Structure
|
||||
|
||||
- Package layout: `src/<package>/`, tests in `tests/`.
|
||||
- **Shared behavior goes in a real shared module**, imported by each consumer — not
|
||||
copy-pasted between sibling packages. When three components grow the same queue/retry/
|
||||
formatting logic, that is the signal to extract it.
|
||||
- Reusable, generic functionality (a format parser, a protocol client) becomes **its own
|
||||
installable package** with its own repo and version, consumed as a dependency.
|
||||
- Keep persistent state in the framework's own config/storage system so it survives restarts;
|
||||
do not invent a parallel JSON file beside it.
|
||||
- File and function size limits from the general rules apply.
|
||||
|
||||
---
|
||||
|
||||
## 9. Testing
|
||||
|
||||
- `pytest`, with `pytest-asyncio` for coroutines.
|
||||
- Fixtures over setup/teardown methods; `tmp_path` for anything touching the filesystem.
|
||||
- **Regression test every fixed bug.**
|
||||
- Mock at the network boundary (`respx`, `responses`), not by monkeypatching your own
|
||||
internals — a test that patches the code under test proves nothing.
|
||||
- **Never exercise destructive operations against real accounts or real data.** Temporary
|
||||
fixtures only.
|
||||
- `pytest -p no:randomly` only when order genuinely matters, and note why.
|
||||
|
||||
---
|
||||
|
||||
## 10. Related
|
||||
- [`../coding-dev.md`](../coding-dev.md) — the general rules this extends.
|
||||
- [`bash.md`](./bash.md) — for the surrounding build and deploy scripts.
|
||||
Reference in new issue
Block a user