Skip to content
cccskills
FreePublished on GitHub

python

Idiomatic programming style, patterns, and conventions for Python development. Trigger when: - Writing, refactoring, reviewing, or debugging Python code. - Files matching the pattern **/*.py (including requirements.txt, pyproject.toml, setup.py) are in the workspace or referenced. - Tasks involve: python, pytest, pip, uv, virtualenv, flake8, black, mypy. - Prompt contains keywords: python, py, pep8, typing, list comprehension, decorator, generator, asyncio, yield, poetry.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md19.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Python Language Idioms

Work with the language. Lean into duck typing, generators, context managers, and the standard library. Write code that reads like pseudocode — if it needs a comment to explain what it does, rewrite it.


Core Philosophy

  • Readability is a feature — code is read far more than it is written; optimize for the reader
  • Explicit over implicit — no magic, no hidden state, no clever tricks
  • Flat over nested — avoid deep indentation; early returns, guard clauses, comprehensions
  • Composition over inheritance — functions, protocols, and dataclasses beat class hierarchies
  • Standard library first — reach for builtins and stdlib before adding dependencies

Formatting

Let the formatter handle it. Pick one formatter project-wide and enforce it in CI.

ruff check .          # Lint
ruff format .         # Format
black .               # Alternative formatter

Non-Negotiables

RuleStandard
Indentation4 spaces. Tabs are prohibited.
Operator spacingx = y * 12 + 13, not x=y*12+13
Quote stylePick single or double project-wide; enforce via formatter. PEP 8 is deliberately silent.
Line lengthConfigure in formatter (88 for black/ruff, 79 for strict PEP 8). Don't manually wrap.
Importsisort-ordered: stdlib → third-party → local. One import per line for from imports.

Naming

Conventions

ScopeStyleExample
Modules, packagessnake_casecrypto_key.py, utils/
Functions, methodssnake_case (verbs)get_url(), calculate_checksum()
Variablessnake_case (nouns)raw_payload, user_registry
ClassesPascalCaseHttpClient, TokenParser
ConstantsSCREAMING_SNAKEMAX_RETRIES, DEFAULT_TIMEOUT
Private_leading_underscore_internal_cache, _validate()
Name-mangled__double_leading__secret (rarely needed)
Dunder__name__Reserved for the language. Never invent new ones.

The Verb-Noun Rule

Functions are actions → verbs. Variables are data → nouns. If a function name is a noun, it's probably a property.

Shadowing Built-ins (Prohibited)

Redefining these names breaks built-in functionality within scope and causes non-deterministic bugs:

id, type, len, range, list, dict, str, int, float, min, max, abs, set, map, filter, input, open, hash, format, next, iter, sum, any, all, dir, vars, help

Use descriptive alternatives: user_id, item_type, name_list, count.

Natural Phrasing

✅ Idiomatic❌ Avoid
if x not in yif not x in y
if x != yif not x == y
if x is not Noneif not x is None

Boolean and Comparison Idioms

Scenario❌ Wrong✅ IdiomaticWhy
Truthinessif x == True:if x:if evaluates truthiness directly
Falsinessif x == False:if not x:not is the logical inverter
None checkif x == None:if x is None:is checks identity; == can be overridden by __eq__
Empty checkif len(seq) == 0:if not seq:Empty collections are falsy
Type checktype(x) == intisinstance(x, int)Respects inheritance

Data Modeling

When to Use What

TypeUse WhenKey Trait
@dataclassMutable data with behaviorAuto-generates __init__, __repr__, __eq__
@dataclass(frozen=True)Immutable value objectsHashable, safe as dict keys
NamedTupleLightweight immutable recordsTuple-compatible, unpacking works
TypedDictTyped dict shapes (JSON, APIs)Runtime is still a plain dict
Plain dictDynamic/unknown keysNo structure guarantees
from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class Token:
    kind: str
    value: str
    line: int = 0

__slots__

Use slots=True on dataclasses (3.10+) or define __slots__ manually. It prevents dynamic attribute creation, reduces memory, and speeds up attribute access:

# ✅ With slots — fixed attributes, lower memory
@dataclass(slots=True)
class Point:
    x: float
    y: float

# ❌ Without slots — __dict__ per instance, allows typos like p.z = 3

Functions

Arguments and Returns

# ✅ Type-annotated at boundaries
def parse_config(raw: str, *, strict: bool = False) -> Config:
    ...

# ✅ Keyword-only after * — prevents positional misuse
def connect(host: str, port: int, *, timeout: float = 30.0) -> Connection:
    ...
  • Use * to force keyword-only arguments when order could be confused
  • Use / (3.8+) for positional-only when the parameter name is an implementation detail
  • Return None explicitly when a function can return None — don't rely on implicit fallthrough

Default Mutable Arguments (Critical)

# ❌ DANGEROUS — shared across all calls
def append_to(item, target=[]):
    target.append(item)
    return target

# ✅ Sentinel pattern
def append_to(item, target: list | None = None):
    if target is None:
        target = []
    target.append(item)
    return target

Properties

Use @property for computed attributes. Use @cached_property (3.8+) when the result is expensive and stable:

class Circle:
    def __init__(self, radius: float):
        self.radius = radius

    @property
    def area(self) -> float:
        return math.pi * self.radius ** 2

Iterators and Comprehensions

Comprehensions

Prefer comprehensions for simple transforms. Use explicit loops when the logic needs if/else branching or side effects:

# ✅ Clear — single transform + filter
names = [u.name for u in users if u.is_active]

# ✅ Dict comprehension
lookup = {u.id: u for u in users}

# ✅ Set comprehension
unique_tags = {tag for post in posts for tag in post.tags}

# ❌ Nested comprehension that requires mental parsing
result = [f(x) for x in [g(y) for y in items if h(y)] if p(x)]
# → Use intermediate variables or a generator pipeline instead

Generators

Use generators for lazy evaluation over large or infinite sequences. They consume O(1) memory:

# ✅ Generator expression — lazy, memory-efficient
total = sum(order.amount for order in orders)

# ✅ Generator function — for complex logic
def read_chunks(path: str, size: int = 8192):
    with open(path, 'rb') as f:
        while chunk := f.read(size):
            yield chunk

itertools

Reach for itertools before rolling your own iteration logic:

FunctionPurpose
chainConcatenate iterables
isliceSlice without materializing
groupbyGroup sorted items by key
productCartesian product
starmapmap() with argument unpacking
batched (3.12+)Fixed-size chunks

Context Managers

Use with for any resource that needs cleanup — files, locks, connections, transactions:

# ✅ Automatic cleanup
with open('data.json') as f:
    data = json.load(f)

# ✅ Multiple resources
with open('in.txt') as src, open('out.txt', 'w') as dst:
    dst.write(src.read())

Writing Context Managers

For simple cases, use contextlib.contextmanager:

from contextlib import contextmanager

@contextmanager
def temporary_directory():
    path = tempfile.mkdtemp()
    try:
        yield path
    finally:
        shutil.rmtree(path)

For classes, implement __enter__ and __exit__. For async resources, use async with and __aenter__ / __aexit__.


Async Patterns

async/await

Prefer async/await for I/O-bound concurrency:

import asyncio

async def fetch_user(session: aiohttp.ClientSession, uid: str) -> User:
    async with session.get(f'/api/users/{uid}') as resp:
        resp.raise_for_status()
        return User(**(await resp.json()))

Concurrent Operations

PatternBehaviorUse When
asyncio.gather(*coros)Runs concurrently, fails fastAll must succeed
asyncio.TaskGroup (3.11+)Structured concurrency, auto-cancelPrefer over gather
asyncio.to_thread(fn)Offloads blocking I/O to thread poolWrapping sync libraries

Rules

  • Never mix asyncio.run() with a running loop — it raises RuntimeError
  • Never call blocking I/O in an async function without to_thread — it blocks the entire loop
  • Use async for for async iteration and async with for async context managers
  • Cancellation: Handle asyncio.CancelledError explicitly when cleanup is needed

Structural Pattern Matching (3.10+)

match/case is a structural destructuring tool, not a switch statement. It combines type checking, attribute extraction, and branching in one expression:

match command:
    case {"action": "move", "direction": str(d)}:
        move(d)
    case {"action": "quit"}:
        sys.exit(0)
    case Point(x=0, y=y):
        print(f"On y-axis at {y}")
    case [first, *rest] if len(rest) > 2:
        process_batch(first, rest)
    case _:
        raise ValueError(f"Unknown command: {command}")

Key Facts

FactDetail
Single subjectmatch targets exactly one variable — no scattered conditions
DestructuringSequences, mappings, and class attributes are unpacked declaratively
Guardscase X if condition: for fine-grained filtering
Wildcard _Can appear multiple times in one pattern (unlike assignment)
Class matchingUses __match_args__ or @dataclass for positional patterns

[!WARNING] Strings are NOT sequences in match/case. Unlike standard unpacking, case [x, y] will never match a two-character string. This is intentional — it prevents a class of bugs where strings are accidentally iterated as character arrays.


Type System

Python's type system is gradual — annotations are optional but invaluable for static analysis. The runtime remains dynamic; types don't enforce anything at execution time.

Annotation Rules

  • Annotate function signatures — parameters and return types
  • Let inference handle locals — don't annotate obvious assignments
  • Use modern syntax — int | str not Union[int, str], str | None not Optional[str] (3.10+)

Key Constructs

ConstructUse
type Vector = list[float] (3.12+)Type alias — equivalent name for a type
NewType('UserId', int)Distinct subtype — a raw int won't satisfy UserId
ProtocolStructural subtyping ("static duck typing")
@runtime_checkableEnables isinstance checks against a Protocol
TypeIs (3.13+)Preferred over TypeGuard — supports intersection narrowing and negative narrowing
TypeGuardOlder narrowing — only narrows in the positive branch

Any vs object

Any disables type checking. object is type-safe — it requires narrowing before use. Prefer object when you mean "anything" but still want the checker engaged.

The TYPE_CHECKING Pattern

Avoid import cycles by guarding type-only imports:

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from expensive_module import HeavyType

def process(item: HeavyType) -> None:
    ...

String Handling

f-strings (Default)

f-strings are the standard for string interpolation. They are fast, readable, and support format specs:

name = "world"
print(f"Hello, {name!r}")       # repr
print(f"Pi is {math.pi:.4f}")   # format spec
print(f"{value:>10,}")          # right-aligned with comma separator

Template Strings (3.14+)

T-strings (t"...") produce a Template object instead of a str, enabling handler-based interpolation. Use them when interpolating untrusted input — f-strings evaluate eagerly and cannot be intercepted:

from string.templatelib import Template, Interpolation

def sanitized_sql(template: Template) -> tuple[str, tuple]:
    parts, params = [], []
    for item in template:
        if isinstance(item, str):
            parts.append(item)
        elif isinstance(item, Interpolation):
            parts.append("?")
            params.append(item.value)
    return "".join(parts), tuple(params)

query, params = sanitized_sql(t"SELECT * FROM users WHERE id = {user_input}")

Legacy

  • str.format() — use only when format spec is dynamic (known at runtime, not write-time)
  • % formatting — legacy only. Do not use in new code.

Error Handling

Specific Exceptions

Catch specific exceptions. Never use bare except: or except Exception: pass:

# ✅ Specific, with context
try:
    config = load_config(path)
except FileNotFoundError:
    raise ConfigError(f"Config not found: {path}") from None
except json.JSONDecodeError as e:
    raise ConfigError(f"Malformed config at {path}: {e}") from e

# ❌ Silent swallow — hides every possible failure
try:
    config = load_config(path)
except:
    pass

Exception Chaining

Use from to preserve the causal chain. Use from None to intentionally suppress it:

# Chain — preserves original traceback
raise AppError("operation failed") from original_error

# Suppress — when the original is noise for the caller
raise AppError("not found") from None

Custom Exceptions

Define domain-specific exceptions. Keep hierarchies shallow:

class AppError(Exception):
    """Base for application errors."""

class ValidationError(AppError):
    def __init__(self, field: str, message: str):
        self.field = field
        super().__init__(f"{field}: {message}")

Anti-Patterns

Anti-PatternDescriptionRemedy
Mutable default argsdef f(x=[]): — shared across callsUse None sentinel
Bare exceptexcept: catches SystemExit, KeyboardInterruptCatch specific types
Shadowing builtinslist = [1, 2, 3]Use descriptive names
God classMonolithic class with 20+ methodsDecompose into functions and smaller classes
Stringly typedUsing strings where enums or types belongenum.Enum, NewType, Literal
Deep nesting4+ levels of indentationEarly returns, guard clauses, helper functions
import *Pollutes namespace, breaks toolingExplicit named imports
Type: ignore spamSilencing every type errorFix the types or narrow properly
Overusing classesClasses with no state (just methods)Use plain functions
isinstance chainsLong if isinstance(x, A) ... elif isinstance(x, B)match/case or dispatch

Tooling

ruff check . --fix    # Lint + autofix
ruff format .         # Format
mypy .                # Type check (strict)
pyright .             # Alternative type checker
pytest                # Run tests
pytest --cov          # Coverage
uv run ...            # Fast dependency management and execution

Quick Reference

  • 4-space indent — never tabs, never 2-space
  • snake_case — functions, variables, modules
  • PascalCase — classes only
  • if x is None — not == None
  • if not seq — not len(seq) == 0
  • @dataclass — not manual __init__
  • with — for any resource needing cleanup
  • Comprehensions — for simple transforms, loops for complex logic
  • Generators — for lazy iteration over large data
  • f-strings — default interpolation; T-strings for untrusted input
  • int | str — not Union[int, str] (3.10+)
  • * separator — force keyword-only args when order is ambiguous
  • Never shadow builtins — id, type, list, dict, etc.
  • Never except: pass — catch specific, handle or propagate

These idioms refine but are subordinate to the Code-Edit Constraints.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

SOP for auditing AI-generated code. Trigger when: - Reviewing, refactoring, or cleaning up AI-generated code to prevent regressions or hallucinated APIs. - Prompt contains: /ai-audit, code audit, AI cleanup, common flaws.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

Protocol for auditing API surface coherence and type safety. Trigger when: - Evaluating API designs, interface type safety, or design elegance. - Prompt contains: /api-audit, API surface, API coherence, type safety.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

Normative sufficiency conditions for Initial Boundary Conditions (IBCs) and the SOP for the cheap-tier boundary refinement loop (/boundary). Trigger when: - Crafting, auditing, or refining a prompt/IBC destined for an expensive (architect-class) model or an autonomous worker dispatch. - Evaluating whether a task frame is sufficient to bound an agent walk. - Prompt contains: /boundary, IBC, initial boundary condition, boundary contract, sufficiency conditions, worker prompt, prompt refinement.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

SOP for the architect-tier campaign workflow (/campaign): exhaustive survey, mitigation planning, tiered orchestration, and reconciliation. Trigger when: - Running a multi-workstream initiative where an expensive architect-tier council surveys, plans, emits worker prompts, and judges landed work. - Conducting production-readiness assessments that fan out into autonomous mitigation dispatches across model tiers. - Prompt contains: /campaign, campaign workflow, survey, orchestrate, reconcile, premise freshness, tier routing, worker IBC, scratch.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

Maintain and update the persistent project chronicle (docs/chronicle.md). Trigger when: - The human requests a history summary or chronicle update. - Starting work on a new codebase and needing context on its evolution. - Prompt contains keywords: /chronicle, chronicle, project history, git log summary, history summary.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

Rules, conventions, and constraints for formatting git commit messages and committing at logical boundaries. Trigger when: - Drafting, revising, or validating git commit messages. - Pausing at commit boundaries under the CORE or CONTINUE workflows. - Evaluating whether a changeset should be split into multiple commits. - Prompt contains keywords: commit message, git commit, conventional commits, commit hygiene, commit guidelines, logical boundary, spaghetti diff, atomic commit, commit boundary.

The English summary is being prepared. Showing the original description.

nrdxp/predicate10Updated Sep 1, 2026

See all skills by nrdxp

Report a problem with this skill