Skip to content

Commit 6f2679c

Browse files
Refactor CLI output messages for consistency and clarity; enhance type hints in tests
1 parent 90234a4 commit 6f2679c

5 files changed

Lines changed: 46 additions & 48 deletions

File tree

eopf_geozarr/cli.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def info_command(args: argparse.Namespace) -> None:
120120
print(f"Total groups: {len(dt.children)}")
121121

122122
print("\nGroup structure:")
123-
for group_name, group in dt.children.items():
123+
for group_name, group in dt.groups.items():
124124
print(f" {group_name}:")
125125
if hasattr(group, "data_vars") and group.data_vars:
126126
print(f" Variables: {list(group.data_vars.keys())}")
@@ -221,14 +221,14 @@ def validate_command(args: argparse.Namespace) -> None:
221221
print(f"Non-compliant variables: {total_variables - compliant_variables}")
222222

223223
if compliance_issues:
224-
print(f"\n❌ Dataset is NOT GeoZarr compliant")
224+
print("\n❌ Dataset is NOT GeoZarr compliant")
225225
print(f"Issues found: {len(compliance_issues)}")
226226
if args.verbose:
227227
print("Detailed issues:")
228228
for issue in set(compliance_issues):
229229
print(f" - {issue}")
230230
else:
231-
print(f"\n✅ Dataset appears to be GeoZarr compliant")
231+
print("\n✅ Dataset appears to be GeoZarr compliant")
232232

233233
except Exception as e:
234234
print(f"❌ Error validating dataset: {e}")
@@ -316,7 +316,7 @@ def create_parser() -> argparse.ArgumentParser:
316316

317317

318318
def main() -> None:
319-
"""Main entry point for the CLI."""
319+
"""Execute main entry point for the CLI."""
320320
parser = create_parser()
321321

322322
if len(sys.argv) == 1:

eopf_geozarr/conversion/geozarr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ def write_geozarr_group(
518518
zarr_group = zarr.open_group(group_path, mode="r+", zarr_format=3)
519519
consolidate_metadata(zarr_group.store)
520520

521-
print(f" ✅ Metadata consolidated")
521+
print(" ✅ Metadata consolidated")
522522

523523
ds = xr.open_dataset(
524524
group_path, engine="zarr", zarr_format=3, decode_coords="all"
@@ -645,7 +645,7 @@ def create_geozarr_compliant_multiscales(
645645

646646
# Skip level 0 - native resolution is in the root group
647647
if level == 0:
648-
print(f"Skipping level 0 - native resolution is already in group 0")
648+
print("Skipping level 0 - native resolution is already in group 0")
649649
continue
650650

651651
print(f"\nCreating overview level {level} (1:{scale_factor} scale)...")

eopf_geozarr/tests/test_cli_e2e.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ class TestCLIEndToEnd:
1919
"""End-to-end CLI tests with real data."""
2020

2121
@pytest.fixture
22-
def temp_output_dir(self) -> str:
22+
def temp_output_dir(self): # type: ignore[no-untyped-def]
2323
"""Create a temporary directory for test outputs."""
2424
temp_dir = tempfile.mkdtemp()
2525
yield temp_dir
2626
shutil.rmtree(temp_dir)
2727

2828
@pytest.mark.slow
2929
@pytest.mark.network
30-
def test_cli_convert_real_sentinel2_data(self, temp_output_dir) -> None:
30+
def test_cli_convert_real_sentinel2_data(self, temp_output_dir: str) -> None:
3131
"""
3232
Test CLI conversion using real Sentinel-2 data from the notebook.
3333
@@ -111,8 +111,9 @@ def test_cli_convert_real_sentinel2_data(self, temp_output_dir) -> None:
111111

112112
# Verify info output contains expected information
113113
info_output = result_info.stdout
114-
assert "Groups found:" in info_output, "Info should list groups"
115-
assert "measurements/reflectance/r10m" in info_output, "Should find r10m group"
114+
assert "Total groups:" in info_output, "Info should show total groups count"
115+
assert "Group structure:" in info_output, "Info should show group structure"
116+
assert "measurements:" in info_output, "Should find measurements group"
116117

117118
# Test 3: CLI validate command
118119
print("\n=== Testing CLI validate command ===")
@@ -136,7 +137,7 @@ def test_cli_convert_real_sentinel2_data(self, temp_output_dir) -> None:
136137

137138
# Verify validation output
138139
validate_output = result_validate.stdout
139-
assert "GeoZarr compliance validation" in validate_output, "Should show validation header"
140+
assert "Validation Results:" in validate_output, "Should show validation header"
140141
assert "✅" in validate_output, "Should show successful validations"
141142

142143
# Test 4: Verify data structure and compliance
@@ -146,9 +147,8 @@ def test_cli_convert_real_sentinel2_data(self, temp_output_dir) -> None:
146147

147148
print("✅ All CLI end-to-end tests passed!")
148149

149-
def _verify_converted_data_structure(self, output_path, groups) -> None:
150+
def _verify_converted_data_structure(self, output_path: Path, groups: list[str]) -> None:
150151
"""Verify the structure and compliance of converted data."""
151-
152152
# Check each group was converted
153153
for group in groups:
154154
group_path = output_path / group.lstrip("/")
@@ -195,7 +195,7 @@ def _verify_converted_data_structure(self, output_path, groups) -> None:
195195
assert (
196196
"_ARRAY_DIMENSIONS" in ds["spatial_ref"].attrs
197197
), f"Missing _ARRAY_DIMENSIONS in spatial_ref for {group}"
198-
print(f" ✅ spatial_ref variable verified")
198+
print(" ✅ spatial_ref variable verified")
199199

200200
ds.close()
201201

@@ -204,11 +204,10 @@ def _verify_converted_data_structure(self, output_path, groups) -> None:
204204
print(f" Overview levels: {sorted([d.name for d in level_dirs])}")
205205

206206
if len(level_dirs) > 1:
207-
print(f" ✅ Multiscale structure created")
207+
print(" ✅ Multiscale structure created")
208208

209209
def test_cli_help_commands(self) -> None:
210210
"""Test that all CLI help commands work."""
211-
212211
# Test main help
213212
result = subprocess.run(
214213
["python", "-m", "eopf_geozarr", "--help"], capture_output=True, text=True

eopf_geozarr/tests/test_conversion.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Tests for the conversion module."""
22

3-
from unittest.mock import Mock, patch
3+
from unittest.mock import patch
44

55
import numpy as np
66
import pytest
@@ -20,7 +20,7 @@
2020
class TestUtilityFunctions:
2121
"""Test utility functions."""
2222

23-
def test_downsample_2d_array_block_averaging(self):
23+
def test_downsample_2d_array_block_averaging(self) -> None:
2424
"""Test downsampling with block averaging."""
2525
# Create a 4x4 array
2626
source_data = np.array(
@@ -40,7 +40,7 @@ def test_downsample_2d_array_block_averaging(self):
4040

4141
np.testing.assert_array_equal(result, expected)
4242

43-
def test_downsample_2d_array_subsampling(self):
43+
def test_downsample_2d_array_subsampling(self) -> None:
4444
"""Test downsampling with subsampling when block size is 1."""
4545
source_data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=float)
4646

@@ -52,27 +52,27 @@ def test_downsample_2d_array_subsampling(self):
5252

5353
np.testing.assert_array_equal(result, expected)
5454

55-
def test_calculate_aligned_chunk_size_perfect_divisor(self):
55+
def test_calculate_aligned_chunk_size_perfect_divisor(self) -> None:
5656
"""Test chunk size calculation with perfect divisor."""
5757
# 1000 dimension, want 256 chunks
5858
result = calculate_aligned_chunk_size(1000, 256)
5959
# Should find 250 as the largest divisor <= 256
6060
assert result == 250
6161
assert 1000 % result == 0
6262

63-
def test_calculate_aligned_chunk_size_larger_than_dimension(self):
63+
def test_calculate_aligned_chunk_size_larger_than_dimension(self) -> None:
6464
"""Test chunk size calculation when desired size is larger than dimension."""
6565
result = calculate_aligned_chunk_size(100, 256)
6666
assert result == 100
6767

68-
def test_calculate_aligned_chunk_size_no_perfect_divisor(self):
68+
def test_calculate_aligned_chunk_size_no_perfect_divisor(self) -> None:
6969
"""Test chunk size calculation when no perfect divisor exists."""
7070
# Prime number dimension
7171
result = calculate_aligned_chunk_size(97, 50)
7272
# Should return 1 as the only divisor when no good divisor is found
7373
assert result == 1
7474

75-
def test_is_grid_mapping_variable(self):
75+
def test_is_grid_mapping_variable(self) -> None:
7676
"""Test grid mapping variable detection."""
7777
# Create a dataset with a grid mapping variable
7878
ds = xr.Dataset(
@@ -86,10 +86,10 @@ def test_is_grid_mapping_variable(self):
8686
}
8787
)
8888

89-
assert is_grid_mapping_variable(ds, "spatial_ref") == True
90-
assert is_grid_mapping_variable(ds, "temperature") == False
89+
assert is_grid_mapping_variable(ds, "spatial_ref") is True
90+
assert is_grid_mapping_variable(ds, "temperature") is False
9191

92-
def test_validate_existing_band_data_valid(self):
92+
def test_validate_existing_band_data_valid(self) -> None:
9393
"""Test validation of existing valid band data."""
9494
# Create datasets
9595
existing_ds = xr.Dataset(
@@ -108,16 +108,16 @@ def test_validate_existing_band_data_valid(self):
108108

109109
expected_ds = xr.Dataset({"B04": (["y", "x"], np.random.rand(100, 100))})
110110

111-
assert validate_existing_band_data(existing_ds, "B04", expected_ds) == True
111+
assert validate_existing_band_data(existing_ds, "B04", expected_ds) is True
112112

113-
def test_validate_existing_band_data_missing(self):
113+
def test_validate_existing_band_data_missing(self) -> None:
114114
"""Test validation of missing band data."""
115115
existing_ds = xr.Dataset({})
116116
expected_ds = xr.Dataset({"B04": (["y", "x"], np.random.rand(100, 100))})
117117

118-
assert validate_existing_band_data(existing_ds, "B04", expected_ds) == False
118+
assert validate_existing_band_data(existing_ds, "B04", expected_ds) is False
119119

120-
def test_calculate_overview_levels(self):
120+
def test_calculate_overview_levels(self) -> None:
121121
"""Test overview levels calculation."""
122122
levels = calculate_overview_levels(1024, 1024, min_dimension=256, tile_width=256)
123123

@@ -142,7 +142,7 @@ def test_calculate_overview_levels(self):
142142
class TestMetadataSetup:
143143
"""Test metadata setup functions."""
144144

145-
def test_setup_datatree_metadata_geozarr_spec_compliant(self):
145+
def test_setup_datatree_metadata_geozarr_spec_compliant(self) -> None:
146146
"""Test GeoZarr metadata setup."""
147147
# Create a real DataTree with measurement groups
148148
# Create datasets for different resolution groups
@@ -189,7 +189,7 @@ def test_setup_datatree_metadata_geozarr_spec_compliant(self):
189189
class TestIntegration:
190190
"""Integration tests."""
191191

192-
def test_create_simple_geozarr_metadata(self):
192+
def test_create_simple_geozarr_metadata(self) -> None:
193193
"""Test creating simple GeoZarr metadata structure."""
194194
# Create a simple dataset
195195
data = np.random.rand(10, 10)

eopf_geozarr/tests/test_integration_sentinel2.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
docs/analysis/eopf-geozarr/EOPF_Sentinel2_ZarrV3_geozarr_compliant.ipynb
77
"""
88

9-
import os
109
import shutil
1110
import tempfile
1211
from pathlib import Path
@@ -24,7 +23,7 @@ class TestSentinel2Integration:
2423
"""Integration tests for Sentinel-2 EOPF to GeoZarr conversion."""
2524

2625
@pytest.fixture
27-
def sample_sentinel2_datatree(self):
26+
def sample_sentinel2_datatree(self) -> xr.DataTree:
2827
"""
2928
Create a sample Sentinel-2 EOPF DataTree structure for testing.
3029
@@ -217,15 +216,15 @@ def sample_sentinel2_datatree(self):
217216
return dt
218217

219218
@pytest.fixture
220-
def temp_output_dir(self):
219+
def temp_output_dir(self) -> str:
221220
"""Create a temporary directory for test outputs."""
222221
temp_dir = tempfile.mkdtemp()
223222
yield temp_dir
224223
shutil.rmtree(temp_dir)
225224

226225
def test_complete_sentinel2_conversion_notebook_workflow(
227226
self, sample_sentinel2_datatree, temp_output_dir
228-
):
227+
) -> None:
229228
"""
230229
Test complete conversion following the notebook workflow.
231230
@@ -247,7 +246,7 @@ def test_complete_sentinel2_conversion_notebook_workflow(
247246
"/quality/l1c_quicklook/r10m",
248247
]
249248

250-
print(f"Converting Sentinel-2 EOPF DataTree to GeoZarr format...")
249+
print("Converting Sentinel-2 EOPF DataTree to GeoZarr format...")
251250
print(f"Input groups: {groups}")
252251
print(f"Output path: {output_path}")
253252

@@ -283,7 +282,7 @@ def test_complete_sentinel2_conversion_notebook_workflow(
283282

284283
print("✅ All integration tests passed!")
285284

286-
def _verify_basic_structure(self, output_path, groups):
285+
def _verify_basic_structure(self, output_path, groups) -> None:
287286
"""Verify the basic Zarr store structure."""
288287
print("Verifying basic structure...")
289288

@@ -301,7 +300,7 @@ def _verify_basic_structure(self, output_path, groups):
301300
assert level_0_path.exists(), f"Level 0 not found for {group}"
302301
assert (level_0_path / "zarr.json").exists(), f"Level 0 missing zarr.json for {group}"
303302

304-
def _verify_geozarr_spec_compliance(self, output_path, group):
303+
def _verify_geozarr_spec_compliance(self, output_path, group) -> None:
305304
"""
306305
Verify GeoZarr specification compliance following the notebook verification.
307306
@@ -380,13 +379,13 @@ def _verify_geozarr_spec_compliance(self, output_path, group):
380379
if "GeoTransform" in ds["spatial_ref"].attrs:
381380
print(f" ✅ GeoTransform: {ds['spatial_ref'].attrs['GeoTransform']}")
382381
else:
383-
print(f" ⚠️ Missing GeoTransform attribute")
382+
print(" ⚠️ Missing GeoTransform attribute")
384383

385384
# Check 6: CRS information (from notebook verification)
386385
if "crs_wkt" in ds["spatial_ref"].attrs:
387-
print(f" ✅ CRS WKT present")
386+
print(" ✅ CRS WKT present")
388387
else:
389-
print(f" ⚠️ Missing CRS WKT")
388+
print(" ⚠️ Missing CRS WKT")
390389

391390
# Check 7: Coordinate standard names (from notebook verification)
392391
for coord in ["x", "y"]:
@@ -402,7 +401,7 @@ def _verify_geozarr_spec_compliance(self, output_path, group):
402401

403402
ds.close()
404403

405-
def _verify_multiscale_structure(self, output_path, group):
404+
def _verify_multiscale_structure(self, output_path, group) -> None:
406405
"""Verify multiscale structure following notebook patterns."""
407406
print(f"Verifying multiscale structure for {group}...")
408407

@@ -475,15 +474,15 @@ def _verify_multiscale_structure(self, output_path, group):
475474
f" Level {prev_level}{level} downsampling ratio: {height_ratio:.2f}x{width_ratio:.2f}"
476475
)
477476

478-
def _verify_rgb_data_access(self, output_path, groups):
477+
def _verify_rgb_data_access(self, output_path, groups) -> None:
479478
"""Verify RGB data access patterns from the notebook."""
480479
print("Verifying RGB data access patterns...")
481480

482481
# Find groups with RGB bands (following notebook logic)
483482
rgb_groups = []
484483
for group in groups:
485-
group_path = str(output_path / group.lstrip("/") / "0")
486-
ds = xr.open_dataset(group_path, engine="zarr", zarr_format=3)
484+
group_path_str = str(output_path / group.lstrip("/") / "0")
485+
ds = xr.open_dataset(group_path_str, engine="zarr", zarr_format=3)
487486

488487
# Check for RGB bands (b04=red, b03=green, b02=blue for Sentinel-2)
489488
has_rgb = all(band in ds.data_vars for band in ["b04", "b03", "b02"])
@@ -530,7 +529,7 @@ def _verify_rgb_data_access(self, output_path, groups):
530529
ds.close()
531530

532531
@pytest.mark.slow
533-
def test_performance_characteristics(self, sample_sentinel2_datatree, temp_output_dir):
532+
def test_performance_characteristics(self, sample_sentinel2_datatree, temp_output_dir) -> None:
534533
"""
535534
Test performance characteristics following notebook analysis.
536535
@@ -546,7 +545,7 @@ def test_performance_characteristics(self, sample_sentinel2_datatree, temp_outpu
546545
groups = ["/measurements/reflectance/r10m"] # Focus on one group for performance testing
547546

548547
with patch("eopf_geozarr.conversion.geozarr.print"):
549-
dt_geozarr = create_geozarr_dataset(
548+
create_geozarr_dataset(
550549
dt_input=dt_input,
551550
groups=groups,
552551
output_path=str(output_path),

0 commit comments

Comments
 (0)