zarr-cm 0.1.0__tar.gz → 0.2.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.
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/PKG-INFO +1 -1
- zarr_cm-0.2.0/docs/api.md +42 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/docs/index.md +34 -0
- zarr_cm-0.2.0/src/zarr_cm/__init__.py +275 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/_version.py +2 -2
- zarr_cm-0.2.0/tests/test_multi.py +238 -0
- zarr_cm-0.1.0/docs/api.md +0 -26
- zarr_cm-0.1.0/src/zarr_cm/__init__.py +0 -44
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.copier-answers.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.git_archival.txt +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.gitattributes +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.github/CONTRIBUTING.md +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.github/dependabot.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.github/release.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.github/workflows/cd.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.github/workflows/ci.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.gitignore +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.pre-commit-config.yaml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/.readthedocs.yaml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/LICENSE +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/README.md +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/mkdocs.yml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/noxfile.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/pyproject.toml +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/_core.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/_version.pyi +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/geo_proj.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/license.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/multiscales.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/py.typed +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/spatial.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/src/zarr_cm/uom.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/conftest.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/geo-proj.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/license.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/multiscales.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/spatial.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/uom.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/schemas/zarr-conventions-schema.json +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_docs.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_geo_proj.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_license.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_multiscales.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_package.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_spatial.py +0 -0
- {zarr_cm-0.1.0 → zarr_cm-0.2.0}/tests/test_uom.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zarr-cm
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python implementation of Zarr Conventions Metadata
|
|
5
5
|
Project-URL: Homepage, https://github.com/zarr-conventions/zarr-cm
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/zarr-conventions/zarr-cm/issues
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## Multi-convention
|
|
4
|
+
|
|
5
|
+
<!-- prettier-ignore -->
|
|
6
|
+
::: zarr_cm
|
|
7
|
+
options:
|
|
8
|
+
members:
|
|
9
|
+
- CONVENTION_NAMES
|
|
10
|
+
- ALL_CONVENTION_KEYS
|
|
11
|
+
- MultiConventionAttrs
|
|
12
|
+
- create_many
|
|
13
|
+
- validate_many
|
|
14
|
+
- validate_all
|
|
15
|
+
- insert_many
|
|
16
|
+
- extract_many
|
|
17
|
+
- extract_all
|
|
18
|
+
|
|
19
|
+
## Core
|
|
20
|
+
|
|
21
|
+
<!-- prettier-ignore -->
|
|
22
|
+
::: zarr_cm._core
|
|
23
|
+
|
|
24
|
+
## geo-proj
|
|
25
|
+
|
|
26
|
+
::: zarr_cm.geo_proj
|
|
27
|
+
|
|
28
|
+
## spatial
|
|
29
|
+
|
|
30
|
+
::: zarr_cm.spatial
|
|
31
|
+
|
|
32
|
+
## multiscales
|
|
33
|
+
|
|
34
|
+
::: zarr_cm.multiscales
|
|
35
|
+
|
|
36
|
+
## license
|
|
37
|
+
|
|
38
|
+
::: zarr_cm.license
|
|
39
|
+
|
|
40
|
+
## uom
|
|
41
|
+
|
|
42
|
+
::: zarr_cm.uom
|
|
@@ -73,3 +73,37 @@ print(extracted)
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
<!-- blacken-docs:on -->
|
|
76
|
+
|
|
77
|
+
## Multiple conventions
|
|
78
|
+
|
|
79
|
+
`create_many`, `insert_many`, `extract_many`, and `validate_many` work with
|
|
80
|
+
several conventions at once, keyed by convention name. `extract_all` and
|
|
81
|
+
`validate_all` are shortcuts that operate on all known conventions.
|
|
82
|
+
|
|
83
|
+
<!-- blacken-docs:off -->
|
|
84
|
+
<!-- prettier-ignore -->
|
|
85
|
+
```python
|
|
86
|
+
from zarr_cm import create_many, extract_all
|
|
87
|
+
|
|
88
|
+
# Create attributes with multiple conventions at once
|
|
89
|
+
attrs = create_many(
|
|
90
|
+
{
|
|
91
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
92
|
+
"spatial": {"spatial:dimensions": ["y", "x"]},
|
|
93
|
+
"license": {"spdx": "MIT"},
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
print(sorted(attrs.keys()))
|
|
97
|
+
#> ['license', 'proj:code', 'spatial:dimensions', 'zarr_conventions']
|
|
98
|
+
|
|
99
|
+
# Extract all known conventions
|
|
100
|
+
remaining, extracted = extract_all(attrs)
|
|
101
|
+
print(remaining)
|
|
102
|
+
#> {}
|
|
103
|
+
print(sorted(extracted.keys()))
|
|
104
|
+
#> ['geo-proj', 'license', 'spatial']
|
|
105
|
+
print(extracted["geo-proj"])
|
|
106
|
+
#> {'proj:code': 'EPSG:4326'}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
<!-- blacken-docs:on -->
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026 Davis Bennett. All rights reserved.
|
|
3
|
+
|
|
4
|
+
zarr-cm: Python implementation of Zarr Conventions Metadata
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import typing
|
|
10
|
+
from typing import Any, Final, Literal, NotRequired, TypedDict
|
|
11
|
+
|
|
12
|
+
if typing.TYPE_CHECKING:
|
|
13
|
+
import types
|
|
14
|
+
from collections.abc import Iterable
|
|
15
|
+
|
|
16
|
+
from . import geo_proj, multiscales, spatial, uom
|
|
17
|
+
from . import license as license_
|
|
18
|
+
from ._core import (
|
|
19
|
+
ConventionAttrs,
|
|
20
|
+
ConventionMetadataObject,
|
|
21
|
+
validate_convention_metadata_object,
|
|
22
|
+
)
|
|
23
|
+
from ._version import version as __version__
|
|
24
|
+
from .geo_proj import GeoProjAttrs, GeoProjConventionAttrs
|
|
25
|
+
from .license import LicenseAttrs, LicenseConventionAttrs
|
|
26
|
+
from .multiscales import (
|
|
27
|
+
LayoutObject,
|
|
28
|
+
MultiscalesAttrs,
|
|
29
|
+
MultiscalesConventionAttrs,
|
|
30
|
+
Transform,
|
|
31
|
+
)
|
|
32
|
+
from .spatial import SpatialAttrs, SpatialConventionAttrs
|
|
33
|
+
from .uom import UCUM, UomAttrs, UomConventionAttrs
|
|
34
|
+
|
|
35
|
+
ConventionName = Literal["geo-proj", "spatial", "multiscales", "license", "uom"]
|
|
36
|
+
|
|
37
|
+
_REGISTRY: Final[dict[ConventionName, types.ModuleType]] = {
|
|
38
|
+
"geo-proj": geo_proj,
|
|
39
|
+
"spatial": spatial,
|
|
40
|
+
"multiscales": multiscales,
|
|
41
|
+
"license": license_,
|
|
42
|
+
"uom": uom,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
CONVENTION_NAMES: Final = frozenset(_REGISTRY)
|
|
46
|
+
|
|
47
|
+
ALL_CONVENTION_KEYS: Final = frozenset(
|
|
48
|
+
geo_proj.CONVENTION_KEYS
|
|
49
|
+
| spatial.CONVENTION_KEYS
|
|
50
|
+
| multiscales.CONVENTION_KEYS
|
|
51
|
+
| license_.CONVENTION_KEYS
|
|
52
|
+
| uom.CONVENTION_KEYS
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
MultiConventionAttrs = TypedDict(
|
|
56
|
+
"MultiConventionAttrs",
|
|
57
|
+
{
|
|
58
|
+
"zarr_conventions": NotRequired[list[ConventionMetadataObject]],
|
|
59
|
+
# geo-proj
|
|
60
|
+
"proj:code": NotRequired[str],
|
|
61
|
+
"proj:wkt2": NotRequired[str],
|
|
62
|
+
"proj:projjson": NotRequired[dict[str, Any]],
|
|
63
|
+
# spatial
|
|
64
|
+
"spatial:dimensions": NotRequired[list[str]],
|
|
65
|
+
"spatial:bbox": NotRequired[list[float]],
|
|
66
|
+
"spatial:transform_type": NotRequired[str],
|
|
67
|
+
"spatial:transform": NotRequired[list[float]],
|
|
68
|
+
"spatial:shape": NotRequired[list[int]],
|
|
69
|
+
"spatial:registration": NotRequired[str],
|
|
70
|
+
# multiscales
|
|
71
|
+
"multiscales": NotRequired[MultiscalesAttrs],
|
|
72
|
+
# license
|
|
73
|
+
"license": NotRequired[LicenseAttrs],
|
|
74
|
+
# uom
|
|
75
|
+
"uom": NotRequired[UomAttrs],
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _get_module(name: ConventionName) -> types.ModuleType:
|
|
81
|
+
"""Look up convention module by display name, raise ValueError if unknown."""
|
|
82
|
+
try:
|
|
83
|
+
return _REGISTRY[name]
|
|
84
|
+
except KeyError:
|
|
85
|
+
msg = f"Unknown convention {name!r}. Valid names: {sorted(CONVENTION_NAMES)}"
|
|
86
|
+
raise ValueError(msg) from None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _detect_conventions(attrs: dict[str, Any]) -> frozenset[ConventionName]:
|
|
90
|
+
"""Identify which conventions are present by matching UUIDs in zarr_conventions."""
|
|
91
|
+
uuids = {cmo.get("uuid") for cmo in attrs.get("zarr_conventions", [])}
|
|
92
|
+
return frozenset(name for name, mod in _REGISTRY.items() if mod.UUID in uuids)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def create_many(
|
|
96
|
+
conventions: dict[ConventionName, dict[str, Any]],
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
"""Create and insert multiple conventions into a single attributes dict.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
conventions
|
|
103
|
+
Mapping from convention display name (e.g. ``"geo-proj"``) to
|
|
104
|
+
already-formed convention data (the ``AttrsT`` value).
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
dict[str, Any]
|
|
109
|
+
A new attributes dict containing all convention data and a
|
|
110
|
+
combined ``zarr_conventions`` array.
|
|
111
|
+
"""
|
|
112
|
+
result: dict[str, Any] = {}
|
|
113
|
+
for name, data in conventions.items():
|
|
114
|
+
mod = _get_module(name)
|
|
115
|
+
mod.validate(data)
|
|
116
|
+
result = mod.insert(result, data, overwrite=True)
|
|
117
|
+
return result
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def validate_many(
|
|
121
|
+
attrs: dict[str, Any],
|
|
122
|
+
conventions: Iterable[ConventionName],
|
|
123
|
+
) -> dict[str, Any]:
|
|
124
|
+
"""Validate multiple conventions within an attributes dict.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
attrs
|
|
129
|
+
The attributes dict to validate.
|
|
130
|
+
conventions
|
|
131
|
+
Convention names to validate.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
dict[str, Any]
|
|
136
|
+
The input *attrs* (pass-through on success).
|
|
137
|
+
"""
|
|
138
|
+
for name in conventions:
|
|
139
|
+
mod = _get_module(name)
|
|
140
|
+
_, extracted = mod.extract(attrs)
|
|
141
|
+
mod.validate(dict(extracted))
|
|
142
|
+
return attrs
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def insert_many(
|
|
146
|
+
attrs: dict[str, Any],
|
|
147
|
+
conventions: dict[ConventionName, dict[str, Any]],
|
|
148
|
+
*,
|
|
149
|
+
overwrite: bool = False,
|
|
150
|
+
) -> dict[str, Any]:
|
|
151
|
+
"""Insert multiple conventions into an attributes dict.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
attrs
|
|
156
|
+
The existing attributes dict.
|
|
157
|
+
conventions
|
|
158
|
+
Mapping from convention display name to already-formed convention data.
|
|
159
|
+
overwrite
|
|
160
|
+
If False (default), raise ``ValueError`` when *attrs* already
|
|
161
|
+
contains keys present in a convention's data.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
dict[str, Any]
|
|
166
|
+
A new attributes dict with all convention data merged in.
|
|
167
|
+
"""
|
|
168
|
+
result = attrs
|
|
169
|
+
for name, data in conventions.items():
|
|
170
|
+
mod = _get_module(name)
|
|
171
|
+
mod.validate(data)
|
|
172
|
+
result = mod.insert(result, data, overwrite=overwrite)
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def extract_many(
|
|
177
|
+
attrs: dict[str, Any],
|
|
178
|
+
conventions: Iterable[ConventionName],
|
|
179
|
+
) -> tuple[dict[str, Any], dict[ConventionName, dict[str, Any]]]:
|
|
180
|
+
"""Extract multiple conventions from an attributes dict.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
attrs
|
|
185
|
+
The attributes dict to extract from.
|
|
186
|
+
conventions
|
|
187
|
+
Convention names to extract.
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
tuple[dict[str, Any], dict[str, dict[str, Any]]]
|
|
192
|
+
``(remaining_attrs, extracted)`` where *extracted* maps
|
|
193
|
+
convention names to their convention data dicts.
|
|
194
|
+
"""
|
|
195
|
+
remaining = attrs
|
|
196
|
+
extracted: dict[ConventionName, dict[str, Any]] = {}
|
|
197
|
+
for name in conventions:
|
|
198
|
+
mod = _get_module(name)
|
|
199
|
+
remaining, data = mod.extract(remaining)
|
|
200
|
+
extracted[name] = dict(data)
|
|
201
|
+
return remaining, extracted
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def validate_all(
|
|
205
|
+
attrs: dict[str, Any],
|
|
206
|
+
) -> dict[str, Any]:
|
|
207
|
+
"""Validate all detected conventions within an attributes dict.
|
|
208
|
+
|
|
209
|
+
Detects which conventions are present by matching UUIDs in
|
|
210
|
+
``zarr_conventions``, then validates each one.
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
attrs
|
|
215
|
+
The attributes dict to validate.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
dict[str, Any]
|
|
220
|
+
The input *attrs* (pass-through on success).
|
|
221
|
+
"""
|
|
222
|
+
return validate_many(attrs, _detect_conventions(attrs))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def extract_all(
|
|
226
|
+
attrs: dict[str, Any],
|
|
227
|
+
) -> tuple[dict[str, Any], dict[ConventionName, dict[str, Any]]]:
|
|
228
|
+
"""Extract all detected conventions from an attributes dict.
|
|
229
|
+
|
|
230
|
+
Detects which conventions are present by matching UUIDs in
|
|
231
|
+
``zarr_conventions``, then extracts each one.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
attrs
|
|
236
|
+
The attributes dict to extract from.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
tuple[dict[str, Any], dict[str, dict[str, Any]]]
|
|
241
|
+
``(remaining_attrs, extracted)`` where *extracted* maps
|
|
242
|
+
convention names to their convention data dicts.
|
|
243
|
+
"""
|
|
244
|
+
return extract_many(attrs, _detect_conventions(attrs))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
__all__ = [
|
|
248
|
+
"ALL_CONVENTION_KEYS",
|
|
249
|
+
"CONVENTION_NAMES",
|
|
250
|
+
"UCUM",
|
|
251
|
+
"ConventionAttrs",
|
|
252
|
+
"ConventionMetadataObject",
|
|
253
|
+
"ConventionName",
|
|
254
|
+
"GeoProjAttrs",
|
|
255
|
+
"GeoProjConventionAttrs",
|
|
256
|
+
"LayoutObject",
|
|
257
|
+
"LicenseAttrs",
|
|
258
|
+
"LicenseConventionAttrs",
|
|
259
|
+
"MultiConventionAttrs",
|
|
260
|
+
"MultiscalesAttrs",
|
|
261
|
+
"MultiscalesConventionAttrs",
|
|
262
|
+
"SpatialAttrs",
|
|
263
|
+
"SpatialConventionAttrs",
|
|
264
|
+
"Transform",
|
|
265
|
+
"UomAttrs",
|
|
266
|
+
"UomConventionAttrs",
|
|
267
|
+
"__version__",
|
|
268
|
+
"create_many",
|
|
269
|
+
"extract_all",
|
|
270
|
+
"extract_many",
|
|
271
|
+
"insert_many",
|
|
272
|
+
"validate_all",
|
|
273
|
+
"validate_convention_metadata_object",
|
|
274
|
+
"validate_many",
|
|
275
|
+
]
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.2.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 2, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, get_args
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import zarr_cm
|
|
8
|
+
from zarr_cm import (
|
|
9
|
+
ALL_CONVENTION_KEYS,
|
|
10
|
+
CONVENTION_NAMES,
|
|
11
|
+
ConventionName,
|
|
12
|
+
create_many,
|
|
13
|
+
extract_all,
|
|
14
|
+
extract_many,
|
|
15
|
+
insert_many,
|
|
16
|
+
validate_all,
|
|
17
|
+
validate_many,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_convention_names_constant() -> None:
|
|
22
|
+
assert (
|
|
23
|
+
frozenset({"geo-proj", "spatial", "multiscales", "license", "uom"})
|
|
24
|
+
== CONVENTION_NAMES
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_all_convention_keys_constant() -> None:
|
|
29
|
+
assert (
|
|
30
|
+
frozenset(
|
|
31
|
+
{
|
|
32
|
+
"proj:code",
|
|
33
|
+
"proj:wkt2",
|
|
34
|
+
"proj:projjson",
|
|
35
|
+
"spatial:dimensions",
|
|
36
|
+
"spatial:bbox",
|
|
37
|
+
"spatial:transform_type",
|
|
38
|
+
"spatial:transform",
|
|
39
|
+
"spatial:shape",
|
|
40
|
+
"spatial:registration",
|
|
41
|
+
"multiscales",
|
|
42
|
+
"license",
|
|
43
|
+
"uom",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
== ALL_CONVENTION_KEYS
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_create_many_single() -> None:
|
|
51
|
+
result = create_many({"geo-proj": {"proj:code": "EPSG:4326"}})
|
|
52
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
53
|
+
assert len(result["zarr_conventions"]) == 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_create_many_mixed() -> None:
|
|
57
|
+
result = create_many(
|
|
58
|
+
{
|
|
59
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
60
|
+
"license": {"spdx": "MIT"},
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
64
|
+
assert result["license"] == {"spdx": "MIT"}
|
|
65
|
+
assert len(result["zarr_conventions"]) == 2
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_create_many_all() -> None:
|
|
69
|
+
result = create_many(
|
|
70
|
+
{
|
|
71
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
72
|
+
"spatial": {"spatial:dimensions": ["y", "x"]},
|
|
73
|
+
"multiscales": {"layout": [{"asset": "0"}]},
|
|
74
|
+
"license": {"spdx": "MIT"},
|
|
75
|
+
"uom": {"ucum": {"unit": "kg"}},
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
assert len(result["zarr_conventions"]) == 5
|
|
79
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
80
|
+
assert result["spatial:dimensions"] == ["y", "x"]
|
|
81
|
+
assert result["multiscales"]["layout"] == [{"asset": "0"}]
|
|
82
|
+
assert result["license"] == {"spdx": "MIT"}
|
|
83
|
+
assert result["uom"]["ucum"]["unit"] == "kg"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_create_many_invalid_name() -> None:
|
|
87
|
+
with pytest.raises(ValueError, match="Unknown convention"):
|
|
88
|
+
create_many({"not-a-convention": {}}) # type: ignore[dict-item]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_create_many_invalid_data() -> None:
|
|
92
|
+
with pytest.raises(ValueError, match="Exactly one"):
|
|
93
|
+
create_many({"geo-proj": {}})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_validate_many() -> None:
|
|
97
|
+
attrs = create_many(
|
|
98
|
+
{
|
|
99
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
100
|
+
"license": {"spdx": "MIT"},
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
result = validate_many(attrs, ["geo-proj", "license"])
|
|
104
|
+
assert result is attrs
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_validate_many_subset() -> None:
|
|
108
|
+
attrs = create_many(
|
|
109
|
+
{
|
|
110
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
111
|
+
"license": {"spdx": "MIT"},
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
result = validate_many(attrs, ["geo-proj"])
|
|
115
|
+
assert result is attrs
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_validate_many_invalid() -> None:
|
|
119
|
+
attrs = create_many({"license": {"spdx": "MIT"}})
|
|
120
|
+
# Corrupt the license data
|
|
121
|
+
attrs["license"] = {}
|
|
122
|
+
with pytest.raises(ValueError, match="At least one"):
|
|
123
|
+
validate_many(attrs, ["license"])
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_validate_all() -> None:
|
|
127
|
+
attrs = create_many(
|
|
128
|
+
{
|
|
129
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
130
|
+
"license": {"spdx": "MIT"},
|
|
131
|
+
"uom": {"ucum": {"unit": "kg"}},
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
result = validate_all(attrs)
|
|
135
|
+
assert result is attrs
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_insert_many_empty_attrs() -> None:
|
|
139
|
+
result = insert_many(
|
|
140
|
+
{},
|
|
141
|
+
{
|
|
142
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
143
|
+
"license": {"spdx": "MIT"},
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
147
|
+
assert result["license"] == {"spdx": "MIT"}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_insert_many_preserves_attrs() -> None:
|
|
151
|
+
result = insert_many(
|
|
152
|
+
{"foo": "bar"},
|
|
153
|
+
{"geo-proj": {"proj:code": "EPSG:4326"}},
|
|
154
|
+
)
|
|
155
|
+
assert result["foo"] == "bar"
|
|
156
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_insert_many_collision_raises() -> None:
|
|
160
|
+
attrs = {"proj:code": "EPSG:3857"}
|
|
161
|
+
with pytest.raises(ValueError, match="overwritten"):
|
|
162
|
+
insert_many(attrs, {"geo-proj": {"proj:code": "EPSG:4326"}})
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_insert_many_overwrite() -> None:
|
|
166
|
+
attrs = {"proj:code": "EPSG:3857"}
|
|
167
|
+
result = insert_many(
|
|
168
|
+
attrs,
|
|
169
|
+
{"geo-proj": {"proj:code": "EPSG:4326"}},
|
|
170
|
+
overwrite=True,
|
|
171
|
+
)
|
|
172
|
+
assert result["proj:code"] == "EPSG:4326"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_extract_many() -> None:
|
|
176
|
+
attrs = create_many(
|
|
177
|
+
{
|
|
178
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
179
|
+
"license": {"spdx": "MIT"},
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
remaining, extracted = extract_many(attrs, ["geo-proj", "license"])
|
|
183
|
+
assert remaining == {}
|
|
184
|
+
assert extracted["geo-proj"] == {"proj:code": "EPSG:4326"}
|
|
185
|
+
assert extracted["license"] == {"spdx": "MIT"}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_extract_many_subset() -> None:
|
|
189
|
+
attrs = create_many(
|
|
190
|
+
{
|
|
191
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
192
|
+
"license": {"spdx": "MIT"},
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
remaining, extracted = extract_many(attrs, ["geo-proj"])
|
|
196
|
+
assert "geo-proj" in extracted
|
|
197
|
+
assert "license" not in extracted
|
|
198
|
+
# license data stays in remaining
|
|
199
|
+
assert remaining["license"] == {"spdx": "MIT"}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_extract_many_preserves_remaining() -> None:
|
|
203
|
+
attrs = create_many({"geo-proj": {"proj:code": "EPSG:4326"}})
|
|
204
|
+
attrs["foo"] = "bar"
|
|
205
|
+
remaining, extracted = extract_many(attrs, ["geo-proj"])
|
|
206
|
+
assert remaining == {"foo": "bar"}
|
|
207
|
+
assert extracted["geo-proj"] == {"proj:code": "EPSG:4326"}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_extract_all() -> None:
|
|
211
|
+
conventions: dict[ConventionName, dict[str, Any]] = {
|
|
212
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
213
|
+
"license": {"spdx": "MIT"},
|
|
214
|
+
}
|
|
215
|
+
attrs = create_many(conventions)
|
|
216
|
+
remaining, extracted = extract_all(attrs)
|
|
217
|
+
assert remaining == {}
|
|
218
|
+
assert extracted["geo-proj"] == {"proj:code": "EPSG:4326"}
|
|
219
|
+
assert extracted["license"] == {"spdx": "MIT"}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_convention_name_literal_matches_registry() -> None:
|
|
223
|
+
for name in get_args(ConventionName):
|
|
224
|
+
assert hasattr(zarr_cm, name.replace("-", "_")), (
|
|
225
|
+
f"{name!r} is not a module in zarr_cm"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_roundtrip() -> None:
|
|
230
|
+
conventions: dict[ConventionName, dict[str, Any]] = {
|
|
231
|
+
"geo-proj": {"proj:code": "EPSG:4326"},
|
|
232
|
+
"spatial": {"spatial:dimensions": ["y", "x"]},
|
|
233
|
+
"license": {"spdx": "MIT"},
|
|
234
|
+
}
|
|
235
|
+
attrs = create_many(conventions)
|
|
236
|
+
remaining, extracted = extract_many(attrs, conventions.keys())
|
|
237
|
+
assert remaining == {}
|
|
238
|
+
assert extracted == conventions
|
zarr_cm-0.1.0/docs/api.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# API Reference
|
|
2
|
-
|
|
3
|
-
## Core
|
|
4
|
-
|
|
5
|
-
<!-- prettier-ignore -->
|
|
6
|
-
::: zarr_cm._core
|
|
7
|
-
|
|
8
|
-
## geo-proj
|
|
9
|
-
|
|
10
|
-
::: zarr_cm.geo_proj
|
|
11
|
-
|
|
12
|
-
## spatial
|
|
13
|
-
|
|
14
|
-
::: zarr_cm.spatial
|
|
15
|
-
|
|
16
|
-
## multiscales
|
|
17
|
-
|
|
18
|
-
::: zarr_cm.multiscales
|
|
19
|
-
|
|
20
|
-
## license
|
|
21
|
-
|
|
22
|
-
::: zarr_cm.license
|
|
23
|
-
|
|
24
|
-
## uom
|
|
25
|
-
|
|
26
|
-
::: zarr_cm.uom
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Copyright (c) 2026 Davis Bennett. All rights reserved.
|
|
3
|
-
|
|
4
|
-
zarr-cm: Python implementation of Zarr Conventions Metadata
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from ._core import (
|
|
10
|
-
ConventionAttrs,
|
|
11
|
-
ConventionMetadataObject,
|
|
12
|
-
validate_convention_metadata_object,
|
|
13
|
-
)
|
|
14
|
-
from ._version import version as __version__
|
|
15
|
-
from .geo_proj import GeoProjAttrs, GeoProjConventionAttrs
|
|
16
|
-
from .license import LicenseAttrs, LicenseConventionAttrs
|
|
17
|
-
from .multiscales import (
|
|
18
|
-
LayoutObject,
|
|
19
|
-
MultiscalesAttrs,
|
|
20
|
-
MultiscalesConventionAttrs,
|
|
21
|
-
Transform,
|
|
22
|
-
)
|
|
23
|
-
from .spatial import SpatialAttrs, SpatialConventionAttrs
|
|
24
|
-
from .uom import UCUM, UomAttrs, UomConventionAttrs
|
|
25
|
-
|
|
26
|
-
__all__ = [
|
|
27
|
-
"UCUM",
|
|
28
|
-
"ConventionAttrs",
|
|
29
|
-
"ConventionMetadataObject",
|
|
30
|
-
"GeoProjAttrs",
|
|
31
|
-
"GeoProjConventionAttrs",
|
|
32
|
-
"LayoutObject",
|
|
33
|
-
"LicenseAttrs",
|
|
34
|
-
"LicenseConventionAttrs",
|
|
35
|
-
"MultiscalesAttrs",
|
|
36
|
-
"MultiscalesConventionAttrs",
|
|
37
|
-
"SpatialAttrs",
|
|
38
|
-
"SpatialConventionAttrs",
|
|
39
|
-
"Transform",
|
|
40
|
-
"UomAttrs",
|
|
41
|
-
"UomConventionAttrs",
|
|
42
|
-
"__version__",
|
|
43
|
-
"validate_convention_metadata_object",
|
|
44
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|