| name | python-pep8-style |
| description | Use this skill when writing Python code following PEP 8 and modern Python best practices. Provides comprehensive guidance on code layout, naming conventions, EAFP philosophy, type hints, exception handling, and pytest-based TDD. Covers all critical PEP 8 rules including indentation, imports, whitespace, documentation, and idiomatic Python patterns. Appropriate for any task involving .py files, Python code reviews, refactoring, or ensuring Python code quality. |
Python PEP 8 Style Guide Skill
This skill embeds PEP 8 (Python Enhancement Proposal 8) - the official style guide for Python code authored by Guido van Rossum, Barry Warsaw, and Alyssa Coghlan. It also incorporates modern Python best practices from the Google Python Style Guide and community standards.
When to Use This Skill
- Writing new Python code that should follow PEP 8 standards
- Reviewing Python code for style compliance
- Refactoring Python code to improve readability
- Learning idiomatic Python patterns
- Setting up Python project structure and tooling
Core Python Philosophy
The Zen of Python (PEP 20)
import this
EAFP vs LBYL
Python favors EAFP (Easier to Ask for Forgiveness than Permission) over LBYL (Look Before You Leap):
try:
value = data["key"]["nested"]
except KeyError:
value = default_value
if "key" in data and "nested" in data["key"]:
value = data["key"]["nested"]
else:
value = default_value
Structural Limits
| Element | Limit | Rationale |
|---|
| Line length | 79-88 chars | PEP 8: 79, Black: 88 |
| Function length | ≤25 lines | Single responsibility |
| Function parameters | ≤5 | Use dataclasses/kwargs for more |
| Nesting depth | ≤4 levels | Extract to functions |
| Import groups | 3 | stdlib → third-party → local |
Critical PEP 8 Rules
Code Layout
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction)
class MyClass:
pass
def my_function():
pass
Imports
import os
import sys
from pathlib import Path
import requests
from pydantic import BaseModel
from myapp.models import User
from myapp.utils import helpers
Naming Conventions
| Type | Convention | Example |
|---|
| Modules | lowercase_underscore | my_module.py |
| Packages | lowercase | mypackage |
| Classes | PascalCase | MyClass |
| Functions | snake_case | my_function() |
| Variables | snake_case | my_variable |
| Constants | UPPER_SNAKE_CASE | MAX_SIZE |
| Private | _leading_underscore | _internal_var |
| "Private" class | __double_leading | __mangled |
Whitespace
x = 1
y = x + 2
if x == 4:
print(x, y)
spam(ham[1], {eggs: 2})
foo = (0,)
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3]
def complex(real, imag=0.0):
return magic(r=real, i=imag)
def munge(sep: str = None): ...
Modern Python Features
Type Hints (PEP 484, 585)
from typing import Optional
from collections.abc import Sequence, Mapping
def process_items(
items: list[str],
config: dict[str, int] | None = None,
) -> dict[str, list[str]]:
"""Process items with optional configuration."""
...
from typing import Protocol
class Serializable(Protocol):
def to_dict(self) -> dict[str, Any]: ...
Dataclasses
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
username: str
email: str
created_at: datetime = field(default_factory=datetime.now)
is_active: bool = True
tags: list[str] = field(default_factory=list)
def __post_init__(self) -> None:
self.email = self.email.lower()
Context Managers
from contextlib import contextmanager
with open("file.txt") as f:
data = f.read()
@contextmanager
def database_transaction(connection):
transaction = connection.begin()
try:
yield transaction
transaction.commit()
except Exception:
transaction.rollback()
raise
TDD Workflow
1. Red Phase - Write Failing Test
import pytest
def test_user_creation_with_valid_email():
user = User(email="test@example.com", name="Test")
assert user.email == "test@example.com"
def test_user_creation_with_invalid_email_raises():
with pytest.raises(ValueError, match="Invalid email"):
User(email="invalid", name="Test")
2. Green Phase - Minimal Implementation
@dataclass
class User:
email: str
name: str
def __post_init__(self) -> None:
if "@" not in self.email:
raise ValueError("Invalid email format")
3. Refactor Phase - Improve While Green
import re
from dataclasses import dataclass
EMAIL_PATTERN = re.compile(r"^[\w\.-]+@[\w\.-]+\.\w+$")
@dataclass
class User:
email: str
name: str
def __post_init__(self) -> None:
self._validate_email()
self.email = self.email.lower()
def _validate_email(self) -> None:
if not EMAIL_PATTERN.match(self.email):
raise ValueError(f"Invalid email format: {self.email}")
Exception Handling
class UserNotFoundError(Exception):
"""Raised when a user cannot be found."""
def __init__(self, user_id: int) -> None:
self.user_id = user_id
super().__init__(f"User {user_id} not found")
def get_user(user_id: int) -> User:
try:
return database.fetch_user(user_id)
except DatabaseError as e:
raise UserNotFoundError(user_id) from e
try:
result = process_data(data)
except ValueError as e:
logger.warning("Invalid data: %s", e)
result = default_value
except (IOError, OSError) as e:
logger.error("IO error: %s", e)
raise ProcessingError("Failed to process") from e
Quality Checklist
Before delivering Python code, verify:
Code Style
Type Safety
Testing
Documentation
Modern Python
Essential Tooling
uv pip install ruff mypy pytest pytest-cov bandit
ruff check .
ruff format .
mypy .
pytest --cov=src
bandit -r src/
Reference Files
For detailed patterns and examples, see:
references/python-patterns.md - Comprehensive PEP 8 patterns
references/testing-patterns.md - pytest and TDD patterns
references/modern-python.md - Type hints, dataclasses, async
Template Files
Quick-start templates available in assets/templates/:
class_template.py - Standard class structure
service_class.py - Service object pattern
pytest_test.py - pytest test file template
cli_script.py - Command-line script template