Skip to content

Commit 6dee183

Browse files
planet.jeroenclaude
andcommitted
refactor: ports ABC→Protocol, session restore logging, narrower exception handling
- Convert all port interfaces from ABC to runtime_checkable Protocol (structural typing per documented architecture) - Add logging.warning for failed layer restores in session load - Narrow except Exception to specific types in satellite table computation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd83418 commit 6dee183

File tree

4 files changed

+19
-25
lines changed

4 files changed

+19
-25
lines changed

packages/core/src/humeris/ports/__init__.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,30 @@
55
66
Adapters implement these to handle different file formats.
77
"""
8-
from abc import ABC, abstractmethod
9-
from typing import Any
8+
from typing import Any, Protocol, runtime_checkable
109

1110

12-
class SimulationReader(ABC):
11+
@runtime_checkable
12+
class SimulationReader(Protocol):
1313
"""Port for reading simulation template data."""
1414

15-
@abstractmethod
1615
def read_simulation(self, path: str) -> dict[str, Any]:
1716
"""Read and parse a simulation file."""
1817
...
1918

20-
@abstractmethod
2119
def extract_template_entity(self, sim_data: dict, entity_name: str) -> dict:
2220
"""Extract a named entity to use as a template."""
2321
...
2422

25-
@abstractmethod
2623
def extract_earth_entity(self, sim_data: dict) -> dict:
2724
"""Extract the Earth entity."""
2825
...
2926

3027

31-
class SimulationWriter(ABC):
28+
@runtime_checkable
29+
class SimulationWriter(Protocol):
3230
"""Port for writing simulation output data."""
3331

34-
@abstractmethod
3532
def write_simulation(self, sim_data: dict, path: str) -> None:
3633
"""Write simulation data to output file."""
3734
...

packages/core/src/humeris/ports/export.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
Adapters implement this to export satellite positions in various formats
77
(CSV, GeoJSON, etc.).
88
"""
9-
from abc import ABC, abstractmethod
109
from datetime import datetime
10+
from typing import Protocol, runtime_checkable
1111

1212
from humeris.domain.constellation import Satellite
1313

1414

15-
class SatelliteExporter(ABC):
15+
@runtime_checkable
16+
class SatelliteExporter(Protocol):
1617
"""Port for exporting satellite data to file."""
1718

18-
@abstractmethod
1919
def export(
2020
self,
2121
satellites: list[Satellite],

packages/core/src/humeris/ports/orbital_data.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,25 @@
55
66
Adapters handle the actual HTTP/API calls.
77
"""
8-
from abc import ABC, abstractmethod
9-
from typing import Any
8+
from typing import Any, Protocol, runtime_checkable
109

1110

12-
class OrbitalDataSource(ABC):
11+
@runtime_checkable
12+
class OrbitalDataSource(Protocol):
1313
"""Port for fetching orbital element data from external sources."""
1414

15-
@abstractmethod
1615
def fetch_group(self, group_name: str) -> list[dict[str, Any]]:
1716
"""Fetch OMM records for a named satellite group."""
1817
...
1918

20-
@abstractmethod
2119
def fetch_by_name(self, name: str) -> list[dict[str, Any]]:
2220
"""Fetch OMM records matching a satellite name."""
2321
...
2422

25-
@abstractmethod
2623
def fetch_by_catnr(self, catalog_number: int) -> list[dict[str, Any]]:
2724
"""Fetch OMM record for a NORAD catalog number."""
2825
...
2926

30-
@abstractmethod
3127
def fetch_satellites(self, group: str | None = None,
3228
name: str | None = None,
3329
catnr: int | None = None) -> list:

packages/pro/src/humeris/adapters/viewer_server.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,12 +1234,12 @@ def get_satellite_table(self, layer_id: str) -> dict[str, Any]:
12341234
plane = idx // sats_per_plane if sats_per_plane > 0 else 0
12351235
try:
12361236
beta_deg = compute_beta_angle(state.raan_rad, state.inclination_rad, self.epoch)
1237-
except Exception:
1237+
except (ValueError, ArithmeticError, ZeroDivisionError):
12381238
beta_deg = 0.0
12391239
try:
12401240
es = compute_eclipse_statistics(state, self.epoch, _DEFAULT_DURATION, _DEFAULT_STEP)
12411241
eclipse_pct = round(es.eclipse_fraction * 100.0, 1)
1242-
except Exception:
1242+
except (ValueError, ArithmeticError, ZeroDivisionError):
12431243
eclipse_pct = 0.0
12441244
rows.append({
12451245
"_sat_idx": idx,
@@ -1385,7 +1385,8 @@ def _restore_layers(self, layers_data: list[dict[str, Any]]) -> int:
13851385
)
13861386
restored_layers.append(layer_id)
13871387
restored += 1
1388-
except (KeyError, TypeError, ValueError):
1388+
except (KeyError, TypeError, ValueError) as e:
1389+
logging.warning("Failed to restore layer %r: %s", layer_data.get("name", "?"), e)
13891390
restored_layers.append("")
13901391
else:
13911392
restored_layers.append("")
@@ -1412,8 +1413,8 @@ def _restore_layers(self, layers_data: list[dict[str, Any]]) -> int:
14121413
)
14131414
restored_layers[idx] = gs_lid
14141415
restored += 1
1415-
except (KeyError, TypeError, ValueError):
1416-
pass
1416+
except (KeyError, TypeError, ValueError) as e:
1417+
logging.warning("Failed to restore ground station %r: %s", params.get("name", "?"), e)
14171418

14181419
# Pass 2: Restore analysis layers using source constellation reference
14191420
for idx, layer_data in enumerate(layers_data):
@@ -1451,8 +1452,8 @@ def _restore_layers(self, layers_data: list[dict[str, Any]]) -> int:
14511452
sat_names=source_sat_names,
14521453
)
14531454
restored += 1
1454-
except (KeyError, TypeError, ValueError, ArithmeticError):
1455-
pass
1455+
except (KeyError, TypeError, ValueError, ArithmeticError) as e:
1456+
logging.warning("Failed to restore analysis layer %r: %s", layer_data.get("name", "?"), e)
14561457

14571458
return restored
14581459

0 commit comments

Comments
 (0)