# Python Style Guide (PEP 8) & Docstring Conventions (PEP 257)

This document captures the core Python style and documentation conventions, with links to the authoritative Python Enhancement Proposals.

## Why It Matters

Consistent style and thorough documentation:

- Improves readability & onboarding speed
- Reduces code review friction
- Helps static analysis (linters, type checkers) produce reliable results
- Enables automated formatting tools (e.g., `black`, `ruff`) to run cleanly

---

## PEP 8: Style Guide for Python Code

Authoritative reference: [PEP 8](https://peps.python.org/pep-0008/)

### Core Principles

1. Readability & consistency over cleverness.
2. Explicit is better than implicit.
3. Practicality beats purity (prefer consistent adoption over sporadic perfection).

### Key Rules (Operational Cheat Sheet)

- Indentation: 4 spaces. Never tabs.
- Line length: Target 79 chars (72 for standalone comments/docstrings). OK to exceed for long URLs.
- Encoding: UTF-8 (default). Avoid non-ASCII unless necessary.
- Imports order: standard library, third-party, local. Separate groups by a blank line.
- Import style: Prefer explicit imports. Avoid `from module import *`.
- Naming:
  - Packages/modules: `lower_snake_case`
  - Classes: `CapWords`
  - Functions/methods: `lower_snake_case`
  - Variables: `lower_snake_case`
  - Constants: `UPPER_SNAKE_CASE`
- Whitespace:
  - One space after comma, colon, semicolon.
  - No space directly inside parentheses/brackets/braces.
  - Spaces around binary operators (`a + b`, `x == y`).
  - For alignment vs readability conflicts, prioritize readability.
- Blank lines:
  - 2 between top-level class/function definitions.
  - 1 between methods inside a class.
- Expressions & comparisons:
  - Use `is` / `is not` for `None` and booleans (`if x is None:`).
  - Avoid `== None`; prefer identity checks.
  - Use `x is not None` instead of truthiness when sentinel is meaningful.
- String formatting: Prefer f-strings. Avoid `%` formatting in new code.
- Trailing whitespace: Remove it; CI may flag.
- Inline comments: Keep concise; avoid obvious restatements.
- Block comments: Wrap at ~72 chars.
- Shebang: Only for executable scripts (e.g., CLI entrypoints). Not for library modules.
- Comprehensive logging: Use `logging` module—never `print()` for runtime progress.

### Best Practices

- Type hints: Required for all public functions, module-level interfaces, and data containers.
- Dataclasses: Use for simple structured data; prefer `frozen=True` when immutability is helpful.
- Path handling: Use `pathlib.Path` over `os.path`.
- Secrets: Never inline; always sourced from environment variables or secret stores.
- Structural Pattern Matching: Use judiciously (Python 3.10+); ensure clarity outweighs complexity.

### Linters & Formatters

- `ruff`: Fast linting. Config enforces PEP 8 plus additional rules.
- `flake8`: Secondary lint (if enabled).
- `black`: Auto-format; run before commit to minimize diffs.
- `isort`: Organize imports if configured (ruff may handle).

---

## PEP 257: Docstring Conventions

Authoritative reference: [PEP 257](https://peps.python.org/pep-0257/)

### General Principles

Docstrings describe the public contract: what a function/class/module does—not how (unless non-obvious decisions need rationale). They should enable correct usage without diving into implementation details.

### Placement

- First statement in a module, function, class, or method.
- Triple double quotes: `"""Docstring"""`.

### One-Line Docstrings

- Use for trivial functions returning simple values.
- Format: `"""Return the active record ID."""`
- No blank line before closing quotes.

### Multi-Line Docstrings

Structure:

```text
"""Imperative summary sentence ending with a period.

Extended description (optional). Include context, constraints, or rationale.

Args:
    param1 (str): Meaning and constraints.
    param2 (Path): Must exist; created if missing.

Returns:
    int: Count of processed records.

Raises:
    ValueError: On invalid configuration.
    RuntimeError: On unrecoverable I/O conflict.
"""
```

### Style Notes

- Summary line: Imperative mood ("Compute", "Create", "Return").
- Blank line after summary if additional content follows.
- Wrap at ~72 chars.
- Describe parameters, return types, and exceptions if non-trivial.
- Avoid duplication with type hints: focus on semantics, constraints, invariants.

### Class Docstrings

- State purpose, usage pattern, and important attributes.
- Document any required lifecycle (e.g., must call `.close()`).

### Module Docstrings

Include:

- High-level purpose
- Key public functions/classes
- External integration notes

### When to Omit

Private helpers with obvious functionality may omit docstrings but include brief inline comments if logic is subtle. Prefer docstrings if the function is more than a trivial one-liner.

### Best Practices

- Capture security-sensitive rationale: e.g., "Password never logged; retrieved from secret manager.".
- Mark deprecated functions with clear guidance.
- For new subsystems: include data flow summary and link to architecture docs (if present).

### Anti-Patterns

- Copy-pasted parameter lists that drift from signature.
- Docstrings that merely restate the function name.
- Logging, TODOs, or implementation detail dumps.
- Massive usage examples where a short snippet suffices.

### Good Examples

Function example:

```python
def load_data(path: Path) -> list[str]:
    """Load a data file and return raw line items.

    Strips trailing newline characters and ignores empty lines.

    Args:
        path (Path): Path to the data file. Must exist.

    Returns:
        list[str]: Ordered list of non-empty lines.

    Raises:
        FileNotFoundError: If the file does not exist.
    """
```

Class example:

```python
class DataExporter:
    """Coordinate generation and export of data files.

    Workflow:
        1. Generate data components.
        2. Package into output format.
        3. If enabled, transfer to remote destination.

    Attributes:
        output_dir (Path): Local directory where files are written.
        logger (Logger): Module-level logger for structured output.
    """
```

---

## Tooling Workflow

1. Write docstrings before implementing complex logic (design aid).
2. Run linters locally (`ruff check .` or `flake8 .`).
3. Maintain alignment between signatures & docstrings—update both on change.
4. Capture design decisions in `docs/` (see Decision Log template).

---

## Quick Reference Summary Table

| Topic | Rule |
|-------|------|
| Indentation | 4 spaces |
| Line length | 79 code / 72 docs |
| Imports | stdlib, third-party, local |
| Naming | module_func_var `snake_case`; Class `CapWords`; constants UPPER |
| Docstring summary | Imperative, one sentence |
| Type hints | Required for public APIs |
| Logging | `logging`, no prints |
| Path | `pathlib.Path` |
| String formatting | f-strings |
| Secret handling | env/secret store, never logged |

---

## References

- [PEP 8](https://peps.python.org/pep-0008/)
- [PEP 257](https://peps.python.org/pep-0257/)
- [Black](https://github.com/psf/black)
- [Ruff](https://docs.astral.sh/ruff/)
- [isort](https://pycqa.github.io/isort/)

---

**Version:** 1.0
