7 structured documents covering stack, integrations, architecture, structure, conventions, testing, and concerns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.6 KiB
Coding Conventions
Analysis Date: 2026-04-01
Naming Patterns
Files:
snake_case.pythroughout —coordinates.py,chunk_manager.py,processor.py- Test files:
test_<module>.pyprefix —test_coordinates.py,test_api_flights.py - Schema files grouped by domain —
flight.py,vo.py,graph.py,metric.py
Classes:
PascalCasefor all classes —FlightProcessor,CoordinateTransformer,FactorGraphOptimizer- Interface/ABC classes prefixed with
I—IImageMatcher,ISequentialVisualOdometry,IModelManager - Error/Exception classes suffixed with
Error—OriginNotSetError,QueueFullError,ValidationError - Pydantic schema classes named after the concept they represent, suffixed with
Request/Responsefor API boundaries —FlightCreateRequest,FlightDetailResponse,BatchUpdateResponse - Config classes suffixed with
ConfigorSettings—DatabaseConfig,RecoveryConfig,AppSettings
Functions/Methods:
snake_case—set_enu_origin,compute_relative_pose,retrieve_candidate_tiles- Async functions not distinguished by name from sync (no
async_prefix); theasync defkeyword is the signal - Private/internal methods prefixed with single underscore —
_init_flight,_cleanup_flight,_publish_frame_result
Variables:
snake_case—flight_id,rel_pose,chunk_mgr- Per-flight in-memory dicts named
_<plural_noun>keyed byflight_id—_origins,_flight_states,_prev_images,_flight_cameras - Constants: uppercase not consistently enforced; numeric magic values appear in comments (e.g.,
111319.5)
Type Parameters:
PascalCase— standard Python typing conventions
Code Style
Formatter/Linter:
ruff— configured inpyproject.toml- Line length: 100 characters (
line-length = 100) - Target: Python 3.11 (
target-version = "py311") - Ruff rule sets active:
E(pycodestyle errors),F(Pyflakes),I(isort),W(pycodestyle warnings) - No
B(flake8-bugbear) orUP(pyupgrade) rules enabled
Type Hints:
- Type hints on all public method signatures — parameters and return types
from __future__ import annotationsused in modules with complex forward references (processor.py,flight.py,config.py)Optional[T]used alongsideT | Nonesyntax (inconsistency — both styles present)dict[str, X]andlist[X]lowercase generics (Python 3.9+ style)- Untyped component slots use
= Nonewithout annotation — e.g.,self._vo = None— intentional for lazy init
Module Docstrings
Every source module starts with a one-line docstring naming the component:
"""Coordinate Transformer (Component F13)."""
"""Core Flight Processor — Full Processing Pipeline (Stage 10)."""
"""Sequential Visual Odometry (Component F07)."""
Test files also carry docstrings stating what they test and which component ID they cover.
Import Organization
Order (enforced by ruff I rules):
- Standard library (
__future__,asyncio,logging,math,abc) - Third-party (
fastapi,numpy,cv2,pydantic,sqlalchemy) - Internal project (
gps_denied.*)
Internal imports:
- Always absolute —
from gps_denied.core.coordinates import CoordinateTransformer - Schema imports collected into explicit multi-line blocks at top of file
- Lazy in-function imports used to avoid circular imports:
from gps_denied.schemas import Geofencesinside a method body inprocessor.py
ABC/Interface Pattern
All major processing components define an Abstract Base Class in the same module:
class ISequentialVisualOdometry(ABC):
@abstractmethod
def compute_relative_pose(...) -> RelativePose | None: ...
class SequentialVisualOdometry(ISequentialVisualOdometry):
...
Components that depend on another component accept the interface type in __init__, enabling mock injection:
class SequentialVisualOdometry(ISequentialVisualOdometry):
def __init__(self, model_manager: IModelManager): ...
This pattern appears in: vo.py, models.py, rotation.py.
FlightProcessor uses a post-construction injection method instead:
def attach_components(self, vo=None, gpr=None, metric=None, ...): ...
Pydantic Schema Conventions
- All schemas inherit from
pydantic.BaseModel - Validation constraints use
Field(..., ge=..., le=..., gt=..., min_length=..., max_length=...) - Default values use
Field(default_factory=...)for mutable defaults model_dump()andmodel_validate_json()used (Pydantic v2 API)- Config classes inherit from
pydantic_settings.BaseSettingswithSettingsConfigDict(env_prefix=...) - Grouped into separate schema files by domain:
flight.py,vo.py,graph.py,metric.py,gpr.py,chunk.py,rotation.py,satellite.py,events.py,image.py,model.py
Error Handling
Custom exceptions:
- Defined in the module where they originate —
OriginNotSetErrorincoordinates.py,QueueFullError/ValidationErrorinpipeline.py - Inherit directly from
Exception(no base project exception class)
In async pipeline code:
try/except Exception as excwithlogger.warning(...)— swallowed errors are logged, not re-raised:except Exception as exc: logger.warning("VO failed for frame %d: %s", frame_id, exc)- HTTP layer raises
HTTPExceptionfrom FastAPI — no custom HTTP exception hierarchy
Type-ignore suppressions:
# type: ignore[union-attr]used on SQLAlchemyresult.rowcountaccesses inrepository.py# type: ignoreon two FastAPI dependency parameters inflights.py(multipart form endpoint)
Logging
Framework: Python stdlib logging
Setup per module:
import logging
logger = logging.getLogger(__name__)
Usage patterns:
logger.warning(...)for recoverable failures (VO failure, drift correction failure)logger.info(...)for state machine transitions:"Flight %s → LOST at frame %d"logger.debug(...)for high-frequency data: optimization convergence results
Section Separators
Long modules use visual comment dividers to delineate sections:
# ---------------------------------------------------------------------------
# State Machine
# ---------------------------------------------------------------------------
and
# =========================================================
# process_frame — central orchestration
# =========================================================
Function Design
Size: Methods tend to be 10-40 lines; process_frame is intentionally longer (~80 lines) as the central orchestrator.
Parameters: Typed, ordered: self, required positional, optional keyword with defaults.
Return Values:
- Async methods return domain schema objects or
Noneon not-found - Sync methods return
boolfor success/failure of CRUD operations Optional[T]/T | Noneused for nullable returns consistently
Comments
Inline comments used liberally to explain non-obvious math or mock logic:
# 111319.5 meters per degree at equator
# FAKE Math for mockup:
# Very rough scaling: assume 1 pixel is ~0.1 meter
Section-level comments label major logical steps inside long methods:
# ---- 1. Visual Odometry (frame-to-frame) ----
# ---- 2. State Machine transitions ----
Module Design
Exports: No explicit __all__ in any module; consumers import directly from sub-modules.
Barrel Files: src/gps_denied/schemas/__init__.py re-exports common types (GPSPoint, CameraParameters, Geofences, Polygon). Other packages use direct imports.
Convention analysis: 2026-04-01