Skip to content

Commit 82fe1e5

Browse files
d-v-bmaxrjones
andauthored
fix/revert 792 (#802)
* Update pyproject.toml * update pyproject.toml * abstract over crc32c modules * Fix dependency groups/optional-dependencies * Add deprecation warning * Put back test and docs optional dependencies * Keep test-extras * Update deps in tests * Add env for local testing * Only emit warning once * Add test * Lint * Don't install pcodec with python 3.14 * Ignore slow google_crc32c warning --------- Co-authored-by: Max Jones <[email protected]>
1 parent 91dfe42 commit 82fe1e5

File tree

6 files changed

+117
-32
lines changed

6 files changed

+117
-32
lines changed

.github/workflows/ci-i386.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ jobs:
6363
run: |
6464
export DISABLE_NUMCODECS_AVX2=""
6565
uv venv
66-
uv pip install -v -e .[test,test_extras,msgpack,crc32c,pcodec,zfpy]
66+
# TODO: Remove this conditional when pcodec supports Python 3.14
67+
if [[ "${{ matrix.python-version }}" == "3.14" ]]; then
68+
uv pip install -v -e .[test,test_extras,msgpack,google_crc32c,crc32c,zfpy]
69+
else
70+
uv pip install -v -e .[test,test_extras,msgpack,google_crc32c,crc32c,pcodec,zfpy]
71+
fi
6772
shell: alpine.sh {0}
6873

6974
- name: List installed packages

.github/workflows/ci.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ jobs:
5050
- name: Install numcodecs
5151
run: |
5252
export DISABLE_NUMCODECS_AVX2=""
53-
python -m pip install -v -e .[test,test_extras,msgpack,crc32c,pcodec,zfpy]
53+
# TODO: Remove this conditional when pcodec supports Python 3.14
54+
if [[ "${{ matrix.python-version }}" == "3.14" ]]; then
55+
python -m pip install -v -e .[test,test_extras,msgpack,google_crc32c,crc32c,zfpy]
56+
else
57+
python -m pip install -v -e .[test,test_extras,msgpack,google_crc32c,crc32c,pcodec,zfpy]
58+
fi
5459
5560
- name: List installed packages
5661
run: python -m pip list

docs/contributing.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ like the following::
101101
$ source ~/pyenv/numcodecs-dev/bin/activate
102102
$ pip install -e .[docs,test,msgpack,zfpy]
103103

104+
You may need to initialize the submodule for c-blosc:
105+
106+
$ git submodule update --init --recursive
107+
104108
To verify that your development environment is working, you can run the unit tests::
105109

106110
$ pytest -v

numcodecs/checksum32.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import abc
2+
import importlib.util
23
import struct
4+
import warnings
35
import zlib
4-
from contextlib import suppress
5-
from types import ModuleType
6+
from collections.abc import Callable
67
from typing import Literal
78

89
import numpy as np
@@ -12,9 +13,32 @@
1213
from .compat import ensure_contiguous_ndarray, ndarray_copy
1314
from .jenkins import jenkins_lookup3
1415

15-
_crc32c: ModuleType | None = None
16-
with suppress(ImportError):
17-
import google_crc32c as _crc32c # type: ignore[no-redef, unused-ignore]
16+
crc32c_checksum: Callable[[Buffer, int], int] | None
17+
18+
warnings.filterwarnings('once', message='crc32c usage is deprecated.*', category=DeprecationWarning)
19+
20+
if importlib.util.find_spec("google_crc32c") is not None:
21+
import google_crc32c
22+
23+
def crc32c_checksum(data: Buffer, value: int = 0) -> int:
24+
if value == 0:
25+
return google_crc32c.value(data)
26+
else:
27+
return google_crc32c.extend(value, data)
28+
29+
elif importlib.util.find_spec("crc32c") is not None:
30+
warnings.warn(
31+
"crc32c usage is deprecated since numcodecs v0.16.4. "
32+
"It is recommended to install google_crc32c instead.",
33+
DeprecationWarning,
34+
stacklevel=2,
35+
)
36+
import crc32c
37+
38+
def crc32c_checksum(data: Buffer, value: int = 0) -> int:
39+
return crc32c.crc32c(data, value)
40+
else:
41+
crc32c_checksum = None
1842

1943
CHECKSUM_LOCATION = Literal['start', 'end']
2044

@@ -167,7 +191,7 @@ def decode(self, buf, out=None):
167191
return memoryview(b[:-4])
168192

169193

170-
if _crc32c:
194+
if crc32c_checksum is not None:
171195

172196
class CRC32C(Checksum32):
173197
"""Codec add a crc32c checksum to the buffer.
@@ -183,7 +207,4 @@ class CRC32C(Checksum32):
183207

184208
@staticmethod
185209
def checksum(data: Buffer, value: int = 0) -> int:
186-
if value == 0:
187-
return _crc32c.value(data) # type: ignore[union-attr]
188-
else:
189-
return _crc32c.extend(value, data) # type: ignore[union-attr]
210+
return crc32c_checksum(data, value) # type: ignore[misc]

numcodecs/tests/test_checksum32.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib
12
import itertools
23
from contextlib import suppress
34

@@ -15,6 +16,9 @@
1516
)
1617

1718
has_crc32c = False
19+
has_google_crc32c = importlib.util.find_spec("google_crc32c") is not None
20+
has_legacy_crc32c = importlib.util.find_spec("crc32c") is not None
21+
1822
with suppress(ImportError):
1923
from numcodecs.checksum32 import CRC32C
2024

@@ -104,7 +108,7 @@ def test_err_location():
104108
with pytest.raises(ValueError):
105109
Adler32(location="foo")
106110
if not has_crc32c:
107-
pytest.skip("Needs `crc32c` installed")
111+
pytest.skip("Needs `crc32c` or `google_crc32c` installed")
108112
with pytest.raises(ValueError):
109113
CRC32C(location="foo")
110114

@@ -118,11 +122,15 @@ def test_err_location():
118122
"Adler32(location='end')",
119123
pytest.param(
120124
"CRC32C(location='start')",
121-
marks=pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed"),
125+
marks=pytest.mark.skipif(
126+
not has_crc32c, reason="Needs `crc32c` or `google_crc32c` installed"
127+
),
122128
),
123129
pytest.param(
124130
"CRC32C(location='end')",
125-
marks=pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed"),
131+
marks=pytest.mark.skipif(
132+
not has_crc32c, reason="Needs `crc32c` or `google_crc32c` installed"
133+
),
126134
),
127135
],
128136
)
@@ -141,7 +149,7 @@ def test_backwards_compatibility(codec_id, codec_instance):
141149
check_backwards_compatibility(codec_id, arrays, [codec_instance])
142150

143151

144-
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed")
152+
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` or `google_crc32c` installed")
145153
def test_backwards_compatibility_crc32c():
146154
check_backwards_compatibility(CRC32C.codec_id, arrays, [CRC32C()])
147155

@@ -164,14 +172,14 @@ def test_err_out_too_small(codec):
164172
codec.decode(codec.encode(arr), out)
165173

166174

167-
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed")
175+
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` or `google_crc32c` installed")
168176
def test_crc32c_checksum():
169177
arr = np.arange(0, 64, dtype="uint8")
170178
buf = CRC32C(location="end").encode(arr)
171179
assert np.frombuffer(buf, dtype="<u4", offset=(len(buf) - 4))[0] == np.uint32(4218238699)
172180

173181

174-
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed")
182+
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` or `google_crc32c` installed")
175183
def test_crc32c_incremental():
176184
"""Test that CRC32C.checksum supports incremental calculation via value parameter."""
177185
# Test incremental checksum calculation (for API compatibility)
@@ -197,3 +205,23 @@ def test_err_checksum(codec):
197205
buf[-1] = 0 # corrupt the checksum
198206
with pytest.raises(RuntimeError):
199207
codec.decode(buf)
208+
209+
210+
@pytest.mark.skipif(
211+
has_google_crc32c or not has_legacy_crc32c,
212+
reason="Only runs when legacy `crc32c` is used (not `google_crc32c`)",
213+
)
214+
def test_crc32c_deprecation_warning():
215+
"""Test that using legacy crc32c (not google_crc32c) issues a deprecation warning."""
216+
import sys
217+
218+
# Remove the module from cache to force re-import and trigger the warning
219+
if 'numcodecs.checksum32' in sys.modules:
220+
del sys.modules['numcodecs.checksum32']
221+
222+
with pytest.warns(
223+
DeprecationWarning, match="crc32c usage is deprecated since numcodecs v0.16.4"
224+
):
225+
import numcodecs.checksum32
226+
227+
importlib.reload(numcodecs.checksum32)

pyproject.toml

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ Documentation = "https://numcodecs.readthedocs.io/"
4747
Homepage = "https://github.com/zarr-developers/numcodecs"
4848

4949
[project.optional-dependencies]
50+
msgpack = [
51+
"msgpack",
52+
]
53+
zfpy = [
54+
"zfpy>=1.0.0"
55+
]
56+
pcodec = [
57+
"pcodec>=0.3,<0.4",
58+
]
59+
crc32c = [
60+
"crc32c>=2.7",
61+
]
62+
google_crc32c = [
63+
"google-crc32c>=1.5"
64+
]
5065
docs = [
5166
"sphinx",
5267
"sphinx-issues",
@@ -84,19 +99,6 @@ test-zarr-main = [
8499
"crc32c",
85100
]
86101

87-
msgpack = [
88-
"msgpack",
89-
]
90-
zfpy = [
91-
"zfpy>=1.0.0"
92-
]
93-
pcodec = [
94-
"pcodec>=0.3,<0.4",
95-
]
96-
crc32c = [
97-
"google-crc32c>=1.5",
98-
]
99-
100102
[tool.setuptools]
101103
package-dir = {"" = "."}
102104
packages = ["numcodecs", "numcodecs.tests"]
@@ -152,6 +154,7 @@ log_cli_level = "INFO"
152154
xfail_strict = true
153155
filterwarnings = [
154156
"error",
157+
"ignore:As the c extension couldn't be imported:RuntimeWarning", # Ignore warning about pure python google_crc32c (on Python 3.14)
155158
]
156159

157160
[tool.cibuildwheel]
@@ -251,20 +254,39 @@ conflicts = [
251254
]
252255
]
253256

254-
[tool.pixi.project]
257+
[tool.pixi.workspace]
255258
channels = ["conda-forge"]
256259
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
257260

258261
[tool.pixi.dependencies]
262+
python = "=3.14"
259263
clang = ">=19.1.7,<20"
260264
c-compiler = ">=1.9.0,<2"
261265
cxx-compiler = ">=1.9.0,<2"
262266
uv = "*"
263267

268+
[tool.pixi.feature.test.pypi-dependencies]
269+
numcodecs = { path = ".", editable = true, extras = ["test","test_extras","msgpack","zfpy"] }
270+
271+
[tool.pixi.feature.test-google-crc32c.pypi-dependencies]
272+
numcodecs = { path = ".", editable = true, extras = ["google_crc32c"] }
273+
274+
[tool.pixi.feature.test-crc32c.pypi-dependencies]
275+
numcodecs = { path = ".", editable = true, extras = ["crc32c"] }
276+
277+
[tool.pixi.environments]
278+
default = { solve-group = "default" }
279+
test = ["test"]
280+
test-crc32c = ["test", "test-crc32c"]
281+
test-google-crc32c = ["test", "test-google-crc32c"]
282+
264283
[tool.pixi.tasks]
265284
ls-deps-312 = "uv run --group test-zarr-312 uv pip freeze"
266285
ls-deps-313 = "uv run --group test-zarr-313 uv pip freeze"
267286
ls-deps-main = "uv run --group test-zarr-main uv pip freeze"
268287
test-zarr-312 = "uv run --group test-zarr-312 pytest numcodecs/tests/test_zarr3.py numcodecs/tests/test_zarr3_import.py"
269288
test-zarr-313 = "uv run --group test-zarr-313 pytest numcodecs/tests/test_zarr3.py numcodecs/tests/test_zarr3_import.py"
270289
test-zarr-main = "uv run --group test-zarr-main pytest numcodecs/tests/test_zarr3.py numcodecs/tests/test_zarr3_import.py"
290+
291+
[tool.pixi.feature.test.tasks]
292+
run-tests = "pytest -v"

0 commit comments

Comments
 (0)