Python language conventions, modern idioms, and toolchain. Invoke whenever task involves any interaction with Python code — writing, reviewing, refactoring, debugging, or understanding Python projects.
Applies Python 3.14+ conventions and modern tooling for writing, reviewing, and refactoring code.
npx claudepluginhub xobotyi/cc-foundryThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/concurrency.mdreferences/modules.mdreferences/packaging.mdreferences/typing.mdReadability counts. Explicit is better than implicit. If your code needs a comment to explain its control flow, restructure it.
Python 3.14+ is the baseline. Use modern syntax unconditionally — no backward compatibility with older Python versions unless the project explicitly requires it.
Extended examples, packaging workflows, and detailed rationale for the rules below live in
references/.
| Topic | Reference | Contents |
|---|---|---|
| Type annotation patterns, generics, overloads, TypeVar, variance | typing.md | Full annotation examples, generic class patterns, Protocol implementation, TypeVar usage |
| Project layout, pyproject.toml, uv, dependency management | packaging.md | pyproject.toml templates, uv workflows, src layout, dependency groups, build backends |
Module system, imports, namespace packages, __init__.py | modules.md | Import resolution order, circular import fixes, lazy imports, namespace packages |
| asyncio, TaskGroup, cancellation, timeouts, threading interop | concurrency.md | TaskGroup error handling, timeout scopes, cancellation semantics, to_thread, eager task factory |
| Entity | Style | Examples |
|---|---|---|
| Variables, functions, methods | snake_case | user_name, fetch_data |
| Classes, type aliases | PascalCase | UserService, HttpClient |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES, API_BASE_URL |
| Modules, packages | snake_case, short | user_store, auth |
| Private attributes/methods | _ prefix | _internal_cache, _validate() |
| Name-mangled attributes | __ prefix | __secret (rarely needed) |
| Type variables | PascalCase, short | T, KT, VT, ResponseT |
| Protocols | PascalCase, -able/-ible suffix | Renderable, Serializable |
user_count not n. Short names (i, x) only in tiny scopes
(comprehensions, simple lambdas).car.make not car.car_make.is_/has_/can_/should_ prefix: is_valid, has_access.f for file, e for exception, k/v for key/value).Python 3.14+ uses modern annotation syntax natively. No from __future__ import annotations
needed — all annotations are evaluated lazily by default.
list[str], dict[str, int], tuple[int, ...],
set[float]. Never import List, Dict, Tuple, Set from typing.|: str | None, int | float. Never Optional[X] or Union[X, Y].type statement for aliases: type Vector = list[float]. Not TypeAlias annotation.None return: annotate -> None on functions that return nothing. Omit return type
only on __init__.Any — it disables type checking. Use object when you mean "any type but still
type-safe." Use Any only at true interop boundaries with untyped code.type parameter syntax (3.12+): class Stack[T]: and def first[T](items: list[T]) -> T:
instead of TypeVar declarations.def process[T: (str, bytes)](data: T) -> T: for a
finite set of allowed types.def sort[T: Comparable](items: list[T]) -> list[T]: for
upper-bound constraints.covariant/contravariant
flags needed.@runtime_checkable only when you need isinstance() checks — it adds overhead and
only validates method presence, not signatures.from typing import Protocol, runtime_checkable
@runtime_checkable
class Renderable(Protocol):
def render(self) -> str: ...
collections.abc.Callable for callable annotations:
Callable[[int, str], bool].ParamSpec for decorators that preserve signatures:
def decorator[**P, R](fn: Callable[P, R]) -> Callable[P, R]:.Protocol for complex callable signatures with keyword arguments or overloads.TypeIs (3.13+) for narrowing that refines the input type:
def is_str_list(val: list[object]) -> TypeIs[list[str]]:.TypeGuard for narrowing where the output type is unrelated to input:
def is_valid_config(data: object) -> TypeGuard[Config]:.See typing.md for full annotation patterns, generics, overloads, and variance.
@dataclass for data containers — classes that primarily hold data with minimal
behavior.frozen=True for immutable data: @dataclass(frozen=True). Default to frozen unless
mutation is required.slots=True for memory efficiency and attribute safety:
@dataclass(slots=True, frozen=True).kw_only=True when constructors have more than 3 fields — prevents positional
argument ordering bugs.field(default_factory=list) for mutable defaults. Never use mutable default
arguments.__post_init__ for derived fields and validation.@dataclass(frozen=True, slots=True, kw_only=True)
class User:
name: str
email: str
roles: list[str] = field(default_factory=list)
NamedTuple or plain tuples.class syntax over functional form: class Point(NamedTuple): x: float; y: float.enum.Enum for categorical constants. Never use bare strings or ints as
pseudo-enums.enum.StrEnum when the enum must interoperate with string APIs (JSON, config keys).enum.IntEnum only when integer interop is mandatory (legacy protocols). Prefer
Enum otherwise.@enum.unique to prevent duplicate values.Color(1). Access by name: Color["RED"]. Iteration: for c in Color:.from enum import StrEnum, unique
@unique
class Status(StrEnum):
ACTIVE = "active"
INACTIVE = "inactive"
SUSPENDED = "suspended"
match/case (3.10+) is the preferred dispatch mechanism for structural patterns.
if/elif chains on a single value.case _: arm unless the match is provably exhaustive.if: case Point(x, y) if x > 0:.| for alternatives: case "quit" | "exit" | "q":.case {"error": str() as msg}: captures while matching type.__match_args__ or keyword patterns:
case Point(x=0, y=y):.match command:
case {"action": "move", "direction": str() as direction}:
move(direction)
case {"action": "attack", "target": str() as target}:
attack(target)
case _:
raise ValueError(f"Unknown command: {command}")
None + conditional for mutable
defaults: def f(items: list[int] | None = None): then items = items or [] in body.
Never def f(items: list[int] = []):.* to force keyword-only arguments after positional params:
def connect(host: str, *, port: int = 443):./ to force positional-only for parameters that callers shouldn't name:
def sqrt(x: float, /) -> float:.None means absent, not error. Return T | None for optional results. Raise
exceptions for errors.functools.wraps:
def retry[**P, R](fn: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(fn)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
...
return wrapper
@staticmethod and
@classmethod must be outermost (topmost in source).@retry(attempts=3) means retry returns
the actual decorator function.contextlib.contextmanager for simple resource management:
@contextmanager
def managed_connection(url: str) -> Iterator[Connection]:
conn = Connection(url)
try:
yield conn
finally:
conn.close()
__enter__ and __exit__.contextlib.suppress(ExceptionType) instead of empty except: pass.contextlib.closing(thing) for objects with .close() but no __exit__.contextlib.asynccontextmanager for async resource management.with for files, locks, database connections, and any resource that
needs deterministic cleanup.yield to produce values on demand instead of
building full lists in memory.sum(x * x for x in range(1000)).yield from to delegate to sub-generators — preserves .send(), .throw(),
.close() protocol.itertools for composition: chain, islice, groupby, batched (3.12+),
pairwise (3.10+).def gen() -> Iterator[int]: for simple generators,
Generator[YieldType, SendType, ReturnType] when using .send().itertools.tee.[x.name for x in users if x.active].for clauses are the absolute limit.
Beyond that, extract to a function.[print(x) for x in items] is wrong —
use a for loop.[y for x in data if (y := transform(x)) is not None].{k.lower(): v for k, v in headers.items()}.except ValueError: not
except Exception:.except:. It catches SystemExit, KeyboardInterrupt, and GeneratorExit.
At minimum use except Exception:.except* ExceptionGroup (3.11+) for handling multiple concurrent exceptions from
TaskGroup and similar.raise AppError("context") from err chains the original cause.if key in dict: not
try: dict[key] except KeyError: (unless the miss is rare and lookup is expensive).Exception:
class AppError(Exception): ...
class NotFoundError(AppError): ...
class ValidationError(AppError): ...
"parse config: invalid format".else clause runs only when no exception was raised — use for code that should
execute on success but isn't part of the try body.finally for unconditional cleanup — prefer context managers when possible.ExceptionGroup to bundle multiple errors. Handle
with except* which matches by type and re-raises unhandled exceptions..add_note() (3.11+) to attach context without creating new
exception types.% formatting or .format() in new code.str.removeprefix() / str.removesuffix() (3.9+) over slicing.textwrap.dedent when indentation matters."".join(parts) for building strings in loops — never += in a loop.r"..." for regex patterns and Windows paths.pathlib.Path for all filesystem operations. Never os.path in new code./ operator for path joining: base / "subdir" / "file.txt".path.exists(), path.is_file(), path.is_dir(),
path.read_text(), path.write_text(), path.mkdir(parents=True, exist_ok=True),
path.iterdir(), path.glob("*.py"), path.rglob("**/*.py").path.resolve() for absolute paths. path.relative_to(base) for relative paths.str | Path in public APIs, convert to Path internally.from mypackage.utils import helper.from .models import User.import os, from pathlib import Path)import httpx, from pydantic import BaseModel)from myapp.models import User)from collections import defaultdict not import collections
(unless you use many names from the module).from module import * — pollutes namespace, breaks type checkers, hides
dependencies.if TYPE_CHECKING: block for imports used only in annotations — avoids circular
imports and runtime overhead. In 3.14+ with lazy annotations, this is less necessary
but still useful for avoiding circular import side effects.__slots__ on classes that will have many instances — prevents __dict__
creation, saves memory, catches typos in attribute names.@dataclass(slots=True) adds slots automatically.__slots__.
Missing slots on a parent reintroduces __dict__.__repr__ on every class — must be unambiguous:
def __repr__(self) -> str: return f"User(name={self.name!r})".__str__ only when a human-readable form differs from repr.__eq__ and __hash__ — if you define __eq__, define __hash__ too (or set
__hash__ = None to make unhashable). Mutable objects should not be hashable.__bool__ — define when truthiness of instances has meaningful semantics.__enter__/__exit__ for context manager protocol.__init_subclass__ for class registration patterns without metaclasses.__class_getitem__ to make classes subscriptable for generic type hints.from abc import ABC, abstractmethod.super() — always use super() (no arguments in 3.x). Never hardcode parent class
names.@classmethod for alternative constructors: User.from_dict(data).@staticmethod for utility functions that don't need class or instance state — but
prefer module-level functions unless the function is logically part of the class's API.pyproject.toml is the single source of truth for project metadata, dependencies,
tool configuration. Never setup.py or setup.cfg in new projects.hatchling, flit-core, or setuptools with
[build-system] table.>= lower bound, avoid upper bounds unless
genuinely incompatible: httpx>=0.27.uv is the preferred Python package manager and environment tool.uv sync to install dependencies from lock file.uv add <package> to add dependencies.uv run <command> to run commands in the project environment.uv lock to generate/update the lock file.uv venv to create virtual environments.uv python install 3.14 to install Python versions.my-project/
├── pyproject.toml
├── uv.lock
├── src/
│ └── my_package/
│ ├── __init__.py
│ └── ...
└── tests/
├── conftest.py
└── ...
src/. Prevents accidental imports from the
project root during testing.__init__.py — keep minimal. Define __all__ for public API. Don't put substantial
logic in init files.ruff for both linting and formatting. Single tool, fast.ruff check to lint. ruff format to format.pyproject.toml under [tool.ruff].See packaging.md for pyproject.toml templates, uv workflows, and dependency management patterns.
async/await for I/O-bound concurrency.asyncio.TaskGroup (3.11+) for structured concurrency — replaces
asyncio.gather() with better error handling.asyncio.gather() in new code — it has inconsistent error semantics.
Use TaskGroup instead.asyncio.run() as the single entry point. Never loop.run_until_complete().asyncio.CancelledError — always clean up resources in finally blocks.concurrent.futures.ThreadPoolExecutor for CPU-light I/O-bound parallel work.threading.Lock for shared mutable state. Always use with lock: context manager.multiprocessing or ProcessPoolExecutor for CPU-bound tasks.--disable-gil, standard
thread-safety practices become critical. Guard all shared mutable state with locks.TaskGroup and context managers over bare
create_task().logging module over print() for anything beyond quick debugging.logger = logging.getLogger(__name__) at module level.logger.info("User %s logged in", user_id) not
logger.info(f"User {user_id} logged in") — f-string evaluates even when level
is disabled.DEBUG for diagnostics, INFO for operational events,
WARNING for degraded but working, ERROR for failures, CRITICAL for system-down.When writing Python code: apply all conventions silently — don't narrate each rule. If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
When reviewing Python code: cite the specific violation and show the fix inline. Don't lecture — state what's wrong and how to fix it.
Bad: "According to Python best practices, you should use type unions
with the pipe operator instead of Optional..."
Good: "Optional[str] -> str | None"
A pyright-langserver LSP server is configured for .py and .pyi files. Always use LSP
tools for code navigation instead of Grep or Glob. LSP understands Python's module system,
type inference, scope rules, and package boundaries — text search does not.
| Task | LSP Operation | Why LSP over text search |
|---|---|---|
| Find where a function/class/variable is defined | goToDefinition | Resolves imports, re-exports, aliases |
| Find all usages of a symbol | findReferences | Scope-aware, no false positives from string matches |
| Get type signature, docs, or return types | hover | Instant type info without reading source files |
| List all symbols in a file | documentSymbol | Structured output vs grepping for def/class |
| Find a symbol by name across the project | workspaceSymbol | Searches all packages, respects __all__ |
| Find implementations of a Protocol or ABC | goToImplementation | Knows the type system and structural subtyping |
| Find what calls a function | incomingCalls | Precise call graph across module boundaries |
| Find what a function calls | outgoingCalls | Structured dependency map |
Grep/Glob remain appropriate for: text in comments, string literals, log messages, TODO markers, config values, env vars, file name patterns, URLs, error message text — anything that isn't a Python identifier.
When spawning subagents for Python codebase exploration, instruct them to use LSP tools. Subagents have access to the same LSP server.
ruff: single entry point for linting and formatting. Must pass before committing.
ruff check — lint. ruff check --fix — auto-fix.ruff format — format.uv: package management, virtual environments, Python version management.mypy or pyright: static type checking. Configure in pyproject.toml.The coding skill governs workflow (discovery, planning, verification); this skill governs Python implementation choices. The pytest skill governs testing conventions — both are active simultaneously when writing Python tests.
Readability counts. If you read a function twice to understand it, rewrite it once to make it clear.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.