# Naming Conventions ## Files - **snake_case** for all Python files: `database.py`, `explorer_helpers.py`, `motion_cache.py` - **PascalCase** NOT used for files ## Functions - **snake_case**: `get_svd_vectors()`, `compute_party_coords()`, `build_scatter_trace()` - Private helpers prefixed with `_`: `_get_window_data()` ## Classes - **PascalCase**: `MotionDatabase`, `Config` - **Dataclass pattern** for Config: `@dataclass` decorator with typed fields ## Variables - **snake_case**: `party_map`, `mp_name`, `svd_vectors`, `party_centroids` - **CONSTANT_SNAKE_CASE** for module-level constants: `PARTY_COLOURS`, `DEFAULT_WINDOW` ## Module-Level Exports - **Singleton instance**: `db = MotionDatabase()` at module bottom (not class-level) - **Config instance**: `config = Config(...)` at module bottom - **Dicts**: `PARTY_COLOURS` exported from `config.py` --- # Error Handling ## Known Patterns 1. **Bare except with pass** (ANTI-PATTERN - see anti-patterns.yaml) ```python except: pass # database.py:47 ``` 2. **Graceful degradation**: catch specific exceptions, fall back to default ```python try: result = compute_svd() except ImportError: result = DEFAULT_SVD ``` 3. **Optional dependency fallbacks**: ```python try: import umap use_umap = True except ImportError: use_umap = False ``` 4. **Nested exception handling** (ANTI-PATTERN - see anti-patterns.yaml): ```python try: ... except Exception: try: ... except Exception: pass ``` ## Rules - Never use bare `except:` — always specify exception type - Never swallow exceptions silently — log or return a sensible default - For optional deps, use `ImportError` or `ModuleNotFoundError` explicitly - Avoid nested try/except blocks --- # Code Organization ## Singleton Pattern Each module owns one shared instance: ```python # database.py db = MotionDatabase() # config.py config = Config(...) PARTY_COLOURS = {...} ``` ## Pure Functions in Helpers `explorer_helpers.py` contains only pure functions (no IO, no Streamlit calls): ```python def compute_party_coords(svd_vectors, party_map): """Pure: no side effects, no imports from this module""" ... def build_scatter_trace(df, color_col): """Pure: returns Plotly trace dict""" ... ``` ## Cached Data Loaders Use `@st.cache_data` for expensive data loading: ```python @st.cache_data def load_svd_vectors(window: str) -> pd.DataFrame: return db.get_svd_vectors(window) ``` ## Dataclass Config ```python @dataclass class Config: db_path: str = "data/stemwijzer.duckdb" default_window: str = "2023" party_colours: dict = field(default_factory=lambda: PARTY_COLOURS) ``` --- # Imports ## Ordering (convention) 1. Standard library 2. Third-party (streamlit, ibis, plotly, sklearn, umap) 3. Local/relative imports ## Avoid - Wildcard imports (`from module import *`) - Circular imports (ensure dependency direction: helpers → database → config)