From 8da13336dfa60c2b4412097bc5e58488aebbb66c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 19:28:07 +0200 Subject: [PATCH 1/2] chore(deps): bump the actions group across 1 directory with 8 updates (#176) Bumps the actions group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) | `0.9.5` | `0.9.6` | | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `6.0.0` | `6.0.1` | | [github/issue-metrics](https://github.com/github/issue-metrics) | `4.2.2` | `4.2.7` | | [j178/prek-action](https://github.com/j178/prek-action) | `2.0.3` | `2.0.4` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `7.0.0` | `7.0.1` | | [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` | | [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.13.0` | `1.14.0` | | [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) | `0.5.3` | `0.5.6` | Updates `prefix-dev/setup-pixi` from 0.9.5 to 0.9.6 - [Release notes](https://github.com/prefix-dev/setup-pixi/releases) - [Commits](https://github.com/prefix-dev/setup-pixi/compare/1b2de7f3351f171c8b4dfeb558c639cb58ed4ec0...5185adfbffb4bd703da3010310260805d89ebb11) Updates `codecov/codecov-action` from 6.0.0 to 6.0.1 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...e79a6962e0d4c0c17b229090214935d2e33f8354) Updates `github/issue-metrics` from 4.2.2 to 4.2.7 - [Release notes](https://github.com/github/issue-metrics/releases) - [Commits](https://github.com/github/issue-metrics/compare/c9e9838147fd355dace335ba787f01b6641a400a...1e38d5e62363e14db8019ed7d106b9855bdba6cc) Updates `j178/prek-action` from 2.0.3 to 2.0.4 - [Release notes](https://github.com/j178/prek-action/releases) - [Commits](https://github.com/j178/prek-action/compare/6ad80277337ad479fe43bd70701c3f7f8aa74db3...bdca6f102f98e2b4c7029491a53dfd366469e33d) Updates `actions/upload-artifact` from 7.0.0 to 7.0.1 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v7...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) Updates `actions/download-artifact` from 7.0.0 to 8.0.1 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v7...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c) Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0 - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.13.0...cef221092ed1bacb1cc03d23a2d87d1d172e277b) Updates `zizmorcore/zizmor-action` from 0.5.3 to 0.5.6 - [Release notes](https://github.com/zizmorcore/zizmor-action/releases) - [Commits](https://github.com/zizmorcore/zizmor-action/compare/b1d7e1fb5de872772f31590499237e7cce841e8e...5f14fd08f7cf1cb1609c1e344975f152c7ee938d) --- updated-dependencies: - dependency-name: prefix-dev/setup-pixi dependency-version: 0.9.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: codecov/codecov-action dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: github/issue-metrics dependency-version: 4.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: j178/prek-action dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: actions/download-artifact dependency-version: 8.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: zizmorcore/zizmor-action dependency-version: 0.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/downstream.yml | 2 +- .github/workflows/gpu_test.yml | 2 +- .github/workflows/hypothesis.yaml | 2 +- .github/workflows/issue-metrics.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 4 ++-- .github/workflows/zarr-metadata-release.yml | 12 ++++++------ .github/workflows/zizmor.yml | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/downstream.yml b/.github/workflows/downstream.yml index 74026233c4..3eb6898895 100644 --- a/.github/workflows/downstream.yml +++ b/.github/workflows/downstream.yml @@ -34,7 +34,7 @@ jobs: persist-credentials: false - name: Set up pixi - uses: prefix-dev/setup-pixi@1b2de7f3351f171c8b4dfeb558c639cb58ed4ec0 # v0.9.5 + uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6 with: manifest-path: xarray/pixi.toml diff --git a/.github/workflows/gpu_test.yml b/.github/workflows/gpu_test.yml index 403441b306..333769cb9e 100644 --- a/.github/workflows/gpu_test.yml +++ b/.github/workflows/gpu_test.yml @@ -76,7 +76,7 @@ jobs: hatch env run --env "$HATCH_ENV" run-coverage - name: Upload coverage - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: gpu diff --git a/.github/workflows/hypothesis.yaml b/.github/workflows/hypothesis.yaml index 4f9467be7d..a456b2aa0a 100644 --- a/.github/workflows/hypothesis.yaml +++ b/.github/workflows/hypothesis.yaml @@ -93,7 +93,7 @@ jobs: key: cache-hypothesis-${{ runner.os }}-${{ github.run_id }} - name: Upload coverage - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: tests diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml index 14fba5b9ec..510849ef3e 100644 --- a/.github/workflows/issue-metrics.yml +++ b/.github/workflows/issue-metrics.yml @@ -33,7 +33,7 @@ jobs: echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV" - name: Run issue-metrics tool - uses: github/issue-metrics@c9e9838147fd355dace335ba787f01b6641a400a # v4.2.2 + uses: github/issue-metrics@1e38d5e62363e14db8019ed7d106b9855bdba6cc # v4.2.7 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SEARCH_QUERY: 'repo:zarr-developers/zarr-python is:issue created:${{ env.last_month }} -reason:"not planned"' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 768e660ec2..fec211b4dd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,4 +30,4 @@ jobs: uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: enable-cache: true - - uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2.0.3 + - uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 03143d3e5b..62e571856b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,7 +78,7 @@ jobs: hatch env run --env "$HATCH_ENV" run-coverage - name: Upload coverage if: ${{ matrix.dependency-set == 'optional' && matrix.os == 'ubuntu-latest' }} - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: tests @@ -125,7 +125,7 @@ jobs: run: | hatch env run --env "$HATCH_ENV" run-coverage - name: Upload coverage - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: tests diff --git a/.github/workflows/zarr-metadata-release.yml b/.github/workflows/zarr-metadata-release.yml index 809d502f16..9639fcfdd3 100644 --- a/.github/workflows/zarr-metadata-release.yml +++ b/.github/workflows/zarr-metadata-release.yml @@ -35,7 +35,7 @@ jobs: - name: Build run: hatch build - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: zarr-metadata-dist path: packages/zarr-metadata/dist @@ -45,7 +45,7 @@ jobs: needs: [build] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: zarr-metadata-dist path: dist @@ -76,7 +76,7 @@ jobs: id-token: write # required for OIDC trusted publishing attestations: write # required for artifact attestations steps: - - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: zarr-metadata-dist path: dist @@ -87,7 +87,7 @@ jobs: subject-path: dist/* - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 upload_testpypi: name: Upload to TestPyPI @@ -101,7 +101,7 @@ jobs: id-token: write attestations: write steps: - - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: zarr-metadata-dist path: dist @@ -112,6 +112,6 @@ jobs: subject-path: dist/* - name: Publish package to TestPyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index da19f22421..7ac4fe5d0e 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -32,4 +32,4 @@ jobs: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 From 5bd5f47ef3e005df81344928f70f70dac8d085d8 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Fri, 19 Jun 2026 14:18:32 +0200 Subject: [PATCH 2/2] feat(zarr-metadata): promote curated front door to top-level public API Promote a curated set of names to the top-level `zarr_metadata` namespace so consumers can import spec types directly (e.g. `from zarr_metadata import ArrayMetadataV3, ShardingIndexLocation, BLOSC_CNAME`). Deep submodule paths continue to work unchanged. Several promoted names get clearer spellings now that they appear bare at the top level: `Endian`->`Endianness`, `IndexLocation`->`ShardingIndexLocation`, `RoundingMode`->`CastRoundingMode`, `OutOfRangeMode`->`CastOutOfRangeMode`, `DateTimeUnit`->`NumpyTimeUnit`, `NamedConfig`->`NamedConfigV3`, and `MetadataFieldV3`->`MetadataV3` (matching the name `zarrs` uses for this `name`-or-`{name, configuration}` shape). Also adds the `NUMPY_TIME_UNIT` runtime constant paired with the `NumpyTimeUnit` Literal. Scoped to packages/zarr-metadata only; no zarr-python changes, so no deprecation shims are needed (zarr-python does not yet depend on zarr-metadata). A follow-up zarr-python PR will consume these names. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../zarr-metadata/changes/4083.feature.md | 21 ++ .../src/zarr_metadata/__init__.py | 295 +++++++++++++++++- .../src/zarr_metadata/_common.py | 2 +- .../src/zarr_metadata/v3/__init__.py | 4 +- .../src/zarr_metadata/v3/_common.py | 8 +- .../src/zarr_metadata/v3/array.py | 22 +- .../src/zarr_metadata/v3/codec/__init__.py | 2 +- .../src/zarr_metadata/v3/codec/bytes.py | 10 +- .../src/zarr_metadata/v3/codec/cast_value.py | 24 +- .../v3/codec/sharding_indexed.py | 16 +- .../v3/data_type/numpy_datetime64.py | 6 +- .../v3/data_type/numpy_timedelta64.py | 26 +- .../src/zarr_metadata/v3/data_type/struct.py | 4 +- .../zarr-metadata/tests/test_public_api.py | 215 +++++++++++++ .../numpy_timedelta64/test_fixtures.py | 7 + 15 files changed, 606 insertions(+), 56 deletions(-) create mode 100644 packages/zarr-metadata/changes/4083.feature.md create mode 100644 packages/zarr-metadata/tests/test_public_api.py diff --git a/packages/zarr-metadata/changes/4083.feature.md b/packages/zarr-metadata/changes/4083.feature.md new file mode 100644 index 0000000000..c383495c1a --- /dev/null +++ b/packages/zarr-metadata/changes/4083.feature.md @@ -0,0 +1,21 @@ +Promoted a curated "front door" of names to the top-level `zarr_metadata` +namespace, so consumers can write e.g. `from zarr_metadata import +ArrayMetadataV3, ShardingIndexLocation, BLOSC_CNAME` instead of importing from +deep submodule paths. The front door covers every metadata-document TypedDict, +each codec/chunk-grid/chunk-key-encoding canonical type, the full data-type +trio for every dtype, and every constant + `Literal` pair. Deep submodule paths +continue to work unchanged. + +Several promoted names were given clearer, less ambiguous spellings than their +deep-module names, since they now appear bare at the top level: +`Endian`/`ENDIAN` → `Endianness`/`ENDIANNESS`, +`IndexLocation`/`INDEX_LOCATION` → `ShardingIndexLocation`/`SHARDING_INDEX_LOCATION`, +`RoundingMode`/`ROUNDING_MODE` → `CastRoundingMode`/`CAST_ROUNDING_MODE`, +`OutOfRangeMode`/`OUT_OF_RANGE_MODE` → `CastOutOfRangeMode`/`CAST_OUT_OF_RANGE_MODE`, +`DateTimeUnit` → `NumpyTimeUnit`, +`NamedConfig` → `NamedConfigV3`, and +`MetadataFieldV3` → `MetadataV3` (matching the name `zarrs` uses for this +`name`-or-`{name, configuration}` shape). + +Also added the `NUMPY_TIME_UNIT` runtime constant (a `Final` tuple paired with +the `NumpyTimeUnit` Literal) in `zarr_metadata.v3.data_type.numpy_timedelta64`. diff --git a/packages/zarr-metadata/src/zarr_metadata/__init__.py b/packages/zarr-metadata/src/zarr_metadata/__init__.py index 7c6461500e..46949570a2 100644 --- a/packages/zarr-metadata/src/zarr_metadata/__init__.py +++ b/packages/zarr-metadata/src/zarr_metadata/__init__.py @@ -1,7 +1,9 @@ from importlib.metadata import version -from zarr_metadata._common import JSONValue, NamedConfig +from zarr_metadata._common import JSONValue, NamedConfigV3 from zarr_metadata.v2.array import ( + ARRAY_DIMENSION_SEPARATOR_V2, + ARRAY_ORDER_V2, ArrayDimensionSeparatorV2, ArrayMetadataV2, ArrayMetadataV2Partial, @@ -13,35 +15,320 @@ from zarr_metadata.v2.codec import CodecMetadataV2 from zarr_metadata.v2.consolidated import ConsolidatedMetadataV2 from zarr_metadata.v2.group import GroupMetadataV2, GroupMetadataV2Partial, ZGroupMetadata -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 from zarr_metadata.v3.array import ArrayMetadataV3, ArrayMetadataV3Partial, ExtensionFieldV3 +from zarr_metadata.v3.chunk_grid.rectilinear import ( + RECTILINEAR_CHUNK_GRID_NAME, + RectilinearChunkGridMetadata, + RectilinearChunkGridName, +) +from zarr_metadata.v3.chunk_grid.regular import ( + REGULAR_CHUNK_GRID_NAME, + RegularChunkGridMetadata, + RegularChunkGridName, +) +from zarr_metadata.v3.chunk_key_encoding.default import ( + DEFAULT_CHUNK_KEY_ENCODING_NAME, + DEFAULT_CHUNK_KEY_ENCODING_SEPARATOR, + DefaultChunkKeyEncodingMetadata, + DefaultChunkKeyEncodingName, + DefaultChunkKeyEncodingSeparator, +) +from zarr_metadata.v3.chunk_key_encoding.v2 import ( + V2_CHUNK_KEY_ENCODING_NAME, + V2_CHUNK_KEY_ENCODING_SEPARATOR, + V2ChunkKeyEncodingMetadata, + V2ChunkKeyEncodingName, + V2ChunkKeyEncodingSeparator, +) +from zarr_metadata.v3.codec.blosc import ( + BLOSC_CNAME, + BLOSC_CODEC_NAME, + BLOSC_SHUFFLE, + BloscCName, + BloscCodecMetadata, + BloscCodecName, + BloscShuffle, +) +from zarr_metadata.v3.codec.bytes import ( + BYTES_CODEC_NAME, + ENDIANNESS, + BytesCodecMetadata, + BytesCodecName, + Endianness, +) +from zarr_metadata.v3.codec.cast_value import ( + CAST_OUT_OF_RANGE_MODE, + CAST_ROUNDING_MODE, + CAST_VALUE_CODEC_NAME, + CastOutOfRangeMode, + CastRoundingMode, + CastValueCodecMetadata, + CastValueCodecName, +) +from zarr_metadata.v3.codec.crc32c import CRC32C_CODEC_NAME, Crc32cCodecMetadata, Crc32cCodecName +from zarr_metadata.v3.codec.gzip import GZIP_CODEC_NAME, GzipCodecMetadata, GzipCodecName +from zarr_metadata.v3.codec.scale_offset import ( + SCALE_OFFSET_CODEC_NAME, + ScaleOffsetCodecMetadata, + ScaleOffsetCodecName, +) +from zarr_metadata.v3.codec.sharding_indexed import ( + SHARDING_INDEX_LOCATION, + SHARDING_INDEXED_CODEC_NAME, + ShardingIndexedCodecMetadata, + ShardingIndexedCodecName, + ShardingIndexLocation, +) +from zarr_metadata.v3.codec.transpose import ( + TRANSPOSE_CODEC_NAME, + TransposeCodecMetadata, + TransposeCodecName, +) +from zarr_metadata.v3.codec.zstd import ZSTD_CODEC_NAME, ZstdCodecMetadata, ZstdCodecName from zarr_metadata.v3.consolidated import ConsolidatedMetadataV3 +from zarr_metadata.v3.data_type.bool import ( + BOOL_DATA_TYPE_NAME, + BoolDataTypeName, + BoolFillValue, +) +from zarr_metadata.v3.data_type.bytes import ( + BYTES_DATA_TYPE_NAME, + BytesDataTypeName, + BytesFillValue, +) +from zarr_metadata.v3.data_type.complex64 import ( + COMPLEX64_DATA_TYPE_NAME, + Complex64DataTypeName, + Complex64FillValue, +) +from zarr_metadata.v3.data_type.complex128 import ( + COMPLEX128_DATA_TYPE_NAME, + Complex128DataTypeName, + Complex128FillValue, +) +from zarr_metadata.v3.data_type.float16 import ( + FLOAT16_DATA_TYPE_NAME, + Float16DataTypeName, + Float16FillValue, +) +from zarr_metadata.v3.data_type.float32 import ( + FLOAT32_DATA_TYPE_NAME, + Float32DataTypeName, + Float32FillValue, +) +from zarr_metadata.v3.data_type.float64 import ( + FLOAT64_DATA_TYPE_NAME, + Float64DataTypeName, + Float64FillValue, +) +from zarr_metadata.v3.data_type.int8 import ( + INT8_DATA_TYPE_NAME, + Int8DataTypeName, + Int8FillValue, +) +from zarr_metadata.v3.data_type.int16 import ( + INT16_DATA_TYPE_NAME, + Int16DataTypeName, + Int16FillValue, +) +from zarr_metadata.v3.data_type.int32 import ( + INT32_DATA_TYPE_NAME, + Int32DataTypeName, + Int32FillValue, +) +from zarr_metadata.v3.data_type.int64 import ( + INT64_DATA_TYPE_NAME, + Int64DataTypeName, + Int64FillValue, +) +from zarr_metadata.v3.data_type.numpy_datetime64 import ( + NUMPY_DATETIME64_DATA_TYPE_NAME, + NumpyDatetime64DataTypeName, + NumpyDatetime64FillValue, +) +from zarr_metadata.v3.data_type.numpy_timedelta64 import ( + NUMPY_TIME_UNIT, + NUMPY_TIMEDELTA64_DATA_TYPE_NAME, + NumpyTimedelta64DataTypeName, + NumpyTimedelta64FillValue, + NumpyTimeUnit, +) +from zarr_metadata.v3.data_type.raw import RawBytesDataTypeName, RawBytesFillValue +from zarr_metadata.v3.data_type.string import ( + STRING_DATA_TYPE_NAME, + StringDataTypeName, + StringFillValue, +) +from zarr_metadata.v3.data_type.struct import ( + STRUCT_DATA_TYPE_NAME, + StructDataTypeName, + StructFillValue, +) +from zarr_metadata.v3.data_type.uint8 import ( + UINT8_DATA_TYPE_NAME, + Uint8DataTypeName, + Uint8FillValue, +) +from zarr_metadata.v3.data_type.uint16 import ( + UINT16_DATA_TYPE_NAME, + Uint16DataTypeName, + Uint16FillValue, +) +from zarr_metadata.v3.data_type.uint32 import ( + UINT32_DATA_TYPE_NAME, + Uint32DataTypeName, + Uint32FillValue, +) +from zarr_metadata.v3.data_type.uint64 import ( + UINT64_DATA_TYPE_NAME, + Uint64DataTypeName, + Uint64FillValue, +) from zarr_metadata.v3.group import GroupMetadataV3, GroupMetadataV3Partial __version__ = version("zarr-metadata") __all__ = [ + "ARRAY_DIMENSION_SEPARATOR_V2", + "ARRAY_ORDER_V2", + "BLOSC_CNAME", + "BLOSC_CODEC_NAME", + "BLOSC_SHUFFLE", + "BOOL_DATA_TYPE_NAME", + "BYTES_CODEC_NAME", + "BYTES_DATA_TYPE_NAME", + "CAST_OUT_OF_RANGE_MODE", + "CAST_ROUNDING_MODE", + "CAST_VALUE_CODEC_NAME", + "COMPLEX64_DATA_TYPE_NAME", + "COMPLEX128_DATA_TYPE_NAME", + "CRC32C_CODEC_NAME", + "DEFAULT_CHUNK_KEY_ENCODING_NAME", + "DEFAULT_CHUNK_KEY_ENCODING_SEPARATOR", + "ENDIANNESS", + "FLOAT16_DATA_TYPE_NAME", + "FLOAT32_DATA_TYPE_NAME", + "FLOAT64_DATA_TYPE_NAME", + "GZIP_CODEC_NAME", + "INT8_DATA_TYPE_NAME", + "INT16_DATA_TYPE_NAME", + "INT32_DATA_TYPE_NAME", + "INT64_DATA_TYPE_NAME", + "NUMPY_DATETIME64_DATA_TYPE_NAME", + "NUMPY_TIMEDELTA64_DATA_TYPE_NAME", + "NUMPY_TIME_UNIT", + "RECTILINEAR_CHUNK_GRID_NAME", + "REGULAR_CHUNK_GRID_NAME", + "SCALE_OFFSET_CODEC_NAME", + "SHARDING_INDEXED_CODEC_NAME", + "SHARDING_INDEX_LOCATION", + "STRING_DATA_TYPE_NAME", + "STRUCT_DATA_TYPE_NAME", + "TRANSPOSE_CODEC_NAME", + "UINT8_DATA_TYPE_NAME", + "UINT16_DATA_TYPE_NAME", + "UINT32_DATA_TYPE_NAME", + "UINT64_DATA_TYPE_NAME", + "V2_CHUNK_KEY_ENCODING_NAME", + "V2_CHUNK_KEY_ENCODING_SEPARATOR", + "ZSTD_CODEC_NAME", "ArrayDimensionSeparatorV2", "ArrayMetadataV2", "ArrayMetadataV2Partial", "ArrayMetadataV3", "ArrayMetadataV3Partial", "ArrayOrderV2", + "BloscCName", + "BloscCodecMetadata", + "BloscCodecName", + "BloscShuffle", + "BoolDataTypeName", + "BoolFillValue", + "BytesCodecMetadata", + "BytesCodecName", + "BytesDataTypeName", + "BytesFillValue", + "CastOutOfRangeMode", + "CastRoundingMode", + "CastValueCodecMetadata", + "CastValueCodecName", "CodecMetadataV2", + "Complex64DataTypeName", + "Complex64FillValue", + "Complex128DataTypeName", + "Complex128FillValue", "ConsolidatedMetadataV2", "ConsolidatedMetadataV3", + "Crc32cCodecMetadata", + "Crc32cCodecName", "DataTypeMetadataV2", + "DefaultChunkKeyEncodingMetadata", + "DefaultChunkKeyEncodingName", + "DefaultChunkKeyEncodingSeparator", + "Endianness", "ExtensionFieldV3", + "Float16DataTypeName", + "Float16FillValue", + "Float32DataTypeName", + "Float32FillValue", + "Float64DataTypeName", + "Float64FillValue", "GroupMetadataV2", "GroupMetadataV2Partial", "GroupMetadataV3", "GroupMetadataV3Partial", + "GzipCodecMetadata", + "GzipCodecName", + "Int8DataTypeName", + "Int8FillValue", + "Int16DataTypeName", + "Int16FillValue", + "Int32DataTypeName", + "Int32FillValue", + "Int64DataTypeName", + "Int64FillValue", "JSONValue", - "MetadataFieldV3", - "NamedConfig", + "MetadataV3", + "NamedConfigV3", + "NumpyDatetime64DataTypeName", + "NumpyDatetime64FillValue", + "NumpyTimeUnit", + "NumpyTimedelta64DataTypeName", + "NumpyTimedelta64FillValue", + "RawBytesDataTypeName", + "RawBytesFillValue", + "RectilinearChunkGridMetadata", + "RectilinearChunkGridName", + "RegularChunkGridMetadata", + "RegularChunkGridName", + "ScaleOffsetCodecMetadata", + "ScaleOffsetCodecName", + "ShardingIndexLocation", + "ShardingIndexedCodecMetadata", + "ShardingIndexedCodecName", + "StringDataTypeName", + "StringFillValue", + "StructDataTypeName", + "StructFillValue", + "TransposeCodecMetadata", + "TransposeCodecName", + "Uint8DataTypeName", + "Uint8FillValue", + "Uint16DataTypeName", + "Uint16FillValue", + "Uint32DataTypeName", + "Uint32FillValue", + "Uint64DataTypeName", + "Uint64FillValue", + "V2ChunkKeyEncodingMetadata", + "V2ChunkKeyEncodingName", + "V2ChunkKeyEncodingSeparator", "ZArrayMetadata", "ZAttrsMetadata", "ZGroupMetadata", + "ZstdCodecMetadata", + "ZstdCodecName", "__version__", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/_common.py b/packages/zarr-metadata/src/zarr_metadata/_common.py index f6064d863f..598a12e80c 100644 --- a/packages/zarr-metadata/src/zarr_metadata/_common.py +++ b/packages/zarr-metadata/src/zarr_metadata/_common.py @@ -24,7 +24,7 @@ """ -class NamedConfig(TypedDict): +class NamedConfigV3(TypedDict): """ Externally-tagged union member for a metadata field. diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/__init__.py b/packages/zarr-metadata/src/zarr_metadata/v3/__init__.py index 7699aa744d..c897f20d52 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/__init__.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/__init__.py @@ -1,6 +1,6 @@ """Zarr v3 metadata types.""" -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 from zarr_metadata.v3.array import ArrayMetadataV3, ExtensionFieldV3 from zarr_metadata.v3.consolidated import ConsolidatedMetadataV3 from zarr_metadata.v3.group import GroupMetadataV3 @@ -10,5 +10,5 @@ "ConsolidatedMetadataV3", "ExtensionFieldV3", "GroupMetadataV3", - "MetadataFieldV3", + "MetadataV3", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/_common.py b/packages/zarr-metadata/src/zarr_metadata/v3/_common.py index 8d8e21616a..3424587a43 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/_common.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/_common.py @@ -2,12 +2,12 @@ This module is private (underscore-prefixed) and exists to avoid circular imports between leaf modules and sub-package `__init__.py` re-exports. -Public consumers should import `MetadataFieldV3` from `zarr_metadata.v3`. +Public consumers should import `MetadataV3` from `zarr_metadata.v3`. """ -from zarr_metadata._common import NamedConfig +from zarr_metadata._common import NamedConfigV3 -MetadataFieldV3 = str | NamedConfig +MetadataV3 = str | NamedConfigV3 """The JSON shape of any v3 metadata extension-point entry: either a bare short-hand name string or a `{name, configuration}` envelope. @@ -19,5 +19,5 @@ __all__ = [ - "MetadataFieldV3", + "MetadataV3", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/array.py b/packages/zarr-metadata/src/zarr_metadata/v3/array.py index d9cea4aef4..a8b0fa3358 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/array.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/array.py @@ -6,7 +6,7 @@ from typing_extensions import TypedDict from zarr_metadata._common import JSONValue -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 class ExtensionFieldV3(TypedDict, extra_items=JSONValue): # type: ignore[call-arg] @@ -52,14 +52,14 @@ class ArrayMetadataV3(TypedDict, extra_items=ExtensionFieldV3): # type: ignore[ zarr_format: Literal[3] node_type: Literal["array"] - data_type: MetadataFieldV3 + data_type: MetadataV3 shape: tuple[int, ...] - chunk_grid: MetadataFieldV3 - chunk_key_encoding: MetadataFieldV3 + chunk_grid: MetadataV3 + chunk_key_encoding: MetadataV3 fill_value: JSONValue - codecs: tuple[MetadataFieldV3, ...] + codecs: tuple[MetadataV3, ...] attributes: NotRequired[Mapping[str, JSONValue]] - storage_transformers: NotRequired[tuple[MetadataFieldV3, ...]] + storage_transformers: NotRequired[tuple[MetadataV3, ...]] dimension_names: NotRequired[tuple[str | None, ...]] @@ -88,14 +88,14 @@ class ArrayMetadataV3Partial(TypedDict, total=False, extra_items=ExtensionFieldV zarr_format: Literal[3] node_type: Literal["array"] - data_type: MetadataFieldV3 + data_type: MetadataV3 shape: tuple[int, ...] - chunk_grid: MetadataFieldV3 - chunk_key_encoding: MetadataFieldV3 + chunk_grid: MetadataV3 + chunk_key_encoding: MetadataV3 fill_value: JSONValue - codecs: tuple[MetadataFieldV3, ...] + codecs: tuple[MetadataV3, ...] attributes: NotRequired[Mapping[str, JSONValue]] - storage_transformers: NotRequired[tuple[MetadataFieldV3, ...]] + storage_transformers: NotRequired[tuple[MetadataV3, ...]] dimension_names: NotRequired[tuple[str | None, ...]] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/codec/__init__.py b/packages/zarr-metadata/src/zarr_metadata/v3/codec/__init__.py index 8cc819496d..b4f357117f 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/codec/__init__.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/codec/__init__.py @@ -11,7 +11,7 @@ `CodecConfiguration`, etc., import directly from the leaf submodule. For the field-level "any codec entry" alias (used in array metadata's -`codecs` list and in sharding's inner pipelines), import `MetadataFieldV3` +`codecs` list and in sharding's inner pipelines), import `MetadataV3` from `zarr_metadata.v3`. See https://zarr-specs.readthedocs.io/en/latest/v3/codecs/index.html diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/codec/bytes.py b/packages/zarr-metadata/src/zarr_metadata/v3/codec/bytes.py index 522cbe10f5..04e746f898 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/codec/bytes.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/codec/bytes.py @@ -14,10 +14,10 @@ BytesCodecName = Literal["bytes"] """Literal type of the `name` field of the `bytes` codec.""" -Endian = Literal["little", "big"] +Endianness = Literal["little", "big"] """Literal type of byte order of multi-byte numeric data.""" -ENDIAN: Final = ("little", "big") +ENDIANNESS: Final = ("little", "big") """Tuple of permitted values for the `endian` field of the `bytes` codec.""" @@ -28,7 +28,7 @@ class BytesCodecConfiguration(TypedDict): The `endian` field is required for multi-byte data types. """ - endian: NotRequired[Endian] + endian: NotRequired[Endianness] class BytesCodecObject(TypedDict): @@ -55,10 +55,10 @@ class BytesCodecObject(TypedDict): __all__ = [ "BYTES_CODEC_NAME", - "ENDIAN", + "ENDIANNESS", "BytesCodecConfiguration", "BytesCodecMetadata", "BytesCodecName", "BytesCodecObject", - "Endian", + "Endianness", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/codec/cast_value.py b/packages/zarr-metadata/src/zarr_metadata/v3/codec/cast_value.py index fd6fb2ee4a..7e9b071669 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/codec/cast_value.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/codec/cast_value.py @@ -9,7 +9,7 @@ from typing_extensions import TypedDict from zarr_metadata._common import JSONValue -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 CAST_VALUE_CODEC_NAME: Final = "cast_value" """The `name` field value of the `cast_value` codec.""" @@ -17,7 +17,7 @@ CastValueCodecName = Literal["cast_value"] """Literal type of the `name` field of the `cast_value` codec.""" -RoundingMode = Literal[ +CastRoundingMode = Literal[ "nearest-even", "towards-zero", "towards-positive", @@ -29,7 +29,7 @@ Defaults to `"nearest-even"` if absent. """ -ROUNDING_MODE: Final = ( +CAST_ROUNDING_MODE: Final = ( "nearest-even", "towards-zero", "towards-positive", @@ -38,13 +38,13 @@ ) """Tuple of permitted values for the `rounding` field of the `cast_value` codec.""" -OutOfRangeMode = Literal["clamp", "wrap"] +CastOutOfRangeMode = Literal["clamp", "wrap"] """Literal type of permitted values for the `out_of_range` configuration field. If absent, out-of-range values are an encoding/decoding error. """ -OUT_OF_RANGE_MODE: Final = ("clamp", "wrap") +CAST_OUT_OF_RANGE_MODE: Final = ("clamp", "wrap") """Tuple of permitted values for the `out_of_range` field of the `cast_value` codec.""" ScalarMapEntry = tuple[JSONValue, JSONValue] @@ -71,9 +71,9 @@ class CastValueCodecConfiguration(TypedDict): bare-string primitive name or a `{name, configuration}` envelope. """ - data_type: MetadataFieldV3 - rounding: NotRequired[RoundingMode] - out_of_range: NotRequired[OutOfRangeMode] + data_type: MetadataV3 + rounding: NotRequired[CastRoundingMode] + out_of_range: NotRequired[CastOutOfRangeMode] scalar_map: NotRequired[ScalarMap] @@ -93,15 +93,15 @@ class CastValueCodecObject(TypedDict): __all__ = [ + "CAST_OUT_OF_RANGE_MODE", + "CAST_ROUNDING_MODE", "CAST_VALUE_CODEC_NAME", - "OUT_OF_RANGE_MODE", - "ROUNDING_MODE", + "CastOutOfRangeMode", + "CastRoundingMode", "CastValueCodecConfiguration", "CastValueCodecMetadata", "CastValueCodecName", "CastValueCodecObject", - "OutOfRangeMode", - "RoundingMode", "ScalarMap", "ScalarMapEntry", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/codec/sharding_indexed.py b/packages/zarr-metadata/src/zarr_metadata/v3/codec/sharding_indexed.py index 93a0774e4e..a1488f7c30 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/codec/sharding_indexed.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/codec/sharding_indexed.py @@ -8,7 +8,7 @@ from typing_extensions import TypedDict -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 SHARDING_INDEXED_CODEC_NAME: Final = "sharding_indexed" """The `name` field value of the `sharding_indexed` codec.""" @@ -16,10 +16,10 @@ ShardingIndexedCodecName = Literal["sharding_indexed"] """Literal type of the `name` field of the `sharding_indexed` codec.""" -IndexLocation = Literal["start", "end"] +ShardingIndexLocation = Literal["start", "end"] """Literal type of the position of the shard index within the encoded shard.""" -INDEX_LOCATION: Final = ("start", "end") +SHARDING_INDEX_LOCATION: Final = ("start", "end") """Tuple of permitted values for the `index_location` field of the `sharding_indexed` codec.""" @@ -40,9 +40,9 @@ class ShardingIndexedCodecConfiguration(TypedDict): """ chunk_shape: tuple[int, ...] - codecs: tuple[MetadataFieldV3, ...] - index_codecs: tuple[MetadataFieldV3, ...] - index_location: NotRequired[IndexLocation] + codecs: tuple[MetadataV3, ...] + index_codecs: tuple[MetadataV3, ...] + index_location: NotRequired[ShardingIndexLocation] class ShardingIndexedCodecObject(TypedDict): @@ -61,9 +61,9 @@ class ShardingIndexedCodecObject(TypedDict): """ __all__ = [ - "INDEX_LOCATION", "SHARDING_INDEXED_CODEC_NAME", - "IndexLocation", + "SHARDING_INDEX_LOCATION", + "ShardingIndexLocation", "ShardingIndexedCodecConfiguration", "ShardingIndexedCodecMetadata", "ShardingIndexedCodecName", diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_datetime64.py b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_datetime64.py index 243d5fb6f6..8784160f71 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_datetime64.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_datetime64.py @@ -14,7 +14,7 @@ NumpyDatetime64DataTypeName = Literal["numpy.datetime64"] """Literal type of the `name` field of the `numpy.datetime64` data type.""" -DateTimeUnit = Literal[ +NumpyTimeUnit = Literal[ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "μs", "ns", "ps", "fs", "as", "generic" ] """Time unit codes used by numpy.datetime64.""" @@ -32,7 +32,7 @@ class NumpyDatetime64Configuration(TypedDict): The multiplier relative to the unit. """ - unit: ReadOnly[DateTimeUnit] + unit: ReadOnly[NumpyTimeUnit] scale_factor: ReadOnly[int] @@ -52,9 +52,9 @@ class NumpyDatetime64(TypedDict): __all__ = [ "NUMPY_DATETIME64_DATA_TYPE_NAME", - "DateTimeUnit", "NumpyDatetime64", "NumpyDatetime64Configuration", "NumpyDatetime64DataTypeName", "NumpyDatetime64FillValue", + "NumpyTimeUnit", ] diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_timedelta64.py b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_timedelta64.py index 41e35e7aae..f5c8c77bf8 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_timedelta64.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/numpy_timedelta64.py @@ -14,11 +14,30 @@ NumpyTimedelta64DataTypeName = Literal["numpy.timedelta64"] """Literal type of the `name` field of the `numpy.timedelta64` data type.""" -DateTimeUnit = Literal[ +NumpyTimeUnit = Literal[ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "μs", "ns", "ps", "fs", "as", "generic" ] """Time unit codes used by numpy.timedelta64.""" +NUMPY_TIME_UNIT: Final = ( + "Y", + "M", + "W", + "D", + "h", + "m", + "s", + "ms", + "us", + "μs", + "ns", + "ps", + "fs", + "as", + "generic", +) +"""Runtime tuple of the permitted `numpy.timedelta64`/`numpy.datetime64` unit strings.""" + class NumpyTimedelta64Configuration(TypedDict): """ @@ -32,7 +51,7 @@ class NumpyTimedelta64Configuration(TypedDict): The multiplier relative to the unit. """ - unit: ReadOnly[DateTimeUnit] + unit: ReadOnly[NumpyTimeUnit] scale_factor: ReadOnly[int] @@ -52,7 +71,8 @@ class NumpyTimedelta64(TypedDict): __all__ = [ "NUMPY_TIMEDELTA64_DATA_TYPE_NAME", - "DateTimeUnit", + "NUMPY_TIME_UNIT", + "NumpyTimeUnit", "NumpyTimedelta64", "NumpyTimedelta64Configuration", "NumpyTimedelta64DataTypeName", diff --git a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/struct.py b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/struct.py index 282bcc83d6..5291e5c309 100644 --- a/packages/zarr-metadata/src/zarr_metadata/v3/data_type/struct.py +++ b/packages/zarr-metadata/src/zarr_metadata/v3/data_type/struct.py @@ -10,7 +10,7 @@ from typing_extensions import ReadOnly, TypedDict from zarr_metadata._common import JSONValue -from zarr_metadata.v3._common import MetadataFieldV3 +from zarr_metadata.v3._common import MetadataV3 STRUCT_DATA_TYPE_NAME: Final = "struct" """The `name` field value of the `struct` data type.""" @@ -33,7 +33,7 @@ class StructField(TypedDict): """ name: ReadOnly[str] - data_type: ReadOnly[MetadataFieldV3] + data_type: ReadOnly[MetadataV3] class StructConfiguration(TypedDict): diff --git a/packages/zarr-metadata/tests/test_public_api.py b/packages/zarr-metadata/tests/test_public_api.py new file mode 100644 index 0000000000..d3270579c3 --- /dev/null +++ b/packages/zarr-metadata/tests/test_public_api.py @@ -0,0 +1,215 @@ +"""Test that the curated front-door names are accessible from the top-level zarr_metadata package.""" + +import re +from typing import get_args + +import zarr_metadata as zm + + +def _group_rank(s: str) -> int: + """RUF022 groups `__all__` as: SCREAMING_SNAKE (0), then TitleCase (1), then dunders (2). + + The exact intra-group ordering is ruff's own natural sort and is enforced by + ruff itself (pre-commit + CI); this test only asserts the grouping, not the + fragile tie-breaking, so it can't drift out of sync with ruff's implementation. + """ + if s.startswith("__") and s.endswith("__"): + return 2 + stripped = re.sub(r"[\d_]", "", s) + return 0 if stripped.isupper() else 1 + + +EXPECTED = [ + # Category A — metadata-document types + "ArrayMetadataV2", + "ArrayMetadataV2Partial", + "ZArrayMetadata", + "GroupMetadataV2", + "GroupMetadataV2Partial", + "ZGroupMetadata", + "ConsolidatedMetadataV2", + "ZAttrsMetadata", + "CodecMetadataV2", + "ArrayMetadataV3", + "ArrayMetadataV3Partial", + "ExtensionFieldV3", + "GroupMetadataV3", + "GroupMetadataV3Partial", + "ConsolidatedMetadataV3", + "NamedConfigV3", + "MetadataV3", + "JSONValue", + # v2 data-type encoding union + "DataTypeMetadataV2", + # Category B — codec canonical unions + "BloscCodecMetadata", + "BytesCodecMetadata", + "CastValueCodecMetadata", + "Crc32cCodecMetadata", + "GzipCodecMetadata", + "ScaleOffsetCodecMetadata", + "ShardingIndexedCodecMetadata", + "TransposeCodecMetadata", + "ZstdCodecMetadata", + # Category C — grid/key canonical unions + "RegularChunkGridMetadata", + "RectilinearChunkGridMetadata", + "DefaultChunkKeyEncodingMetadata", + "V2ChunkKeyEncodingMetadata", + # Category D — dtype trios + # bool + "BoolDataTypeName", + "BOOL_DATA_TYPE_NAME", + "BoolFillValue", + # int8/16/32/64 + "Int8DataTypeName", + "INT8_DATA_TYPE_NAME", + "Int8FillValue", + "Int16DataTypeName", + "INT16_DATA_TYPE_NAME", + "Int16FillValue", + "Int32DataTypeName", + "INT32_DATA_TYPE_NAME", + "Int32FillValue", + "Int64DataTypeName", + "INT64_DATA_TYPE_NAME", + "Int64FillValue", + # uint8/16/32/64 (actual casing is Uint, not UInt) + "Uint8DataTypeName", + "UINT8_DATA_TYPE_NAME", + "Uint8FillValue", + "Uint16DataTypeName", + "UINT16_DATA_TYPE_NAME", + "Uint16FillValue", + "Uint32DataTypeName", + "UINT32_DATA_TYPE_NAME", + "Uint32FillValue", + "Uint64DataTypeName", + "UINT64_DATA_TYPE_NAME", + "Uint64FillValue", + # float16/32/64 + "Float16DataTypeName", + "FLOAT16_DATA_TYPE_NAME", + "Float16FillValue", + "Float32DataTypeName", + "FLOAT32_DATA_TYPE_NAME", + "Float32FillValue", + "Float64DataTypeName", + "FLOAT64_DATA_TYPE_NAME", + "Float64FillValue", + # complex64/128 + "Complex64DataTypeName", + "COMPLEX64_DATA_TYPE_NAME", + "Complex64FillValue", + "Complex128DataTypeName", + "COMPLEX128_DATA_TYPE_NAME", + "Complex128FillValue", + # bytes + "BytesDataTypeName", + "BYTES_DATA_TYPE_NAME", + "BytesFillValue", + # string + "StringDataTypeName", + "STRING_DATA_TYPE_NAME", + "StringFillValue", + # numpy_datetime64 + "NumpyDatetime64DataTypeName", + "NUMPY_DATETIME64_DATA_TYPE_NAME", + "NumpyDatetime64FillValue", + # numpy_timedelta64 + "NumpyTimedelta64DataTypeName", + "NUMPY_TIMEDELTA64_DATA_TYPE_NAME", + "NumpyTimedelta64FillValue", + # struct + "StructDataTypeName", + "STRUCT_DATA_TYPE_NAME", + "StructFillValue", + # raw (no _DATA_TYPE_NAME constant) + "RawBytesDataTypeName", + "RawBytesFillValue", + # Category E — constant+Literal pairs + "ARRAY_ORDER_V2", + "ArrayOrderV2", + "ARRAY_DIMENSION_SEPARATOR_V2", + "ArrayDimensionSeparatorV2", + "ENDIANNESS", + "Endianness", + "BYTES_CODEC_NAME", + "BytesCodecName", + "BLOSC_CODEC_NAME", + "BloscCodecName", + "BLOSC_CNAME", + "BloscCName", + "BLOSC_SHUFFLE", + "BloscShuffle", + "CAST_ROUNDING_MODE", + "CastRoundingMode", + "CAST_OUT_OF_RANGE_MODE", + "CastOutOfRangeMode", + "CAST_VALUE_CODEC_NAME", + "CastValueCodecName", + "CRC32C_CODEC_NAME", + "Crc32cCodecName", + "GZIP_CODEC_NAME", + "GzipCodecName", + "SCALE_OFFSET_CODEC_NAME", + "ScaleOffsetCodecName", + "SHARDING_INDEX_LOCATION", + "ShardingIndexLocation", + "SHARDING_INDEXED_CODEC_NAME", + "ShardingIndexedCodecName", + "TRANSPOSE_CODEC_NAME", + "TransposeCodecName", + "ZSTD_CODEC_NAME", + "ZstdCodecName", + "REGULAR_CHUNK_GRID_NAME", + "RegularChunkGridName", + "RECTILINEAR_CHUNK_GRID_NAME", + "RectilinearChunkGridName", + "DEFAULT_CHUNK_KEY_ENCODING_NAME", + "DefaultChunkKeyEncodingName", + "DEFAULT_CHUNK_KEY_ENCODING_SEPARATOR", + "DefaultChunkKeyEncodingSeparator", + "V2_CHUNK_KEY_ENCODING_NAME", + "V2ChunkKeyEncodingName", + "V2_CHUNK_KEY_ENCODING_SEPARATOR", + "V2ChunkKeyEncodingSeparator", + "NUMPY_TIME_UNIT", + "NumpyTimeUnit", +] + + +def test_front_door_names_public() -> None: + missing = [n for n in EXPECTED if n not in zm.__all__ or not hasattr(zm, n)] + assert not missing, f"missing from top-level API: {missing}" + + +def test_front_door_is_exactly_expected() -> None: + """`__all__` must contain exactly the curated names (plus `__version__`). + + Guards against a name being promoted to the front door without a + corresponding, deliberate entry in `EXPECTED` — i.e. an accidental + addition to the public API surface. + """ + assert set(zm.__all__) - {"__version__"} == set(EXPECTED) + + +def test_all_is_grouped_and_unique() -> None: + ranks = [_group_rank(n) for n in zm.__all__] + assert ranks == sorted(ranks), "`__all__` groups out of order (SCREAMING, TitleCase, dunder)" + assert len(zm.__all__) == len(set(zm.__all__)) + + +def test_promoted_pairs_drift() -> None: + pairs = [ + (zm.ENDIANNESS, zm.Endianness), + (zm.BLOSC_CNAME, zm.BloscCName), + (zm.BLOSC_SHUFFLE, zm.BloscShuffle), + (zm.SHARDING_INDEX_LOCATION, zm.ShardingIndexLocation), + (zm.NUMPY_TIME_UNIT, zm.NumpyTimeUnit), + (zm.CAST_ROUNDING_MODE, zm.CastRoundingMode), + (zm.CAST_OUT_OF_RANGE_MODE, zm.CastOutOfRangeMode), + (zm.ARRAY_ORDER_V2, zm.ArrayOrderV2), + ] + for const, lit in pairs: + assert set(const) == set(get_args(lit)) diff --git a/packages/zarr-metadata/tests/v3/data_type/numpy_timedelta64/test_fixtures.py b/packages/zarr-metadata/tests/v3/data_type/numpy_timedelta64/test_fixtures.py index 1d4bd86a2d..2a6c651582 100644 --- a/packages/zarr-metadata/tests/v3/data_type/numpy_timedelta64/test_fixtures.py +++ b/packages/zarr-metadata/tests/v3/data_type/numpy_timedelta64/test_fixtures.py @@ -4,13 +4,16 @@ import json from pathlib import Path +from typing import get_args import pytest from pydantic import TypeAdapter from zarr_metadata.v3.data_type.numpy_timedelta64 import ( + NUMPY_TIME_UNIT, NumpyTimedelta64, NumpyTimedelta64FillValue, + NumpyTimeUnit, ) DIR = Path(__file__).parent @@ -24,3 +27,7 @@ def test_data_type() -> None: @pytest.mark.parametrize("case", FILL_VALUES.values(), ids=list(FILL_VALUES)) def test_fill_value(case: object) -> None: TypeAdapter(NumpyTimedelta64FillValue).validate_python(case) + + +def test_time_unit_constant_matches_literal() -> None: + assert set(NUMPY_TIME_UNIT) == set(get_args(NumpyTimeUnit))