# Python Rules

Full coding rules for this stack. Read this in full before writing or modifying any Python code in this project — not just once, keep applying it to every edit in the session, not only the first.

---

## Project Structure

```
project/
├── main.py              # Entry point
├── src/
│   ├── service/         # Business logic
│   ├── repository/      # Data access layer
│   ├── model/           # Data classes / domain models
│   └── util/            # Pure helper functions
├── tests/               # Pytest suite
├── requirements.txt
└── pyproject.toml
```

---

## Python Rules

- Use **type hints** on all function signatures and return types.
- Follow **PEP 8** and keep functions small and single-purpose.
- Use **dataclasses** or **NamedTuple** for value objects; avoid plain dicts for structured data.
- Prefer **pathlib** over `os.path`; prefer `with` statements for file/resource handling.
- Raise specific exceptions — never `raise Exception("message")`.

---

## Testing Rules

- Use **Pytest** for all tests.
- Name test functions `test_<what>_<expected_outcome>`.
- Use `pytest.raises` to assert exceptions.

---

## Naming Conventions

| Element | Convention | Example |
|---------|-----------|---------|
| Module/package | snake_case | `user_service.py` |
| Class | PascalCase | `UserService` |
| Function/variable | snake_case | `find_by_id`, `user_id` |
| Constant | UPPER_SNAKE_CASE | `MAX_RETRY` |

---

## Common Anti-Patterns to Avoid

- ❌ Bare `except:` — always catch a specific exception type
- ❌ Mutable default arguments (`def f(x=[])`) — use `None` sentinel instead
- ❌ Global state — pass dependencies explicitly
- ❌ Returning `None` implicitly on error paths — raise or return a typed result
