ncarnate 2.2.2__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ncarnate-2.3.0/.gitattributes +2 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/.github/workflows/ci.yml +104 -14
- ncarnate-2.3.0/.github/workflows/full-granules.yml +39 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/.github/workflows/publish.yml +12 -21
- {ncarnate-2.2.2 → ncarnate-2.3.0}/.gitignore +6 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/.readthedocs.yaml +1 -1
- ncarnate-2.3.0/.zenodo.json +26 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/CHANGELOG.md +45 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/CITATION.cff +5 -2
- {ncarnate-2.2.2 → ncarnate-2.3.0}/CONTRIBUTING.md +33 -5
- {ncarnate-2.2.2 → ncarnate-2.3.0}/PKG-INFO +135 -32
- {ncarnate-2.2.2 → ncarnate-2.3.0}/README.md +125 -29
- ncarnate-2.3.0/brand/OFL.txt +90 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/README.md +21 -0
- ncarnate-2.3.0/brand/build_social_preview.py +78 -0
- ncarnate-2.3.0/brand/ncarnate-social-preview.png +0 -0
- ncarnate-2.3.0/brand/sora-600.ttf +0 -0
- ncarnate-2.3.0/docs/api.rst +184 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/docs/fidelity-notes.md +32 -7
- {ncarnate-2.2.2 → ncarnate-2.3.0}/docs/index.rst +2 -0
- ncarnate-2.3.0/docs/release-notes.rst +5 -0
- ncarnate-2.3.0/docs/releasing.rst +97 -0
- ncarnate-2.3.0/examples/verified_conversion.py +37 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/__init__.py +4 -0
- ncarnate-2.3.0/ncarnate/_bounded_worker.py +38 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/atttypes.py +78 -13
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/__init__.py +1 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/codes.py +19 -1
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/models.py +29 -0
- ncarnate-2.3.0/ncarnate/checkpoints.py +152 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/constants.py +1 -1
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/__init__.py +75 -28
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/preflight.py +14 -125
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/reader.py +5 -1
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/report.py +1 -1
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/core.py +31 -18
- ncarnate-2.3.0/ncarnate/destinations.py +57 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/eos/swath.py +43 -10
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/handoff.py +141 -12
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/hdf4.py +258 -40
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/hdf4_runtime.py +2 -3
- ncarnate-2.3.0/ncarnate/hdf4_stream.py +485 -0
- ncarnate-2.3.0/ncarnate/journal.py +42 -0
- ncarnate-2.3.0/ncarnate/memory.py +216 -0
- ncarnate-2.3.0/ncarnate/prepared.py +186 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/result.py +81 -6
- ncarnate-2.3.0/ncarnate/streaming.py +94 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/paper/paper.md +12 -2
- {ncarnate-2.2.2 → ncarnate-2.3.0}/pyproject.toml +19 -9
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_audit_path.py +12 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_codes.py +4 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/conftest.py +3 -2
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_agreement.py +1 -1
- ncarnate-2.3.0/tests/fixtures/corpus.json +60 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/myd05_trim.provenance.json +5 -1
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/trim_hdfeos2.py +9 -2
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_attribute_types.py +17 -7
- ncarnate-2.3.0/tests/test_contract_hardening.py +260 -0
- ncarnate-2.3.0/tests/test_conversion_reporting.py +87 -0
- ncarnate-2.3.0/tests/test_full_corpus.py +70 -0
- ncarnate-2.3.0/tests/test_hdf4_streaming.py +676 -0
- ncarnate-2.3.0/tests/test_memory_worker.py +158 -0
- ncarnate-2.3.0/tests/test_prepared.py +197 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_raw_granules.py +2 -2
- ncarnate-2.3.0/tests/test_release_review_regressions.py +179 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_sdist_contents.py +19 -2
- ncarnate-2.3.0/tests/test_streaming_cache.py +22 -0
- ncarnate-2.3.0/tests/test_swath_tiling.py +55 -0
- ncarnate-2.3.0/tools/corpus.py +84 -0
- ncarnate-2.3.0/tools/hatch_build.py +18 -0
- ncarnate-2.3.0/tools/test_distribution.py +75 -0
- ncarnate-2.3.0/tools/test_full_corpus.py +45 -0
- ncarnate-2.3.0/tools/tests/test_ci_helpers.py +86 -0
- ncarnate-2.3.0/tools/validate_release_metadata.py +99 -0
- ncarnate-2.2.2/.gitattributes +0 -2
- ncarnate-2.2.2/.zenodo.json +0 -25
- ncarnate-2.2.2/conda-recipe/README.md +0 -54
- ncarnate-2.2.2/conda-recipe/recipe.yaml +0 -78
- ncarnate-2.2.2/docs/api.rst +0 -70
- {ncarnate-2.2.2 → ncarnate-2.3.0}/.github/FUNDING.yml +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/CODE_OF_CONDUCT.md +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/LICENSE +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/build_lockup.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/build_raster_lockup.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/compose_logo.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/globe.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-lockup-dark.png +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-lockup-dark.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-lockup.png +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-lockup.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-mark.png +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-mono.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-source.png +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-white.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-wordmark-white.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate-wordmark.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/brand/ncarnate.svg +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/docs/conf.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/docs/requirements.txt +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/__main__.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/classify.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/inspect.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/audit/report.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/cli.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/integrity.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/convert/models.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/discovery.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/eos/__init__.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/eos/gctp.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/eos/grid.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/eos/structmeta.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/errors.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/formats.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/hashing.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/limits.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/schemas/__init__.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/schemas/handoff.schema.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/ncarnate/stage.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/paper/paper.bib +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/__init__.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/record.schema.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_agreement.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_checksum.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_classify.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_cli_dispatch.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_contract.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_emission.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_error_codes.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_inspect.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_models.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_readonly.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/audit/test_scaffold.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/__init__.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_cli_dispatch.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_cli_integration.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_convert_manifest.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_inplace_skip.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_integrity.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_models.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_reader.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/convert/test_summary_exit.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/blockers/compound_type.nc +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/amsre_5daysnow_trim.hdf +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/amsre_5daysnow_trim.provenance.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/amsre_seaice12km_trim.hdf +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/amsre_seaice12km_trim.provenance.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/mod03_trim.hdf +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/mod03_trim.provenance.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/myd05_trim.hdf +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/raingrid_trim.hdf +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/hdfeos2/raingrid_trim.provenance.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/netcdf/endianness.nc +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/netcdf/nested_groups.nc +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/netcdf/packed_fill.nc +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/data/netcdf/unlimited_dim.nc +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/make_blocker_fixtures.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/make_fixtures.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/operation_result/amsre_seaice12km.result.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/fixtures/operation_result/packed_fill.canonical.json +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_cli.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_convert_collisions.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_convert_inplace_collisions.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_discovery.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_eos_units.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_formats.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_g5_handoff_sufficiency.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_handoff_gate.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_handoff_schema.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_hdf4_conversion.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_limits.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_operation_result.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_optional_hdf4.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_plan_hash.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_recompress_netcdf.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_stage_api_4b.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tests/test_stage_api_g4.py +0 -0
- {ncarnate-2.2.2 → ncarnate-2.3.0}/tools/validate_distributions.py +0 -0
|
@@ -5,6 +5,17 @@ on:
|
|
|
5
5
|
branches: [main]
|
|
6
6
|
pull_request:
|
|
7
7
|
workflow_dispatch:
|
|
8
|
+
workflow_call:
|
|
9
|
+
inputs:
|
|
10
|
+
release_tag:
|
|
11
|
+
type: string
|
|
12
|
+
default: ''
|
|
13
|
+
publish_target:
|
|
14
|
+
type: string
|
|
15
|
+
default: ''
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
8
19
|
|
|
9
20
|
# The pip-based jobs (test, test-windows-degraded, build) install no system
|
|
10
21
|
# HDF libraries on purpose: the netCDF4 wheels bundle libhdf5/libnetcdf, and
|
|
@@ -16,6 +27,20 @@ on:
|
|
|
16
27
|
# the *supported* install channel delivers full HDF4 capability (KD-L7, G2).
|
|
17
28
|
|
|
18
29
|
jobs:
|
|
30
|
+
docs-and-metadata:
|
|
31
|
+
name: Documentation and release metadata
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
|
|
35
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.12"
|
|
38
|
+
- run: python -m pip install -r docs/requirements.txt PyYAML cffconvert pytest
|
|
39
|
+
- run: python -m pytest -q tools/tests
|
|
40
|
+
- run: python tools/validate_release_metadata.py
|
|
41
|
+
- run: cffconvert --validate
|
|
42
|
+
- run: python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
43
|
+
|
|
19
44
|
lint:
|
|
20
45
|
name: ruff
|
|
21
46
|
runs-on: ubuntu-latest
|
|
@@ -41,15 +66,13 @@ jobs:
|
|
|
41
66
|
os: [ubuntu-latest]
|
|
42
67
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
43
68
|
# Smoke the self-contained-wheel install story (pyhdf + netCDF4 +
|
|
44
|
-
# pyproj, no system HDF libs) on macOS
|
|
45
|
-
#
|
|
46
|
-
# HDF4 runtime, so the HDF4 fixtures can't run there. Windows-pip
|
|
47
|
-
# instead gets the dedicated degraded-capability job below
|
|
48
|
-
# (KD-L4/KD-L7), which pins exactly what must keep working — and
|
|
49
|
-
# exactly how the HDF4 path must refuse — without a usable pyhdf.
|
|
69
|
+
# pyproj, no system HDF libs) on macOS and Windows. A separate
|
|
70
|
+
# injected-missing-runtime job retains degraded-capability coverage.
|
|
50
71
|
include:
|
|
51
72
|
- os: macos-latest
|
|
52
73
|
python-version: "3.12"
|
|
74
|
+
- os: windows-latest
|
|
75
|
+
python-version: "3.11"
|
|
53
76
|
steps:
|
|
54
77
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
|
|
55
78
|
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
@@ -71,9 +94,8 @@ jobs:
|
|
|
71
94
|
with:
|
|
72
95
|
python-version: "3.12"
|
|
73
96
|
- name: Install package with test extras (pip wheels only)
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# job exists to pin (KD-L4/KD-L7, gate G2).
|
|
97
|
+
# Tests inject an unavailable runtime, independently of whether the
|
|
98
|
+
# installed pyhdf wheel works on this particular Windows runner.
|
|
77
99
|
run: |
|
|
78
100
|
python -m pip install --upgrade pip
|
|
79
101
|
python -m pip install -e ".[test]"
|
|
@@ -85,8 +107,12 @@ jobs:
|
|
|
85
107
|
run: python -m pytest tests/test_optional_hdf4.py -q
|
|
86
108
|
|
|
87
109
|
test-conda-forge:
|
|
88
|
-
name: pytest full-capability (conda-forge)
|
|
89
|
-
runs-on:
|
|
110
|
+
name: pytest full-capability (conda-forge, ${{ matrix.os }})
|
|
111
|
+
runs-on: ${{ matrix.os }}
|
|
112
|
+
strategy:
|
|
113
|
+
fail-fast: false
|
|
114
|
+
matrix:
|
|
115
|
+
os: [ubuntu-latest, windows-latest]
|
|
90
116
|
# conda-forge is the *supported* HDF4 install channel (the refusal and
|
|
91
117
|
# the README point Windows/aarch64 users at `conda install -c
|
|
92
118
|
# conda-forge ncarnate`). This lane proves that channel actually
|
|
@@ -120,12 +146,15 @@ jobs:
|
|
|
120
146
|
pyproj
|
|
121
147
|
tqdm
|
|
122
148
|
pytest
|
|
149
|
+
pip
|
|
123
150
|
- name: Install ncarnate from source without replacing native deps
|
|
124
151
|
# --no-deps: install only ncarnate; the native stack (pyhdf,
|
|
125
152
|
# netCDF4, numpy, pyproj, tqdm) stays the conda-forge build. Build
|
|
126
153
|
# isolation still fetches the hatchling backend into a throwaway
|
|
127
154
|
# env, which never touches the runtime dependencies above.
|
|
128
|
-
run:
|
|
155
|
+
run: |
|
|
156
|
+
python -m pip install build "hatchling>=1.27"
|
|
157
|
+
python -m pip install -e . --no-deps
|
|
129
158
|
- name: Prove the conda-forge HDF4 runtime imports
|
|
130
159
|
run: |
|
|
131
160
|
python -c "from ncarnate.hdf4_runtime import require_hdf4_runtime; require_hdf4_runtime(); print('conda-forge HDF4 runtime OK')"
|
|
@@ -144,14 +173,75 @@ jobs:
|
|
|
144
173
|
with:
|
|
145
174
|
python-version: "3.12"
|
|
146
175
|
- name: Install build tooling
|
|
147
|
-
run: python -m pip install build twine
|
|
176
|
+
run: python -m pip install build twine PyYAML cffconvert
|
|
177
|
+
- name: Validate release identity before building
|
|
178
|
+
env:
|
|
179
|
+
NCARNATE_RELEASE_TAG: ${{ inputs.release_tag || '' }}
|
|
180
|
+
NCARNATE_PUBLISH_TARGET: ${{ inputs.publish_target || '' }}
|
|
181
|
+
run: |
|
|
182
|
+
python tools/validate_release_metadata.py
|
|
183
|
+
cffconvert --validate
|
|
148
184
|
- name: Build sdist + wheel
|
|
149
185
|
run: python -m build
|
|
150
186
|
- name: Check distributions
|
|
151
|
-
run: twine check dist/*
|
|
187
|
+
run: twine check --strict dist/*
|
|
152
188
|
- name: Validate distributions
|
|
153
189
|
run: >-
|
|
154
190
|
python tools/validate_distributions.py
|
|
155
191
|
--allowed-email dev@erickshepherd.com
|
|
156
192
|
--allowed-email ca@trufo.ai
|
|
157
193
|
dist/*
|
|
194
|
+
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
195
|
+
with:
|
|
196
|
+
name: dist
|
|
197
|
+
path: dist/
|
|
198
|
+
if-no-files-found: error
|
|
199
|
+
|
|
200
|
+
installed-packages:
|
|
201
|
+
name: Installed ${{ matrix.kind }} (${{ matrix.os }})
|
|
202
|
+
needs: build
|
|
203
|
+
runs-on: ${{ matrix.os }}
|
|
204
|
+
timeout-minutes: 20
|
|
205
|
+
strategy:
|
|
206
|
+
fail-fast: false
|
|
207
|
+
matrix:
|
|
208
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
209
|
+
kind: [wheel, sdist]
|
|
210
|
+
steps:
|
|
211
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
|
|
212
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
213
|
+
with:
|
|
214
|
+
python-version: "3.12"
|
|
215
|
+
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
|
|
216
|
+
with:
|
|
217
|
+
name: dist
|
|
218
|
+
path: dist/
|
|
219
|
+
- name: Test installed package outside the source checkout
|
|
220
|
+
run: python tools/test_distribution.py dist --kind ${{ matrix.kind }}
|
|
221
|
+
|
|
222
|
+
zarr-companion:
|
|
223
|
+
name: Installed Zarr companion
|
|
224
|
+
needs: build
|
|
225
|
+
runs-on: ubuntu-latest
|
|
226
|
+
timeout-minutes: 15
|
|
227
|
+
steps:
|
|
228
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
|
|
229
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
230
|
+
with:
|
|
231
|
+
python-version: "3.12"
|
|
232
|
+
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
|
|
233
|
+
with:
|
|
234
|
+
name: dist
|
|
235
|
+
path: dist/
|
|
236
|
+
- name: Build and install the companion with the candidate wheel
|
|
237
|
+
run: |
|
|
238
|
+
python -m pip install build twine
|
|
239
|
+
python -m build companions/zarr-demo --outdir companion-dist
|
|
240
|
+
python -m twine check --strict companion-dist/*
|
|
241
|
+
python -m pip install dist/*.whl companion-dist/*.whl pytest
|
|
242
|
+
python -m pip check
|
|
243
|
+
- name: Run companion tests outside both source packages
|
|
244
|
+
run: |
|
|
245
|
+
cp -R companions/zarr-demo/tests "$RUNNER_TEMP/zarr-tests"
|
|
246
|
+
cd "$RUNNER_TEMP"
|
|
247
|
+
python -m pytest -q zarr-tests
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Full-granule validation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
full-granules:
|
|
13
|
+
name: Exact originals and independent reference
|
|
14
|
+
if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release/')
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 30
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
|
|
19
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- run: python -m pip install -e ".[test]"
|
|
23
|
+
- name: Set the corpus location outside the checkout
|
|
24
|
+
run: echo "NCARNATE_GRANULE_DIR=$RUNNER_TEMP/ncarnate-granules" >> "$GITHUB_ENV"
|
|
25
|
+
- name: Fetch and verify every required file before running tests
|
|
26
|
+
run: >-
|
|
27
|
+
python tools/corpus.py "$NCARNATE_GRANULE_DIR" --fetch --include-references
|
|
28
|
+
> corpus-verification.json
|
|
29
|
+
- name: Run full-size conversions and reference comparisons
|
|
30
|
+
run: python tools/test_full_corpus.py --junitxml full-granules.xml
|
|
31
|
+
- name: Retain verification and test results, including failures
|
|
32
|
+
if: always()
|
|
33
|
+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
|
|
34
|
+
with:
|
|
35
|
+
name: full-granule-results
|
|
36
|
+
path: |
|
|
37
|
+
corpus-verification.json
|
|
38
|
+
full-granules.xml
|
|
39
|
+
if-no-files-found: warn
|
|
@@ -27,28 +27,19 @@ permissions:
|
|
|
27
27
|
contents: read
|
|
28
28
|
|
|
29
29
|
jobs:
|
|
30
|
-
|
|
31
|
-
name:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
run: |
|
|
40
|
-
python -m pip install --upgrade build twine
|
|
41
|
-
python -m build
|
|
42
|
-
python -m twine check dist/*
|
|
43
|
-
python tools/validate_distributions.py --allowed-email dev@erickshepherd.com --allowed-email ca@trufo.ai dist/*
|
|
44
|
-
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
45
|
-
with:
|
|
46
|
-
name: dist
|
|
47
|
-
path: dist/
|
|
30
|
+
verify:
|
|
31
|
+
name: Test and build the exact release revision
|
|
32
|
+
# A local reusable workflow uses the same revision as this caller.
|
|
33
|
+
# Its dist artifact is installed by the package tests and then published
|
|
34
|
+
# unchanged below. Failure of ANY CI job prevents either upload.
|
|
35
|
+
uses: ./.github/workflows/ci.yml
|
|
36
|
+
with:
|
|
37
|
+
release_tag: ${{ github.event_name == 'release' && github.event.release.tag_name || (github.ref_type == 'tag' && github.ref_name || '') }}
|
|
38
|
+
publish_target: ${{ github.event_name == 'release' && 'pypi' || inputs.target }}
|
|
48
39
|
|
|
49
40
|
publish-testpypi:
|
|
50
41
|
name: Publish to TestPyPI
|
|
51
|
-
needs:
|
|
42
|
+
needs: verify
|
|
52
43
|
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
53
44
|
runs-on: ubuntu-latest
|
|
54
45
|
environment: testpypi
|
|
@@ -62,11 +53,11 @@ jobs:
|
|
|
62
53
|
- uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
|
|
63
54
|
with:
|
|
64
55
|
repository-url: https://test.pypi.org/legacy/
|
|
65
|
-
|
|
56
|
+
# Duplicate versions fail: success must mean these files were uploaded.
|
|
66
57
|
|
|
67
58
|
publish-pypi:
|
|
68
59
|
name: Publish to PyPI
|
|
69
|
-
needs:
|
|
60
|
+
needs: verify
|
|
70
61
|
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
71
62
|
runs-on: ubuntu-latest
|
|
72
63
|
environment: pypi
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"upload_type": "software",
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"title": "ncarnate: Convert HDF4/HDF-EOS2 (MODIS, AMSR-E) to CF-annotated netCDF4 and losslessly recompress netCDF/HDF5",
|
|
5
|
+
"description": "A Python library and command-line tool for converting HDF4/HDF-EOS2 granules to CF-annotated netCDF4 and losslessly recompressing netCDF/HDF5. Version 2.3.0 adds explicit-output prepared jobs, digest-verified resume, streamed copying and verification, tiled swath interpolation, and optional isolated conversion workers with OS-enforced memory limits on Windows and Linux. Outputs are verified value-for-value before publication. Generated coordinate fields can still require whole arrays, and the worker limit excludes the calling process.",
|
|
6
|
+
"access_right": "open",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"netCDF4",
|
|
10
|
+
"HDF4",
|
|
11
|
+
"HDF-EOS2",
|
|
12
|
+
"HDF5",
|
|
13
|
+
"CF conventions",
|
|
14
|
+
"MODIS",
|
|
15
|
+
"AMSR-E",
|
|
16
|
+
"remote sensing",
|
|
17
|
+
"atmospheric science",
|
|
18
|
+
"Python"
|
|
19
|
+
],
|
|
20
|
+
"creators": [
|
|
21
|
+
{
|
|
22
|
+
"name": "Shepherd, Erick Edward",
|
|
23
|
+
"orcid": "0000-0002-4750-6100"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -4,6 +4,51 @@ All notable changes to this project are documented here. The format is
|
|
|
4
4
|
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
5
|
this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
## [2.3.0] - 2026-10-08
|
|
10
|
+
|
|
11
|
+
- Add explicit-output prepared plans, shared finite-batch collision checks and
|
|
12
|
+
digest-verified resume journals. Existing one-shot defaults remain compatible.
|
|
13
|
+
- Store completion checkpoints separately so large batches remain resumable;
|
|
14
|
+
resolve directory aliases and check every output parent before execution.
|
|
15
|
+
- Refuse unknown inputs before output-directory creation; reserve CLI result
|
|
16
|
+
journals before conversion and distinguish later reporting failures.
|
|
17
|
+
- Bound hostile handoff records, refuse invalid text and audit modes, freeze
|
|
18
|
+
encoding options, and guard unsynchronized attribute inquiries.
|
|
19
|
+
- Stream netCDF and HDF4 SDS copying and verification in slices; offer an
|
|
20
|
+
OS-enforced conversion-worker memory cap on Windows and Linux. Generated
|
|
21
|
+
HDF-EOS coordinates still materialize whole arrays within that worker cap,
|
|
22
|
+
while tiled swath interpolation avoids large full-grid temporaries.
|
|
23
|
+
- Preserve reads of multidimensional variables named like dimensions when
|
|
24
|
+
configuring netCDF native caches (found with the complete MOD03 granule).
|
|
25
|
+
- Finish large storage chunks before advancing and coalesce small chunks to
|
|
26
|
+
avoid repeated compression and excessive per-chunk Python calls.
|
|
27
|
+
- Add exact-granule retrieval and verification, Windows test portability fixes,
|
|
28
|
+
and a separately packaged numeric Zarr/xarray demonstration.
|
|
29
|
+
- Document the public staged, prepared, handoff and bounded-worker APIs on Read
|
|
30
|
+
the Docs, and synchronize package, citation, Zenodo and conda recipe metadata.
|
|
31
|
+
- Gate publication on the complete CI suite, test built packages outside the
|
|
32
|
+
checkout on Linux, Windows and macOS, and test the separate Zarr companion.
|
|
33
|
+
Add release-PR and manual full-granule validation that refuses incomplete evidence; require
|
|
34
|
+
matching production tags and fail duplicate TestPyPI uploads.
|
|
35
|
+
- Reconcile MYD05 fixture regeneration with its recorded trimmed metadata and
|
|
36
|
+
emit canonical source-archive gzip headers for publication inspection.
|
|
37
|
+
|
|
38
|
+
### Compatibility notes
|
|
39
|
+
|
|
40
|
+
- Existing one-shot source-replacement defaults remain unchanged. The new
|
|
41
|
+
prepared and bounded APIs always require a separate explicit output.
|
|
42
|
+
- Encoding options are now immutable, enforcing the documented immutable-plan
|
|
43
|
+
contract. Use `dataclasses.replace(options, complevel=...)` to derive options;
|
|
44
|
+
assigning to a field now raises `FrozenInstanceError`.
|
|
45
|
+
- Unknown audit modes and invalid UTF-8 result text are now deliberately refused.
|
|
46
|
+
Metadata-only audit is the only implemented audit depth.
|
|
47
|
+
- The handoff schema stays at version 1. New diagnostic codes are append-only;
|
|
48
|
+
the classification ruleset advances independently to version 9.
|
|
49
|
+
- Hard worker limits are available on Windows and Linux; macOS is explicitly
|
|
50
|
+
unsupported by `execute_bounded`. Ordinary conversion remains portable.
|
|
51
|
+
|
|
7
52
|
## [2.2.2] - 2026-07-21
|
|
8
53
|
|
|
9
54
|
Maintenance release with packaging and CI updates. Conversion, recompression,
|
|
@@ -5,14 +5,17 @@ abstract: >-
|
|
|
5
5
|
Reincarnate legacy scientific data as modern netCDF4: losslessly
|
|
6
6
|
recompress netCDF/HDF5 files and convert HDF4/HDF-EOS2 granules to
|
|
7
7
|
CF-annotated netCDF4 with reconstructed grid and swath geolocation.
|
|
8
|
+
Supports verified resumable conversions, streamed data transfer, and
|
|
9
|
+
isolated conversion workers with memory limits on Windows and Linux.
|
|
8
10
|
type: software
|
|
9
11
|
authors:
|
|
10
12
|
- family-names: "Shepherd"
|
|
11
13
|
given-names: "Erick Edward"
|
|
12
14
|
email: "dev@erickshepherd.com"
|
|
13
15
|
orcid: "https://orcid.org/0000-0002-4750-6100"
|
|
14
|
-
version: "2.
|
|
15
|
-
date-released: "2026-
|
|
16
|
+
version: "2.3.0"
|
|
17
|
+
date-released: "2026-10-08"
|
|
18
|
+
# This is the verified concept DOI, which identifies all versions.
|
|
16
19
|
doi: "10.5281/zenodo.21288802"
|
|
17
20
|
license: MIT
|
|
18
21
|
repository-code: "https://github.com/ErickShepherd/ncarnate"
|
|
@@ -30,12 +30,15 @@ or an issue labelled `question`. The README covers installation (including the
|
|
|
30
30
|
```console
|
|
31
31
|
git clone https://github.com/ErickShepherd/ncarnate
|
|
32
32
|
cd ncarnate
|
|
33
|
-
pip install -e ".[test]"
|
|
33
|
+
python -m pip install -e ".[test,release]" "ruff==0.15.*"
|
|
34
|
+
python -m pip install -r docs/requirements.txt
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
Compatible pip wheels can supply the native dependencies. If a wheel is not
|
|
38
|
+
available for your platform and Python version, use conda-forge or install the
|
|
39
|
+
native libraries and build tools described in the README. Ordinary conversion
|
|
40
|
+
and the hard memory-limit worker have different platform coverage: the worker
|
|
41
|
+
supports Windows and Linux and deliberately refuses macOS.
|
|
39
42
|
|
|
40
43
|
## Making a change
|
|
41
44
|
|
|
@@ -47,12 +50,37 @@ HDF4 library first (see the README).
|
|
|
47
50
|
4. Run the checks locally:
|
|
48
51
|
```console
|
|
49
52
|
ruff check .
|
|
50
|
-
pytest
|
|
53
|
+
python -m pytest -q
|
|
54
|
+
python -m pytest -q tools/tests
|
|
55
|
+
python tools/validate_release_metadata.py
|
|
56
|
+
cffconvert --validate
|
|
57
|
+
python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
51
58
|
```
|
|
52
59
|
5. Open a pull request describing the change and, for a conversion change, how
|
|
53
60
|
it preserves the fidelity contract (stored values unchanged; output verified
|
|
54
61
|
against the source before it replaces anything).
|
|
55
62
|
|
|
63
|
+
For full-size regression evidence, fetch and verify the catalogued originals
|
|
64
|
+
and independent reference with
|
|
65
|
+
`python tools/corpus.py DATA_DIRECTORY --fetch --include-references`.
|
|
66
|
+
Set `NCARNATE_GRANULE_DIR` to that directory and
|
|
67
|
+
run `python tools/test_full_corpus.py --junitxml full-granules.xml`. That check
|
|
68
|
+
fails on missing files or skipped/missing tests; ordinary pytest remains usable
|
|
69
|
+
without the external corpus. Keep downloaded inputs and reports outside the
|
|
70
|
+
tracked source tree. The manual full-granule GitHub workflow retains its reports.
|
|
71
|
+
|
|
72
|
+
The Zarr demonstration requires Python 3.11 or newer and is a separate package:
|
|
73
|
+
install `companions/zarr-demo` after the candidate ncarnate package, then run
|
|
74
|
+
`python -m pytest -q companions/zarr-demo/tests`. Its CI job builds and tests the
|
|
75
|
+
installed wheel outside both source packages.
|
|
76
|
+
|
|
77
|
+
To exercise release installations locally, build to a directory outside the
|
|
78
|
+
checkout with `python -m build --outdir PATH_TO_ARTIFACTS`, then run
|
|
79
|
+
`python tools/test_distribution.py PATH_TO_ARTIFACTS --kind wheel` and repeat
|
|
80
|
+
with `--kind sdist`. Each run creates a clean environment and tests the installed
|
|
81
|
+
package. PyPI and TestPyPI wait for these jobs and the full source CI. Publication
|
|
82
|
+
is a separate maintainer action; see `docs/releasing.rst`.
|
|
83
|
+
|
|
56
84
|
## Scope and design
|
|
57
85
|
|
|
58
86
|
ncarnate deliberately **fails loud** on constructs it cannot convert correctly
|