pep8
Python Style & PEP 8 Enforcement
Auto-enforce Python 3.11+ standards.
When to Use
- Writing/reviewing Python code
- Type hint issues or style violations
- User requests PEP 8 check
Core Standards
| Standard | Desc |
|---|---|
| PEP 8 | Naming, imports, spacing |
| PEP 484/585 | Type hints (modern) |
| PEP 257 | Docstrings |
| PEP 604 | Union | |
| PEP 570/3102 | / positional, * keyword |
Naming
class UserAccount: pass # PascalCase
class HTTPClient: pass # Acronyms: all caps
def calculate_total(): pass # snake_case
async def fetch_data(): pass # async same
user_name = "john" # Variables: snake_case
MAX_RETRIES = 3 # Constants: SCREAMING_SNAKE
def _internal(): pass # Private: underscore
__mangled = "hidden" # Name mangling: double
T = TypeVar("T") # TypeVars: PascalCase
UserT = TypeVar("UserT", bound="User")
Type Hints (3.11+)
Modern Syntax (Required)
# Built-in generics (NOT typing module)
def process(items: list[str]) -> dict[str, int]: ...
# Union w/ | (NOT Optional/Union)
def find_user(id: str) -> User | None: ...
# Self type
from typing import Self
class Builder:
def chain(self) -> Self: return self
Patterns
from collections.abc import Callable, Awaitable
from typing import TypedDict, Literal, TypeAlias, ParamSpec, Generic
# Callable
Handler = Callable[[Request], Response]
AsyncHandler = Callable[[Request], Awaitable[Response]]
# TypedDict
class UserData(TypedDict):
id: str
email: Required[str]
phone: NotRequired[str | None]
# Literal
Status = Literal["pending", "active", "disabled"]
# TypeAlias
JsonValue: TypeAlias = str | int | float | bool | None | list["JsonValue"] | dict[str, "JsonValue"]
# ParamSpec (decorators)
P = ParamSpec("P")
def logged(fn: Callable[P, T]) -> Callable[P, T]: ...
# Generic
class Repo(Generic[T]):
def get(self, id: str) -> T | None: ...
Deprecated (Never Use)
# WRONG # RIGHT
List[str] # list[str]
Optional[int] # int | None
Dict[str, int] # dict[str, int]
Tuple[int, str] # tuple[int, str]
Union[int, str] # int | str
Docstrings (Google Style)
def calculate_discount(price: float, percent: float, min_price: float = 0.0) -> float:
"""Calculate discounted price w/ floor.
Args:
price: Original price.
percent: Discount (0-100).
min_price: Min allowed price.
Returns:
Final price, never below min_price.
Raises:
ValueError: If invalid inputs.
"""
Skip docstrings for: self-documenting fns, _private methods, trivial @property
Imports
# 1. Stdlib (alphabetical)
import asyncio
from pathlib import Path
from typing import Any
# 2. Third-party
import httpx
from fastapi import Depends, HTTPException
from pydantic import BaseModel
# 3. Local
from app.core.config import settings
from app.models import User
Rules: No wildcards (*), group from same module, parentheses for long imports
Function Signatures (PEP 570/3102)
def api_fn(
x: int, y: int, # positional-only
/,
z: int = 0, # positional or keyword
*,
strict: bool = False, # keyword-only
) -> Result: ...
# Prevents: api_fn(x=1, y=2) - forces positional
# Requires: api_fn(1, 2, strict=True) - explicit keyword
Overloads
from typing import overload
@overload
def process(v: int) -> int: ...
@overload
def process(v: str) -> str: ...
def process(v: int | str) -> int | str:
return v * 2 if isinstance(v, int) else v.upper()
Function Design
| Lines | Status |
|---|---|
| < 20 | Ideal |
| 20-30 | OK |
| 30-50 | Split |
| > 50 | Refactor |
Params: Max 5 → use dataclass/config obj for more Returns: Always annotate; no flag-based return types
Exception Handling
# DO: Specific exceptions w/ context
try:
user = await db.get(User, id)
except IntegrityError as e:
raise UserExistsError(id) from e
# DO: Context managers
async with AsyncSession(engine) as session:
async with session.begin(): ...
# DON'T
except: # bare - catches SystemExit
except Exception: # swallows errors
pass # silent - at minimum log
Constants
MAX_RETRIES = 3
DEFAULT_TIMEOUT = timedelta(seconds=30)
FORMATS = frozenset({"json", "xml"})
class Status(StrEnum):
PENDING = "pending"
ACTIVE = "active"
# NO magic values
await asyncio.sleep(5) # Bad
await asyncio.sleep(INTERVAL) # Good
Async
# Context managers
async with httpx.AsyncClient() as client:
resp = await client.get(url)
# Concurrent
results = await asyncio.gather(fetch_a(), fetch_b(), return_exceptions=True)
# TaskGroup (3.11+)
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch_a())
tg.create_task(fetch_b())
# Timeout
async with asyncio.timeout(5.0):
await slow_op()
# Never block loop
await asyncio.to_thread(blocking_io) # sync I/O
await asyncio.sleep(1) # NOT time.sleep()
Pathlib (NOT os.path)
from pathlib import Path
data = Path("data") / "config.json"
text = data.read_text(encoding="utf-8")
data.write_text(json.dumps(obj))
path.parent # dir
path.stem # name w/o ext
path.suffix # .ext
path.name # filename
Logging
logger = logging.getLogger(__name__)
# Lazy formatting (NOT f-strings)
logger.info("Processing %s items", count) # YES
logger.info(f"Processing {count}") # NO - always evaluated
# Exception
logger.exception("Failed") # auto-includes traceback
Data Models
| Type | Use Case |
|---|---|
| TypedDict | External JSON/dicts |
| dataclass | Internal DTOs |
| Pydantic | Validation needed |
| NamedTuple | Immutable, hashable |
Context Managers
from contextlib import suppress, asynccontextmanager
# suppress replaces try/except pass
with suppress(FileNotFoundError):
Path("temp.txt").unlink()
@asynccontextmanager
async def connection():
conn = await create()
try: yield conn
finally: await conn.close()
Anti-Patterns
| Bad | Fix |
|---|---|
| No type hints | Type all params & returns |
List, Optional |
list, | None |
Bare except: |
Specific exceptions |
| Magic numbers | Named constants |
d, x, temp |
Descriptive names |
process() |
process_orders() |
| > 50 lines | Split fn |
| Mutable defaults | None + factory |
== None |
is None |
| f-strings in logger | %s formatting |
| os.path | pathlib |
Python 3.11+ Features
# match/case
match cmd:
case {"action": "create", "data": d}: create(d)
case _: raise ValueError()
# Exception groups
except* ValueError as eg:
for e in eg.exceptions: handle(e)
# tomllib (built-in TOML)
import tomllib
config = tomllib.load(open("config.toml", "rb"))
# Self type
from typing import Self
def build(self) -> Self: return self
Formatting
- Line: 88 (Black) or 79 (strict PEP 8)
- Indent: 4 spaces
- Blanks: 2 top-level, 1 methods
- Trailing commas in multi-line
Scripts
Available in scripts/:
| Script | Purpose | Usage |
|---|---|---|
check_style.py |
Full check (ruff + pycodestyle + mypy) | python check_style.py src/ --fix |
check_pep8.sh |
Quick PEP 8 only | ./check_pep8.sh script.py |
check_types.sh |
Type hints only | ./check_types.sh src/ --strict |
fix_style.sh |
Auto-fix issues | ./fix_style.sh src/ |
Install deps: pip install ruff pycodestyle mypy
Tooling
pyproject.toml
[tool.ruff]
target-version = "py311"
line-length = 88
[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP", "N", "RUF", "ASYNC", "S"]
ignore = ["E501"]
[tool.ruff.lint.isort]
known-first-party = ["app"]
[tool.mypy]
python_version = "3.11"
strict = true
Pre-commit
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
More from georgekhananaev/claude-skills-vault
system-architect
System architecture skill for designing scalable, maintainable software systems. Covers microservices/monolith decisions, API design, DB selection, caching, security, and scalability planning.
21skill-creator
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
12code-quality
Multi-language code quality standards and review for TypeScript, Python, Go, and Rust. Enforces type safety, security, performance, and maintainability. Use when writing, reviewing, or refactoring code. Includes review process, checklist, and Python PEP 8 deep-dive.
12token-optimizer
Reduce token count in prompts, docs, and prose. Covers prompt compression (40-60% savings), doc formatting, TOON data serialization, and Strunk's prose clarity rules. Use when compressing prompts, optimizing docs for LLM context, or writing clear technical prose.
12file-converter
Convert & transform files - images (resize, format, HEIC), markdown (PDF/HTML), data (CSV/JSON/YAML/TOML/XML), SVG, base64, text encoding. Cross-platform, single & batch mode. This skill should be used when converting file formats, resizing images, generating PDFs from markdown, or transforming data between formats.
12brainstorm
Transform ideas into fully-formed designs through collaborative dialogue. This skill should be used when brainstorming features, exploring implementation approaches, designing system architecture, or when the user has a vague idea that needs refinement. Uses incremental validation with 200-300 word sections.
11