Skip to content

Commit 2cc8d48

Browse files
committed
Cleanup crc32c soft dependency
1 parent 29995e3 commit 2cc8d48

File tree

3 files changed

+53
-40
lines changed

3 files changed

+53
-40
lines changed

numcodecs/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,17 @@
117117

118118
register_codec(MsgPack)
119119

120-
from numcodecs.checksum32 import CRC32, CRC32C, Adler32, JenkinsLookup3
120+
from numcodecs.checksum32 import CRC32, Adler32, JenkinsLookup3
121121

122122
register_codec(CRC32)
123-
register_codec(CRC32C)
124123
register_codec(Adler32)
125124
register_codec(JenkinsLookup3)
126125

126+
with suppress(ImportError):
127+
from numcodecs.checksum32 import CRC32C
128+
129+
register_codec(CRC32C)
130+
127131
from numcodecs.json import JSON
128132

129133
register_codec(JSON)

numcodecs/checksum32.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import struct
22
import zlib
33
from collections.abc import Callable
4-
from typing import TYPE_CHECKING, Literal
4+
from contextlib import suppress
5+
from types import ModuleType
6+
from typing import TYPE_CHECKING, Literal, Optional
57

68
import numpy as np
79

810
from .abc import Codec
911
from .compat import ensure_contiguous_ndarray, ndarray_copy
1012
from .jenkins import jenkins_lookup3
1113

14+
_crc32c: Optional[ModuleType] = None
15+
with suppress(ImportError):
16+
import crc32c as _crc32c
17+
1218
if TYPE_CHECKING:
1319
from typing_extensions import Buffer
1420

@@ -76,28 +82,6 @@ class CRC32(Checksum32):
7682
location = 'start'
7783

7884

79-
class CRC32C(Checksum32):
80-
"""Codec add a crc32c checksum to the buffer.
81-
82-
Parameters
83-
----------
84-
location : 'start' or 'end'
85-
Where to place the checksum in the buffer.
86-
"""
87-
88-
codec_id = 'crc32c'
89-
90-
def checksum(self, buf):
91-
try:
92-
from crc32c import crc32c as crc32c_
93-
94-
return crc32c_(buf)
95-
except ImportError: # pragma: no cover
96-
raise ImportError("crc32c must be installed to use the CRC32C checksum codec.")
97-
98-
location = 'end'
99-
100-
10185
class Adler32(Checksum32):
10286
"""Codec add a adler32 checksum to the buffer.
10387
@@ -168,3 +152,19 @@ def decode(self, buf, out=None):
168152
out.view("uint8")[:] = b[:-4]
169153
return out
170154
return memoryview(b[:-4])
155+
156+
157+
if _crc32c:
158+
class CRC32C(Checksum32):
159+
"""Codec add a crc32c checksum to the buffer.
160+
161+
Parameters
162+
----------
163+
location : 'start' or 'end'
164+
Where to place the checksum in the buffer.
165+
"""
166+
167+
codec_id = 'crc32c'
168+
169+
checksum = _crc32c.crc32c
170+
location = 'end'

numcodecs/tests/test_checksum32.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import itertools
2+
from contextlib import suppress
23

34
import numpy as np
45
import pytest
56

6-
try:
7-
from numcodecs.checksum32 import CRC32, CRC32C, Adler32
8-
except ImportError: # pragma: no cover
9-
pytest.skip("numcodecs.checksum32 not available", allow_module_level=True)
10-
7+
from numcodecs.checksum32 import CRC32, Adler32
118
from numcodecs.tests.common import (
129
check_backwards_compatibility,
1310
check_config,
@@ -17,6 +14,11 @@
1714
check_repr,
1815
)
1916

17+
has_crc32c = False
18+
with suppress(ImportError):
19+
from numcodecs.checksum32 import CRC32C
20+
has_crc32c = True
21+
2022
# mix of dtypes: integer, float, bool, string
2123
# mix of shapes: 1D, 2D, 3D
2224
# mix of orders: C, F
@@ -39,11 +41,14 @@
3941
codecs = [
4042
CRC32(),
4143
CRC32(location="end"),
42-
CRC32C(location="start"),
43-
CRC32C(),
4444
Adler32(),
4545
Adler32(location="end"),
4646
]
47+
if has_crc32c:
48+
codecs.extend([
49+
CRC32C(location="start"),
50+
CRC32C(),
51+
])
4752

4853

4954
@pytest.mark.parametrize(("codec", "arr"), itertools.product(codecs, arrays))
@@ -86,27 +91,30 @@ def test_err_encode_list(codec):
8691

8792

8893
def test_err_location():
89-
with pytest.raises(ValueError):
90-
CRC32(location="foo")
91-
with pytest.raises(ValueError):
92-
CRC32C(location="foo")
9394
with pytest.raises(ValueError):
9495
Adler32(location="foo")
96+
if has_crc32c:
97+
with pytest.raises(ValueError):
98+
CRC32(location="foo")
99+
with pytest.raises(ValueError):
100+
CRC32C(location="foo")
95101

96102

97103
def test_repr():
98104
check_repr("CRC32(location='start')")
99-
check_repr("CRC32C(location='start')")
100-
check_repr("Adler32(location='start')")
101105
check_repr("CRC32(location='end')")
102-
check_repr("CRC32C(location='end')")
106+
check_repr("Adler32(location='start')")
103107
check_repr("Adler32(location='end')")
108+
if has_crc32c:
109+
check_repr("CRC32C(location='start')")
110+
check_repr("CRC32C(location='end')")
104111

105112

106113
def test_backwards_compatibility():
107114
check_backwards_compatibility(CRC32.codec_id, arrays, [CRC32()])
108115
check_backwards_compatibility(Adler32.codec_id, arrays, [Adler32()])
109-
check_backwards_compatibility(CRC32C.codec_id, arrays, [CRC32C()])
116+
if has_crc32c:
117+
check_backwards_compatibility(CRC32C.codec_id, arrays, [CRC32C()])
110118

111119

112120
@pytest.mark.parametrize("codec", codecs)
@@ -127,6 +135,7 @@ def test_err_out_too_small(codec):
127135
codec.decode(codec.encode(arr), out)
128136

129137

138+
@pytest.mark.skipif(not has_crc32c, reason="Needs `crc32c` installed")
130139
def test_crc32c_checksum():
131140
arr = np.arange(0, 64, dtype="uint8")
132141
buf = CRC32C(location="end").encode(arr)

0 commit comments

Comments
 (0)