mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 22:46:36 +00:00
2dd60a0e37
7 structured documents covering stack, integrations, architecture, structure, conventions, testing, and concerns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
185 lines
7.6 KiB
Markdown
185 lines
7.6 KiB
Markdown
# Coding Conventions
|
|
|
|
**Analysis Date:** 2026-04-01
|
|
|
|
## Naming Patterns
|
|
|
|
**Files:**
|
|
- `snake_case.py` throughout — `coordinates.py`, `chunk_manager.py`, `processor.py`
|
|
- Test files: `test_<module>.py` prefix — `test_coordinates.py`, `test_api_flights.py`
|
|
- Schema files grouped by domain — `flight.py`, `vo.py`, `graph.py`, `metric.py`
|
|
|
|
**Classes:**
|
|
- `PascalCase` for 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`/`Response` for API boundaries — `FlightCreateRequest`, `FlightDetailResponse`, `BatchUpdateResponse`
|
|
- Config classes suffixed with `Config` or `Settings` — `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); the `async def` keyword 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 by `flight_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 in `pyproject.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) or `UP` (pyupgrade) rules enabled
|
|
|
|
**Type Hints:**
|
|
- Type hints on all public method signatures — parameters and return types
|
|
- `from __future__ import annotations` used in modules with complex forward references (`processor.py`, `flight.py`, `config.py`)
|
|
- `Optional[T]` used alongside `T | None` syntax (inconsistency — both styles present)
|
|
- `dict[str, X]` and `list[X]` lowercase generics (Python 3.9+ style)
|
|
- Untyped component slots use `= None` without annotation — e.g., `self._vo = None` — intentional for lazy init
|
|
|
|
## Module Docstrings
|
|
|
|
Every source module starts with a one-line docstring naming the component:
|
|
```python
|
|
"""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):**
|
|
1. Standard library (`__future__`, `asyncio`, `logging`, `math`, `abc`)
|
|
2. Third-party (`fastapi`, `numpy`, `cv2`, `pydantic`, `sqlalchemy`)
|
|
3. 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 Geofences` inside a method body in `processor.py`
|
|
|
|
## ABC/Interface Pattern
|
|
|
|
All major processing components define an Abstract Base Class in the same module:
|
|
```python
|
|
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:
|
|
```python
|
|
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:
|
|
```python
|
|
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()` and `model_validate_json()` used (Pydantic v2 API)
|
|
- Config classes inherit from `pydantic_settings.BaseSettings` with `SettingsConfigDict(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 — `OriginNotSetError` in `coordinates.py`, `QueueFullError` / `ValidationError` in `pipeline.py`
|
|
- Inherit directly from `Exception` (no base project exception class)
|
|
|
|
**In async pipeline code:**
|
|
- `try/except Exception as exc` with `logger.warning(...)` — swallowed errors are logged, not re-raised:
|
|
```python
|
|
except Exception as exc:
|
|
logger.warning("VO failed for frame %d: %s", frame_id, exc)
|
|
```
|
|
- HTTP layer raises `HTTPException` from FastAPI — no custom HTTP exception hierarchy
|
|
|
|
**Type-ignore suppressions:**
|
|
- `# type: ignore[union-attr]` used on SQLAlchemy `result.rowcount` accesses in `repository.py`
|
|
- `# type: ignore` on two FastAPI dependency parameters in `flights.py` (multipart form endpoint)
|
|
|
|
## Logging
|
|
|
|
**Framework:** Python stdlib `logging`
|
|
|
|
**Setup per module:**
|
|
```python
|
|
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:
|
|
```python
|
|
# ---------------------------------------------------------------------------
|
|
# State Machine
|
|
# ---------------------------------------------------------------------------
|
|
```
|
|
and
|
|
```python
|
|
# =========================================================
|
|
# 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 `None` on not-found
|
|
- Sync methods return `bool` for success/failure of CRUD operations
|
|
- `Optional[T]` / `T | None` used for nullable returns consistently
|
|
|
|
## Comments
|
|
|
|
**Inline comments** used liberally to explain non-obvious math or mock logic:
|
|
```python
|
|
# 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:
|
|
```python
|
|
# ---- 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*
|