python-style
Skill0 starsChanged 8 months agoApache-2.0View on GitHub
Python code style preferences — architecture, types, tooling, and conventions for Python projects
6 other projects use a version of this.
Adopted by
Most recently taken up by
- open-edge-platform/anomalibtheir own version, first seen Oct 8, 2026
- mlflow/mlflowtheir own version, first seen Oct 8, 2026
- atsushi-green/ds-ai-coding-skillstheir own version, first seen Sep 25, 2026
- arusli1/askillstheir own version, first seen Sep 2, 2026
- search-atlas-group/amm-founding-circletheir own version, first seen Jun 22, 2026
- faalbers/Claude_Skillstheir own version, first seen Jun 3, 2026
Other versions6 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 6
- mlflow/mlflow★ 29ksame name
- open-edge-platform/anomalib★ 6.4ksame name
- atsushi-green/ds-ai-coding-skills★ 86same name
- arusli1/askills★ 1same name
- faalbers/Claude_Skills★ 0same name
- search-atlas-group/amm-founding-circle★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository4 other files it gives its agents.
- design-docsSkill
- github-issuesSkill
- tddSkill
- test-desiderataSkill
The file
We split this skill into the rules it gives an agent
9 rules. 0 are common, 6 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 11
Use | None union syntax (e.g., str | None) over Optional[str].
- Very specific: line 40
Use .get("key") without a default — let None propagate naturally.
- Distinctive: line 53
Use dataclass for structured data instead of raw dicts when the abstraction is justified.
- Distinctive: line 54
Never add imports inside functions — always at the top of the file.
- Distinctive: line 68
Use custom exception classes for domain errors.
- Distinctive: line 218
Don't use mocks unless necessary — prefer real objects.
- Distinctive: line 280
Test naming: Drop redundant prefixes — context comes from nested classes.
- Very specific: line 292
Use @staticmethod on test methods that don't need self.
- Distinctive: line 315
Write multiple focused test functions rather than one large monolithic test.
“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
★ Starred rules are highest priority — always follow these.
★ Type annotations
Use
| Noneunion syntax (e.g.,str | None) overOptional[str]. Why: PEP 604 native syntax, no typing import needed. Assume Python 3.12+.# Bad from typing import Optional def greet(name: Optional[str]) -> Optional[str]: ... # Good def greet(name: str | None) -> str | None: ...Make fields explicitly nullable rather than providing empty values like
=""or=0. Use| Nonefor optional params; meaningful defaults (e.g.,timeout=30) are fine.# Bad def create_user(name: str = "", age: int = 0): ... class Config: host: str = "" # Good def create_user(name: str | None = None): ... class Config: host: str | None = None # Also good: meaningful defaults def fetch(timeout: int = 30): ...
Code hygiene
Use
.get("key")without a default — letNonepropagate naturally. Why: Avoids masking bugs; empty string or 0 can be valid data. Use default only when it's meaningful andNoneis a valid dict value.# Bad count = data.get("count", 0) # masks that count could be missing # Good count = data.get("count") # let None propagate if missing # Good: meaningful default when None is valid enabled = config.get("enabled", True)Use dataclass for structured data instead of raw dicts when the abstraction is justified.
Never add imports inside functions — always at the top of the file. Sort: stdlib → third-party → local. Prefer relative imports within the same package.
# Bad def process(): from pathlib import Path ... # Good from pathlib import Path def process(): ...Use custom exception classes for domain errors. Prefer built-in exceptions (ValueError, KeyError, etc.) when they match. Use custom exceptions when no built-in fits.
# Bad raise Exception("failed to fetch") raise ValueError("invalid token") # when it's actually a domain error # Good if not token: raise ValueError("token is required") class GitHubError(Exception): pass class GitHubRateLimitError(GitHubError): def __init__(self, retry_after: int): self.retry_after = retry_after super().__init__(f"Rate limited, retry after {retry_after}s")
Code conventions
Paths: Use descriptive constants instead of hardcoded strings. Define at module level.
# Bad with open("/home/alex/data/output.json") as f: ... # Good DATA_DIR = Path(__file__).parent / "data" OUTPUT_FILE = DATA_DIR / "output.json" with OUTPUT_FILE.open() as f: ...Paths: Prefer methods like
with_stem,with_suffix,with_nameover string concatenation. Simple f-strings likef"{name}.json"are fine.# Bad filename = data["name"] + ".json" path = "/data/" + filename # Good filename = f"{data['name']}.json" # simple case: OK output = input_path.with_suffix(".json") output = input_path.with_name("output.json")Duplication: Extract repeated logic into helper functions. Apply Rule of Three: extract after third occurrence. Don't extract trivial or unlikely-to-change duplication.
# Bad def process_user(): data = fetch() validated = validate(data) save(validated) def process_order(): data = fetch() validated = validate(data) save(validated) # Good: extract to helper def process(): data = fetch() validated = validate(data) save(validated) def process_user(): process() def process_order(): process() # Also good: don't extract trivial cases if user.is_active and user.has_permission: ...Enums/Literal: Use
typing.Literalinstead of magic constants or enums. Works for both user-facing choices and internal state machines.# Bad status = "done" if status == "done": ... # Good Status = Literal["pending", "done", "failed"] def process(status: Status) -> None: if status == "done": ...
Architecture
Data classes: Use
dataclassfor simple DTOs with no validation. Usedataclass+msgspecfor fast serialization. Usepydanticwhen you need validation + OpenAPI schema. UseTypedDictwhen integrating with dict-returning code outside your control.# dataclass: simple DTO, no validation @dataclass class User: name: str email: str # msgspec for fast serialization: define with @dataclass, encode/decode with msgspec import msgspec @dataclass class Event: id: str timestamp: float payload: bytes def serialize(event: Event) -> bytes: return msgspec.encode(event) def deserialize(data: bytes) -> Event: return msgspec.decode(data, type=Event) # pydantic: validation + OpenAPI from pydantic import BaseModel class Config(BaseModel): host: str port: int # TypedDict: dict-returning external code from typing import TypedDict class ExternalAPIResponse(TypedDict): user_id: str data: dictModule structure: Prefer flat over deep. Split when a module exceeds ~300 lines or has 3+ distinct responsibilities.
# Bad (deep) src/utils/helpers/formatters/date.py # Good (flat) src/formatters.py # split by responsibility, not by typeSplit triggers: >300 lines or 3+ distinct responsibilities.
__init__.py: Export only what consumers need. Never add placeholder functions. Use__all__to define public API. Avoid wildcard imports (from x import *).# Bad def stub(): pass from utils import * # unclear what's exposed # Good from .parser import parse __all__ = ["parse"]
★ Testing
Don't use mocks unless necessary — prefer real objects. Use fixtures, fakes, or dependency injection instead. Mocks are OK for external APIs, time-dependent code.
# Bad: over-mocking with mock.patch("requests.get") as mock_get: mock_get.return_value.json.return_value = {"id": 1} result = fetch_user() # Good: use fixtures @pytest.fixture def sample_user(): return {"id": 1, "name": "Test"} # Good: use Fake classes class FakeGitHubClient: def fetch(self, repo): return {"stars": 100} # Good: mocks OK for external services with mock.patch("httpx.Client.get") as mock_get: mock_get.return_value = Response(200, json={"id": 1}) result = api_call()Reuse common setups using fixtures and functions. Extract to fixture only when used in 2+ tests.
# Good: reuse setups with fixtures @pytest.fixture def sample_user(): return {"id": 1, "name": "Test", "email": "test@example.com"} @pytest.fixture def db_connection(): return create_test_db() def test_create_user(db_connection, sample_user): result = create_user(db_connection, sample_user) assert result["id"] == 1 def test_user_email(db_connection, sample_user): result = create_user(db_connection, sample_user) assert result["email"] == "test@example.com"Group tests in nested classes matching the code structure. Simple modules don't need deep nesting.
# Good: nested classes match code structure class TestGithubClient: class TestFetch: @staticmethod def test_writes_content_to_file(): ... @staticmethod def test_handles_404(): ... class TestCreate: @staticmethod def test_returns_new_record(): ... # Simple modules don't need deep nesting def test_parse_valid_json(): ... def test_parse_invalid_json_raises(): ...Test naming: Drop redundant prefixes — context comes from nested classes.
# Bad: redundant prefix class TestGithubClient: def test_github_client_fetch_writes_contents_to_file(self): ... # Good: prefix dropped class TestGithubClient: class TestFetch: def test_writes_to_file(): ...Use
@staticmethodon test methods that don't needself.class TestParser: @staticmethod def test_parses_valid_json(): # doesn't need self result = parse('{"key": "value"}') assert result == {"key": "value"}Compare parsed results against fixture files (json/csv/yaml/etc.) rather than many individual assertions. Simple tests don't need fixture files.
# Good: compare against fixture file def test_parse_config(): result = parse_config("fixtures/config.yaml") expected = load_fixture("fixtures/config_expected.json") assert result == expected # Simple tests don't need fixture files def test_empty_list(): result = parse([]) assert result is NoneWrite multiple focused test functions rather than one large monolithic test. Simple tests can be single functions.
# Bad: monolithic test def test_user_crud(): create_user() assert user exists update_user() assert updated delete_user() assert deleted # Good: focused tests def test_create_user(): ... def test_update_user(): ... def test_delete_user(): ... # Simple tests: one function is fine def test_parse_empty_returns_none(): ...
Tooling
- Package manager:
uv. Useuv add <package>for production deps oruv add --dev <package>for dev deps. Why: uv manages lockfiles and dependency resolution automatically. - Testing:
pytest. Run bymake testin the project directory. - Linting/formatting:
make lintin the project directory. Usemake lint-fixto reformat automatically. CI should runmake lintwithout fix. - Type checking:
make tyin the project directory.
Scope: These rules apply to new code. For existing code, follow "fix on touch" — improve as you work in a file but don't refactor unrelated code.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

