Skip to content

Commit 1e6b6a0

Browse files
refactor: improve Pydantic model definitions and streamline imports in S1 RTC module
1 parent daebb0a commit 1e6b6a0

3 files changed

Lines changed: 17 additions & 21 deletions

File tree

src/eopf_geozarr/data_api/s1_rtc.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Pydantic-zarr integrated models for Sentinel-1 GRD γ0T RTC GeoZarr stores.
2+
Pydantic-zarr integrated models for Sentinel-1 GRD gamma0T RTC GeoZarr stores.
33
44
Uses the pyz.v3 GroupSpec/ArraySpec with TypedDict members to enforce strict
55
structure validation — same pattern as s2.py (which uses pyz.v2 for Zarr V2).
@@ -35,7 +35,9 @@
3535

3636
from pydantic import BaseModel, Field, model_validator
3737
from typing_extensions import TypedDict
38-
from zarr_cm import geo_proj, multiscales as multiscales_cm, spatial as spatial_cm
38+
from zarr_cm import geo_proj
39+
from zarr_cm import multiscales as multiscales_cm
40+
from zarr_cm import spatial as spatial_cm
3941

4042
from eopf_geozarr.data_api.geozarr.common import DatasetAttrs
4143
from eopf_geozarr.pyz.v3 import ArraySpec, GroupSpec
@@ -59,7 +61,7 @@
5961
# ============================================================================
6062

6163

62-
class S1RtcOrbitGroupAttrs(BaseModel, extra="allow"):
64+
class S1RtcOrbitGroupAttrs(BaseModel):
6365
"""Attributes for an orbit-direction group (ascending or descending).
6466
6567
Carries the three GeoZarr conventions plus proj:/spatial:/multiscales metadata.
@@ -71,7 +73,7 @@ class S1RtcOrbitGroupAttrs(BaseModel, extra="allow"):
7173
spatial_dimensions: list[str] = Field(alias="spatial:dimensions")
7274
spatial_bbox: list[float] = Field(alias="spatial:bbox")
7375

74-
model_config = {"populate_by_name": True, "serialize_by_alias": True}
76+
model_config = {"extra": "allow", "populate_by_name": True, "serialize_by_alias": True}
7577

7678
@model_validator(mode="after")
7779
def validate_zarr_conventions(self) -> Self:
@@ -108,13 +110,13 @@ def validate_spatial_bbox(self) -> Self:
108110
return self
109111

110112

111-
class S1RtcResolutionAttrs(BaseModel, extra="allow"):
112-
"""Attributes for a resolution-level group (r10m, r20m, )."""
113+
class S1RtcResolutionAttrs(BaseModel):
114+
"""Attributes for a resolution-level group (r10m, r20m, ...)."""
113115

114116
spatial_shape: list[int] = Field(alias="spatial:shape")
115117
spatial_transform: list[float] = Field(alias="spatial:transform")
116118

117-
model_config = {"populate_by_name": True, "serialize_by_alias": True}
119+
model_config = {"extra": "allow", "populate_by_name": True, "serialize_by_alias": True}
118120

119121
@model_validator(mode="after")
120122
def validate_shape(self) -> Self:
@@ -131,14 +133,14 @@ def validate_transform(self) -> Self:
131133
return self
132134

133135

134-
class S1RtcConditionsAttrs(BaseModel, extra="allow"):
136+
class S1RtcConditionsAttrs(BaseModel):
135137
"""Attributes for the conditions group."""
136138

137139
proj_code: str = Field(alias="proj:code")
138140
spatial_dimensions: list[str] = Field(alias="spatial:dimensions")
139141
spatial_transform: list[float] = Field(alias="spatial:transform")
140142

141-
model_config = {"populate_by_name": True, "serialize_by_alias": True}
143+
model_config = {"extra": "allow", "populate_by_name": True, "serialize_by_alias": True}
142144

143145

144146
# ============================================================================
@@ -207,21 +209,17 @@ def border_mask(self) -> ArraySpec[Any]:
207209
class S1RtcOverviewResolutionDataset(
208210
GroupSpec[S1RtcResolutionAttrs, S1RtcOverviewResolutionMembers] # type: ignore[type-var]
209211
):
210-
"""An overview resolution dataset (r20mr720m): data variables only."""
212+
"""An overview resolution dataset (r20m-r720m): data variables only."""
211213

212214

213-
class S1RtcConditionsGroup(
214-
GroupSpec[S1RtcConditionsAttrs, dict[str, ArraySpec[Any]]] # type: ignore[type-var]
215-
):
215+
class S1RtcConditionsGroup(GroupSpec[S1RtcConditionsAttrs, dict[str, ArraySpec[Any]]]):
216216
"""Time-invariant condition arrays, keyed by name (e.g. gamma_area_008)."""
217217

218218
@model_validator(mode="after")
219219
def validate_has_gamma_area(self) -> Self:
220220
"""At least one gamma_area_* array should be present."""
221221
if not any(k.startswith("gamma_area_") for k in self.members):
222-
raise ValueError(
223-
"Conditions group must contain at least one 'gamma_area_*' array"
224-
)
222+
raise ValueError("Conditions group must contain at least one 'gamma_area_*' array")
225223
return self
226224

227225

tests/conftest.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
optimized_geozarr_example_paths = tuple(
2222
pathlib.Path("tests/_test_data/optimized_geozarr_examples").glob("*.json")
2323
)
24-
s1_rtc_example_json_paths = tuple(
25-
pathlib.Path("tests/_test_data/s1_rtc_examples").glob("*.json")
26-
)
24+
s1_rtc_example_json_paths = tuple(pathlib.Path("tests/_test_data/s1_rtc_examples").glob("*.json"))
2725

2826

2927
def read_json(path: pathlib.Path) -> dict[str, object]:

tests/test_data_api/test_s1_rtc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_s1_rtc_r10m_has_data_arrays(s1_rtc_json_example: dict[str, object]) ->
4646

4747

4848
def test_s1_rtc_overview_levels(s1_rtc_json_example: dict[str, object]) -> None:
49-
"""Test that overview levels r20mr720m exist and have vv/vh/border_mask."""
49+
"""Test that overview levels r20m-r720m exist and have vv/vh/border_mask."""
5050
model = S1RtcRoot(**s1_rtc_json_example)
5151
orbit = model.descending
5252
for level in ("r20m", "r60m", "r120m", "r360m", "r720m"):
@@ -123,7 +123,7 @@ def test_s1_rtc_rejects_conditions_without_gamma_area(
123123
cond_members = data["members"]["descending"]["members"]["conditions"]["members"]
124124
# Replace all keys with non-gamma_area names
125125
data["members"]["descending"]["members"]["conditions"]["members"] = {
126-
"some_other": list(cond_members.values())[0]
126+
"some_other": next(iter(cond_members.values()))
127127
}
128128
with pytest.raises(Exception, match="gamma_area"):
129129
S1RtcRoot(**data)

0 commit comments

Comments
 (0)