FX-Technical

Code Style

# Code Style ## Type hints - Annotate all function parameters and return types without exception. - Use modern built-in type syntax: `list[str]`, `dict[str, Any]`, `str | None` — never `Optional`, `Union`, `List`, `Dict`, or `Tuple` from `typing`. `Any` is acceptable when the type is genuinely dynamic. ## Docstrings — Google style - Multi-line format: short imperative summary sentence, blank line, then sections. - `Args:` — one line per parameter, no type (already in the signature). - `Returns:` — `ReturnType: Description`. For union returns with distinct cases, use a bullet list under the type. - `Raises:` — `ExceptionType: Condition that triggers it.` - One-liner docstring acceptable for trivial or self-evident methods. - Class docstring covers the class purpose; `__init__` docstring covers non-obvious constructor parameters. ## No inline comments - Write no `#` comments. Name things so the code reads without annotation. ## Strings - Double quotes throughout. ## Imports - Order: stdlib → third-party → local. One blank line between groups. ## Naming - `snake_case` — functions, methods, variables, module names. - `PascalCase` — classes. - `UPPER_CASE` — module-level constants. - `_prefix` — private methods and attributes. ## Formatting - 4-space indentation. Not Black-formatted. No enforced line length; keep lines readable. - Multi-line expressions use standard 4-space continuation indent. ## Logging - Library modules: `logger = logging.getLogger(__name__)` plus an explicit `logger.setLevel(...)`, both at module level. - Entry-point scripts: `logging.basicConfig(level=..., format="%(levelname)s: %(message)s")` at module level. - `print()` acceptable in deploy and setup scripts for user-facing progress output. ## Error handling - Catch specific exceptions, never bare `except`. - Re-raise with context: `raise XError("...") from e`. - `RuntimeError` for internal invariant violations; `EnvironmentError` for missing env configuration. ## Patterns - Use `Path` from `pathlib` for all filesystem operations. - Apply `@staticmethod` to any method that does not access `self` or `cls`. - Use `@dataclass(frozen=True)` for configuration containers — immutability turns accidental mutation into a TypeError rather than a silent bug. - Use context managers (`with`) for all file I/O. - Use `@abstractmethod` on every method a subclass must override — turns a runtime `AttributeError` into a clear error at class definition time. - Use `Protocol` (from `typing`) for structural typing when callers should not be required to inherit from a base class. - Use `@classmethod` for alternative constructors instead of overloading `__init__` with optional parameters. - Use `functools.cached_property` for expensive computed attributes that are derived purely from `self` state and read more than once. - Define `__all__` in every `__init__.py` to declare the public API explicitly. ## Idioms - Prefer early returns (guard clauses) over nested conditionals — exit on invalid/edge cases at the top of the function. - Build collections with comprehensions, not imperative loops. Use generator expressions when the full list is not needed. - Use f-strings for all string interpolation — never `.format()` or `%`-style. - Use list comprehension