Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Python Conventions

ASecurity

Modern Python idioms for any Python 3.10+ project: PEP 8 style, PEP 484/526 type hints, dataclasses, pathlib, enums, f-strings, structural pattern matching (match/case), context managers, exception handling, and null discipline. Stack-agnostic — referenced by every Python plugin in the marketplace. Use this skill to: - Write strongly-typed code with type hints on every function signature and class attribute. - Replace isinstance chains and magic constants with match/case and enums. - Use dat...

35 stars
0 votes
0 copies
0 views
Added 9/22/2026
developmentpythongobashexpressfastapidjangoflasktestingdebuggingapi

Works with

cliapi

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add AratKruglik/claude-sdlc --skill python-conventions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Conventions?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Python Conventions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-python-conventions/badge)](https://www.skillsdirectory.com/skills/aratkruglik-python-conventions)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: python-conventions
description: |
  Modern Python idioms for any Python 3.10+ project: PEP 8 style, PEP 484/526 type hints, dataclasses, pathlib, enums, f-strings, structural pattern matching (match/case), context managers, exception handling, and null discipline. Stack-agnostic — referenced by every Python plugin in the marketplace.

  Use this skill to:
  - Write strongly-typed code with type hints on every function signature and class attribute.
  - Replace isinstance chains and magic constants with match/case and enums.
  - Use dataclasses or NamedTuple for value objects and data containers.
  - Handle resources safely with context managers and pathlib for all file paths.
  - Apply PEP 8 naming and structure conventions consistently.

  Do NOT use this skill for:
  - Framework-specific idioms (Django ORM, FastAPI routers, Flask blueprints — those live in framework plugin skills).
  - Packaging and dependency management — see python-foundation:python-tooling.
  - Testing patterns — see python-foundation:pytest-testing.
user-invocable: false
paths: ["**/*.py"]
---

# Python Conventions (stack-agnostic, Python 3.10+)

This skill encodes idioms that reduce bugs and improve readability in any Python codebase. Apply alongside the active framework plugin's conventions skill (e.g., `django-plugin:django-conventions`, `fastapi-plugin:fastapi-conventions`).

## Detection

Read `pyproject.toml` to determine the target Python version before writing code:

```toml
# pyproject.toml — Poetry style
[tool.poetry.dependencies]
python = "^3.10"

# pyproject.toml — PEP 621 style
[project]
requires-python = ">=3.10"
```

Also check for a `.python-version` file (used by pyenv):

```bash
cat .python-version   # e.g. "3.12.3"
```

Match the project's minimum version — do not use Python 3.11+ features (e.g. `StrEnum`, `tomllib`, `ExceptionGroup`) on a 3.10 project. All idioms in this skill apply from 3.10 onwards.

## Type hints — always on

Annotate every function parameter, return type, and class attribute. Type hints are documentation that a machine can check.

```python
from __future__ import annotations  # enables forward references on 3.10

from collections.abc import Sequence


def greet(name: str, times: int = 1) -> str:
    return (f"Hello, {name}!\n" * times).rstrip()


def first_active(users: Sequence[User]) -> User | None:  # union syntax (3.10+)
    return next((u for u in users if u.is_active), None)
```

Use `from __future__ import annotations` at the top of files that reference types not yet defined at parse time (forward references, self-referential types). It defers evaluation of all annotations to strings, avoiding `NameError`.

For library or shared utility code, use `TypeVar` and `Generic` to express parametric types:

```python
from typing import TypeVar, Generic

T = TypeVar("T")


class Result(Generic[T]):
    def __init__(self, value: T, ok: bool) -> None:
        self.value = value
        self.ok = ok

    @classmethod
    def success(cls, value: T) -> Result[T]:
        return cls(value, ok=True)

    @classmethod
    def failure(cls, value: T) -> Result[T]:
        return cls(value, ok=False)
```

Use `TypeAlias` to give complex types a readable name:

```python
from typing import TypeAlias

UserId: TypeAlias = int
Headers: TypeAlias = dict[str, str]
```

## Dataclasses — prefer over plain dicts

Use `@dataclass` instead of plain dicts or ad-hoc objects for structured data. Use `frozen=True` for immutable value objects.

```python
from dataclasses import dataclass, field
from datetime import datetime


@dataclass(frozen=True)
class Money:
    amount: int          # stored in cents
    currency: str

    def __post_init__(self) -> None:
        if self.amount < 0:
            raise ValueError(f"amount must be non-negative, got {self.amount}")
        if len(self.currency) != 3:
            raise ValueError(f"currency must be a 3-letter ISO code, got {self.currency!r}")

    def add(self, other: Money) -> Money:
        if self.currency != other.currency:
            raise ValueError(f"currency mismatch: {self.currency} vs {other.currency}")
        return Money(self.amount + other.amount, self.currency)


@dataclass
class Order:
    order_id: str
    lines: list[OrderLine] = field(default_factory=list)
    created_at: datetime = field(default_factory=datetime.utcnow)

    def total(self) -> Money:
        if not self.lines:
            return Money(0, "USD")
        totals = [line.subtotal() for line in self.lines]
        return sum(totals[1:], start=totals[0])
```

`field(default_factory=list)` is required for mutable defaults — never use `lines: list = []` as a default value (it shares the same list across all instances).

`__post_init__` runs after `__init__` and is the right place for validation.

Use `NamedTuple` when you need tuple unpacking or a lighter-weight immutable container:

```python
from typing import NamedTuple


class Point(NamedTuple):
    x: float
    y: float


origin = Point(0.0, 0.0)
x, y = origin  # tuple unpacking works
```

## Enums — replace magic constants

Use `Enum` for closed sets of values. Backed enums (`str`, `int`) serialize and deserialize cleanly.

```python
from enum import Enum


class Status(str, Enum):
    DRAFT = "draft"
    ACTIVE = "active"
    ARCHIVED = "archived"

    def is_published(self) -> bool:
        return self is Status.ACTIVE


class Priority(int, Enum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3
```

Parse from external data with `Status(value)` — raises `ValueError` on unknown values, which is usually what you want at a boundary:

```python
raw = request.json["status"]
status = Status(raw)          # ValueError if raw is not a valid Status value
```

Use enums in `match`/`case` for exhaustive handling (see next section). Never model a closed set with bare string constants.

## match/case — structural pattern matching

Replace long `if/elif` chains and `isinstance` checks with `match`/`case`. Python's `match` is structural — it can destructure objects, sequences, and mappings in one expression.

**Matching on enum values:**

```python
def describe_status(status: Status) -> str:
    match status:
        case Status.DRAFT:
            return "Not yet published"
        case Status.ACTIVE:
            return "Live and visible"
        case Status.ARCHIVED:
            return "Hidden from public"
        case _:
            raise ValueError(f"Unhandled status: {status!r}")
```

**Matching on dataclass structure:**

```python
@dataclass
class Circle:
    radius: float

@dataclass
class Rectangle:
    width: float
    height: float

Shape = Circle | Rectangle


def area(shape: Shape) -> float:
    match shape:
        case Circle(radius=r):
            return 3.14159 * r * r
        case Rectangle(width=w, height=h):
            return w * h
        case _:
            raise TypeError(f"Unknown shape: {shape!r}")
```

**Matching sequences:**

```python
def parse_command(tokens: list[str]) -> str:
    match tokens:
        case ["quit"]:
            return "exit"
        case ["go", direction]:
            return f"moving {direction}"
        case ["pick", "up", item]:
            return f"picking up {item}"
        case [verb, *rest]:
            return f"unknown command: {verb} {rest}"
        case _:
            return "empty input"
```

**Guard clauses with `if`:**

```python
match point:
    case Point(x=x, y=y) if x == y:
        return "on diagonal"
    case Point(x=x, y=y):
        return f"at ({x}, {y})"
```

## pathlib — all filesystem paths

Replace every `os.path.*` call with `pathlib.Path`. Never concatenate path strings with `+` or `/` (the string operator).

```python
from pathlib import Path


def read_config(config_dir: Path) -> dict[str, str]:
    config_file = config_dir / "config.toml"    # / operator for joining
    if not config_file.exists():
        raise FileNotFoundError(f"Config not found: {config_file}")
    return parse_toml(config_file.read_text(encoding="utf-8"))


def ensure_output_dir(base: Path, name: str) -> Path:
    out = base / "output" / name
    out.mkdir(parents=True, exist_ok=True)
    return out


def find_python_files(src: Path) -> list[Path]:
    return sorted(src.glob("**/*.py"))


def write_report(path: Path, content: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")
```

Always pass `encoding="utf-8"` to `read_text`/`write_text` — do not rely on the platform default. Use `Path` objects throughout; convert to `str` only when an external API requires it (`str(path)`).

## Context managers

Use `with` for every resource that needs cleanup: files, locks, network connections, database sessions, temporary directories.

```python
from pathlib import Path


def process_file(path: Path) -> list[str]:
    with path.open(encoding="utf-8") as fh:
        return [line.rstrip() for line in fh]
```

Write custom context managers with `contextlib.contextmanager` instead of implementing `__enter__`/`__exit__` on a class, unless the class already exists for other reasons:

```python
from contextlib import contextmanager
from collections.abc import Generator
import time


@contextmanager
def timer(label: str) -> Generator[None, None, None]:
    start = time.perf_counter()
    try:
        yield
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.3f}s")


with timer("data load"):
    records = load_large_dataset()
```

Use `contextlib.suppress` to swallow expected exceptions without a bare `try/except/pass`:

```python
from contextlib import suppress
from pathlib import Path


def remove_if_exists(path: Path) -> None:
    with suppress(FileNotFoundError):
        path.unlink()
```

## Exception discipline

Never use bare `except:` or `except Exception:` without re-raising. Always catch the most specific exception type available.

```python
# Prefer
try:
    value = int(raw_input)
except ValueError as err:
    raise InvalidInputError(f"Expected integer, got {raw_input!r}") from err

# Avoid
try:
    value = int(raw_input)
except:          # catches SystemExit, KeyboardInterrupt, everything
    pass
```

Define domain exceptions that inherit from `Exception` (not `BaseException`):

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


class NotFoundError(AppError):
    def __init__(self, resource: str, id: int) -> None:
        super().__init__(f"{resource} with id={id} not found")
        self.resource = resource
        self.id = id


class ValidationError(AppError):
    def __init__(self, field: str, message: str) -> None:
        super().__init__(f"Validation failed for '{field}': {message}")
        self.field = field
```

Use exception chaining (`raise ... from err`) to preserve the original cause:

```python
def load_user(user_id: int) -> User:
    try:
        row = db.fetch_one("SELECT * FROM users WHERE id = %s", (user_id,))
    except DatabaseError as err:
        raise NotFoundError("User", user_id) from err
    if row is None:
        raise NotFoundError("User", user_id)
    return User.from_row(row)
```

The `from err` clause attaches the original exception as `__cause__`, giving full traceback context without losing information.

## Naming conventions (PEP 8)

| Element | Convention | Example |
|---|---|---|
| Functions and methods | `snake_case` | `calculate_total()` |
| Variables and parameters | `snake_case` | `order_id`, `is_active` |
| Classes | `PascalCase` | `OrderService`, `UserRepository` |
| Module-level constants | `UPPER_SNAKE_CASE` | `MAX_RETRIES = 3` |
| Private attributes / methods | `_single_underscore` prefix | `_cache`, `_validate()` |
| Name-mangled attributes | `__double_underscore` prefix | `__secret` (use rarely) |
| Type aliases | `PascalCase` | `UserId`, `Headers` |

Module structure — keep this order:

```python
"""Module docstring."""

from __future__ import annotations

# Standard library imports
import os
from pathlib import Path

# Third-party imports
import httpx

# Local imports
from myapp.domain import User
from myapp.exceptions import NotFoundError

# Module-level constants
DEFAULT_TIMEOUT: int = 30
MAX_RETRIES: int = 3

# Public API
__all__ = ["UserClient"]


class UserClient:
    ...
```

## Class design

Prefer composition over inheritance. Keep `__init__` (or `@dataclass`) typed. Use `@property` for computed attributes that derive from instance state without side effects. Use `@classmethod` for alternate constructors.

```python
from dataclasses import dataclass
from datetime import date


@dataclass
class DateRange:
    start: date
    end: date

    def __post_init__(self) -> None:
        if self.end < self.start:
            raise ValueError(f"end ({self.end}) must be >= start ({self.start})")

    @classmethod
    def for_month(cls, year: int, month: int) -> DateRange:
        import calendar
        last_day = calendar.monthrange(year, month)[1]
        return cls(
            start=date(year, month, 1),
            end=date(year, month, last_day),
        )

    @property
    def duration_days(self) -> int:
        return (self.end - self.start).days + 1

    def contains(self, d: date) -> bool:
        return self.start <= d <= self.end
```

`@dataclass` auto-generates `__repr__` and `__eq__`. When not using `@dataclass`, always define `__repr__` to aid debugging — it should return a string that ideally could be used to recreate the object:

```python
class Money:
    def __repr__(self) -> str:
        return f"Money(amount={self.amount!r}, currency={self.currency!r})"
```

Avoid deep inheritance hierarchies. If a class does too many things, extract collaborators and inject them via the constructor (composition). Depend on abstractions (abstract base classes or `Protocol`) rather than concrete types:

```python
from typing import Protocol


class Notifier(Protocol):
    def send(self, recipient: str, message: str) -> None: ...


class OrderService:
    def __init__(self, notifier: Notifier) -> None:
        self._notifier = notifier

    def confirm(self, order: Order) -> None:
        # ... business logic ...
        self._notifier.send(order.customer_email, f"Order {order.id} confirmed")
```

`Protocol` is structural typing — any class with a matching `send` method satisfies `Notifier` without explicit inheritance. Prefer it over ABCs for dependency injection boundaries.

Attribution

AratKruglikAratKruglik
View sourceMore from AratKruglik →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

284722 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2192 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →