Skip to content

Commit 51b9431

Browse files
committed
Merge branch 'feat/numcodecs-protocol' of github.com:d-v-b/zarr-python into feat/v2-v3-codecs
2 parents f1ca290 + cee4389 commit 51b9431

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+722
-428
lines changed

.github/workflows/gpu_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
hatch env run -e gputest.py${{ matrix.python-version }}-${{ matrix.numpy-version }}-${{ matrix.dependency-set }} list-env
6464
- name: Run Tests
6565
run: |
66-
hatch env run --env gputest.py${{ matrix.python-version }}-${{ matrix.numpy-version }}-${{ matrix.dependency-set }} run-coverage
66+
hatch env run --env gputest.py${{ matrix.python-version }}-${{ matrix.numpy-version }}-${{ matrix.dependency-set }} run-coverage-gpu
6767
6868
- name: Upload coverage
6969
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ci:
66
default_stages: [pre-commit, pre-push]
77
repos:
88
- repo: https://github.com/astral-sh/ruff-pre-commit
9-
rev: v0.12.2
9+
rev: v0.12.7
1010
hooks:
1111
- id: ruff-check
1212
args: ["--fix", "--show-fixes"]
@@ -22,7 +22,7 @@ repos:
2222
- id: check-yaml
2323
- id: trailing-whitespace
2424
- repo: https://github.com/pre-commit/mirrors-mypy
25-
rev: v1.16.1
25+
rev: v1.17.1
2626
hooks:
2727
- id: mypy
2828
files: src|tests

changes/3098.misc.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Define Zarr-specific warning classes that subclass the Python built-in warnings.
2+
These classes makes it easier to control the visibility of warnings emitted by Zarr Python.
3+
See `zarr.errors` for these warning classes.

changes/3144.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure that -0.0 is not considered equal to 0.0 when checking if all the values in a chunk are equal to an array's fill value.```

docs/user-guide/arrays.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ built-in delta filter::
238238

239239
>>> import lzma
240240
>>> from numcodecs.zarr3 import LZMA
241+
>>> import warnings
242+
>>> warnings.filterwarnings("ignore", category=UserWarning)
241243
>>>
242244
>>> lzma_filters = [dict(id=lzma.FILTER_DELTA, dist=4), dict(id=lzma.FILTER_LZMA2, preset=1)]
243245
>>> compressors = LZMA(filters=lzma_filters)

docs/user-guide/consolidated_metadata.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ In Python, the consolidated metadata is available on the ``.consolidated_metadat
2727
attribute of the ``GroupMetadata`` object.
2828

2929
>>> import zarr
30+
>>> import warnings
31+
>>> warnings.filterwarnings("ignore", category=UserWarning)
3032
>>>
3133
>>> store = zarr.storage.MemoryStore()
3234
>>> group = zarr.create_group(store=store)

pyproject.toml

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ dependencies = [
150150
]
151151
features = ["test"]
152152

153+
[tool.hatch.envs.test.env-vars]
154+
# Required to test with a pytest plugin; see https://pytest-cov.readthedocs.io/en/latest/plugins.html
155+
COV_CORE_SOURCE = "src"
156+
COV_CORE_CONFIG = ".coveragerc"
157+
COV_CORE_DATAFILE = ".coverage.eager"
158+
153159
[[tool.hatch.envs.test.matrix]]
154160
python = ["3.11", "3.12", "3.13"]
155161
numpy = ["1.26", "2.2"]
@@ -161,25 +167,18 @@ matrix.deps.dependencies = [
161167
]
162168

163169
[tool.hatch.envs.test.scripts]
164-
run-coverage = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
165-
run-coverage-html = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report html --cov=src"
170+
run-coverage = "pytest --cov-config=pyproject.toml --cov=src --cov-append --cov-report xml --junitxml=junit.xml -o junit_family=legacy"
171+
run-coverage-html = "pytest --cov-config=pyproject.toml --cov=src --cov-append --cov-report html"
172+
run-coverage-gpu = "pip install cupy-cuda12x && pytest -m gpu --cov-config=pyproject.toml --cov=src --cov-append --cov-report xml --junitxml=junit.xml -o junit_family=legacy"
166173
run = "run-coverage --no-cov"
167174
run-pytest = "run"
168175
run-verbose = "run-coverage --verbose"
169176
run-mypy = "mypy src"
170177
run-hypothesis = "run-coverage -nauto --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful*"
171178
list-env = "pip list"
172179

173-
[tool.hatch.envs.doctest]
174-
features = ["test", "optional", "remote", "remote_tests"]
175-
description = "Test environment for doctests"
176-
177-
[tool.hatch.envs.doctest.scripts]
178-
run = "rm -r data/; pytest docs/user-guide --doctest-glob='*.rst'"
179-
fix = "rm -r data/; pytest docs/user-guide --doctest-glob='*.rst' --accept"
180-
list-env = "pip list"
181-
182180
[tool.hatch.envs.gputest]
181+
template = "test"
183182
dependencies = [
184183
"numpy~={matrix:numpy}",
185184
"universal_pathlib",
@@ -191,22 +190,8 @@ python = ["3.11", "3.12", "3.13"]
191190
numpy = ["1.26", "2.2"]
192191
version = ["minimal"]
193192

194-
[tool.hatch.envs.gputest.scripts]
195-
run-coverage = "pytest -m gpu --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
196-
run = "run-coverage --no-cov"
197-
run-verbose = "run-coverage --verbose"
198-
run-mypy = "mypy src"
199-
run-hypothesis = "run-coverage --hypothesis-profile ci --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful*"
200-
list-env = "pip list"
201-
202-
[tool.hatch.envs.docs]
203-
features = ['docs']
204-
205-
[tool.hatch.envs.docs.scripts]
206-
build = "cd docs && make html"
207-
serve = "sphinx-autobuild docs docs/_build --host 0.0.0.0"
208-
209193
[tool.hatch.envs.upstream]
194+
template = 'test'
210195
python = "3.13"
211196
dependencies = [
212197
'packaging @ git+https://github.com/pypa/packaging',
@@ -226,20 +211,12 @@ PIP_INDEX_URL = "https://pypi.anaconda.org/scientific-python-nightly-wheels/simp
226211
PIP_EXTRA_INDEX_URL = "https://pypi.org/simple/"
227212
PIP_PRE = "1"
228213

229-
[tool.hatch.envs.upstream.scripts]
230-
run = "pytest --verbose"
231-
run-mypy = "mypy src"
232-
run-hypothesis = "pytest --hypothesis-profile ci tests/test_properties.py tests/test_store/test_stateful*"
233-
run-coverage = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
234-
run-coverage-gpu = "pip install cupy-cuda12x && pytest -m gpu --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
235-
run-coverage-html = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report html --cov=src"
236-
list-env = "pip list"
237-
238214
[tool.hatch.envs.min_deps]
239215
description = """Test environment for minimum supported dependencies
240216
241217
See Spec 0000 for details and drop schedule: https://scientific-python.org/specs/spec-0000/
242218
"""
219+
template = "test"
243220
python = "3.11"
244221
dependencies = [
245222
'zarr[remote]',
@@ -257,13 +234,22 @@ dependencies = [
257234
'zarr[remote_tests]',
258235
]
259236

260-
[tool.hatch.envs.min_deps.scripts]
261-
run = "pytest --verbose"
262-
run-hypothesis = "pytest --hypothesis-profile ci tests/test_properties.py tests/test_store/test_stateful*"
237+
238+
[tool.hatch.envs.doctest]
239+
features = ["test", "optional", "remote", "remote_tests"]
240+
description = "Test environment for doctests"
241+
242+
[tool.hatch.envs.doctest.scripts]
243+
run = "rm -r data/; pytest docs/user-guide --doctest-glob='*.rst'"
244+
fix = "rm -r data/; pytest docs/user-guide --doctest-glob='*.rst' --accept"
263245
list-env = "pip list"
264-
run-coverage = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
265-
run-coverage-gpu = "pip install cupy-cuda12x && pytest -m gpu --cov-config=pyproject.toml --cov=pkg --cov-report xml --cov=src --junitxml=junit.xml -o junit_family=legacy"
266-
run-coverage-html = "pytest --cov-config=pyproject.toml --cov=pkg --cov-report html --cov=src"
246+
247+
[tool.hatch.envs.docs]
248+
features = ['docs']
249+
250+
[tool.hatch.envs.docs.scripts]
251+
build = "cd docs && make html"
252+
serve = "sphinx-autobuild docs docs/_build --host 0.0.0.0"
267253

268254
[tool.ruff]
269255
line-length = 100
@@ -403,16 +389,6 @@ addopts = [
403389
]
404390
filterwarnings = [
405391
"error",
406-
# TODO: explicitly filter or catch the warnings below where we expect them to be emitted in the tests
407-
"ignore:Consolidated metadata is currently not part in the Zarr format 3 specification.*:UserWarning",
408-
"ignore:Creating a zarr.buffer.gpu.Buffer with an array that does not support the __cuda_array_interface__.*:UserWarning",
409-
"ignore:Automatic shard shape inference is experimental and may change without notice.*:UserWarning",
410-
"ignore:The codec .* is currently not part in the Zarr format 3 specification.*:UserWarning",
411-
"ignore:The dtype .* is currently not part in the Zarr format 3 specification.*:UserWarning",
412-
"ignore:Use zarr.create_array instead.:DeprecationWarning",
413-
"ignore:Duplicate name.*:UserWarning",
414-
"ignore:The `compressor` argument is deprecated. Use `compressors` instead.:UserWarning",
415-
"ignore:Numcodecs codecs are not in the Zarr version 3 specification and may not be supported by other zarr implementations.:UserWarning",
416392
"ignore:Unclosed client session <aiohttp.client.ClientSession.*:ResourceWarning"
417393
]
418394
markers = [

src/zarr/_compat.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from inspect import Parameter, signature
55
from typing import Any, TypeVar
66

7+
from zarr.errors import ZarrFutureWarning
8+
79
T = TypeVar("T")
810

911
# Based off https://github.com/scikit-learn/scikit-learn/blob/e87b32a81c70abed8f2e97483758eb64df8255e9/sklearn/utils/validation.py#L63
@@ -54,7 +56,7 @@ def inner_f(*args: Any, **kwargs: Any) -> T:
5456
f"{version} passing these as positional arguments "
5557
"will result in an error"
5658
),
57-
FutureWarning,
59+
ZarrFutureWarning,
5860
stacklevel=2,
5961
)
6062
kwargs.update(zip(sig.parameters, args, strict=False))

src/zarr/abc/codec.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
from abc import abstractmethod
44
from collections.abc import Mapping
5-
from dataclasses import dataclass
65
from typing import (
76
TYPE_CHECKING,
87
ClassVar,
98
Generic,
109
Literal,
1110
Self,
1211
TypedDict,
12+
TypeGuard,
1313
TypeVar,
1414
overload,
1515
)
@@ -51,9 +51,15 @@
5151

5252

5353
class CodecJSON_V2(TypedDict, Generic[TName]):
54+
"""The JSON representation of a codec for Zarr V2"""
55+
5456
id: ReadOnly[TName]
5557

5658

59+
def _check_codecjson_v2(data: object) -> TypeGuard[CodecJSON_V2[str]]:
60+
return isinstance(data, Mapping) and "id" in data and isinstance(data["id"], str)
61+
62+
5763
CodecConfig_V3 = NamedConfig[str, Mapping[str, object]]
5864

5965
CodecJSON_V3 = str | CodecConfig_V3
@@ -505,10 +511,6 @@ async def wrap(chunk: CodecInput | None, chunk_spec: ArraySpec) -> CodecOutput |
505511
return wrap
506512

507513

508-
# Raised when a codec JSON data is invalid
509-
class CodecValidationError(ValueError): ...
510-
511-
512514
class Numcodec(Protocol):
513515
"""
514516
A protocol that models the ``numcodecs.abc.Codec`` interface.
@@ -526,5 +528,3 @@ def get_config(self) -> CodecJSON_V2[str]: ...
526528

527529
@classmethod
528530
def from_config(cls, config: CodecJSON_V2[str]) -> Self: ...
529-
530-

src/zarr/abc/numcodec.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, Self, TypeGuard
2+
3+
from typing_extensions import Protocol
4+
5+
6+
class Numcodec(Protocol):
7+
"""
8+
A protocol that models the ``numcodecs.abc.Codec`` interface.
9+
"""
10+
11+
codec_id: str
12+
13+
def encode(self, buf: Any) -> Any: ...
14+
15+
def decode(self, buf: Any, out: Any | None = None) -> Any: ...
16+
17+
def get_config(self) -> Any: ...
18+
19+
@classmethod
20+
def from_config(cls, config: Any) -> Self: ...
21+
22+
23+
def _is_numcodec_cls(obj: object) -> TypeGuard[type[Numcodec]]:
24+
"""
25+
Check if the given object is a class implements the Numcodec protocol.
26+
27+
The @runtime_checkable decorator does not allow issubclass checks for protocols with non-method
28+
members (i.e., attributes), so we use this function to manually check for the presence of the
29+
required attributes and methods on a given object.
30+
"""
31+
return (
32+
isinstance(obj, type)
33+
and hasattr(obj, "codec_id")
34+
and isinstance(obj.codec_id, str)
35+
and hasattr(obj, "encode")
36+
and callable(obj.encode)
37+
and hasattr(obj, "decode")
38+
and callable(obj.decode)
39+
and hasattr(obj, "get_config")
40+
and callable(obj.get_config)
41+
and hasattr(obj, "from_config")
42+
and callable(obj.from_config)
43+
)
44+
45+
46+
def _is_numcodec(obj: object) -> TypeGuard[Numcodec]:
47+
"""
48+
Check if the given object implements the Numcodec protocol.
49+
50+
The @runtime_checkable decorator does not allow issubclass checks for protocols with non-method
51+
members (i.e., attributes), so we use this function to manually check for the presence of the
52+
required attributes and methods on a given object.
53+
"""
54+
return _is_numcodec_cls(type(obj))

0 commit comments

Comments
 (0)