numcodecs-combinators 0.2.8__tar.gz → 0.2.9__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.
Files changed (22) hide show
  1. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/PKG-INFO +1 -1
  2. numcodecs_combinators-0.2.9/_typos.toml +2 -0
  3. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/pyproject.toml +1 -1
  4. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/abc.py +2 -0
  5. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/stack.py +39 -6
  6. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/tests/test_stack.py +34 -0
  7. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/.github/workflows/ci.yml +0 -0
  8. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/.github/workflows/publish.yml +0 -0
  9. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/.gitignore +0 -0
  10. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/.python-version +0 -0
  11. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/.readthedocs.yaml +0 -0
  12. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/LICENSE +0 -0
  13. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/README.md +0 -0
  14. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/docs/index.md +0 -0
  15. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/docs/requirements.txt +0 -0
  16. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/mkdocs.yml +0 -0
  17. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/__init__.py +0 -0
  18. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/best.py +0 -0
  19. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/framed.py +0 -0
  20. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/src/numcodecs_combinators/py.typed +0 -0
  21. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/tests/test_best.py +0 -0
  22. {numcodecs_combinators-0.2.8 → numcodecs_combinators-0.2.9}/tests/test_framed.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numcodecs-combinators
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Combinator codecs for the `numcodecs` buffer compression API
5
5
  License: Copyright (c) 2024, Juniper Tyree
6
6
 
@@ -0,0 +1,2 @@
1
+ [default.extend-identifiers]
2
+ _MaybeChunkedNdArray = "_MaybeChunkedNdArray"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "numcodecs-combinators"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  description = "Combinator codecs for the `numcodecs` buffer compression API"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -15,6 +15,8 @@ class CodecCombinatorMixin(ABC):
15
15
  Mixin class for combinators over [`Codec`][numcodecs.abc.Codec]s.
16
16
  """
17
17
 
18
+ __slots__ = ()
19
+
18
20
  @abstractmethod
19
21
  def map(self, mapper: Callable[[Codec], Codec]) -> Codec:
20
22
  """
@@ -140,6 +140,8 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
140
140
  buffer protocol.
141
141
  """
142
142
 
143
+ chunked = getattr(buf, "chunked", False)
144
+
143
145
  encoded = np.asarray(
144
146
  numcodecs.compat.ensure_contiguous_ndarray_like(buf, flatten=False)
145
147
  )
@@ -149,16 +151,23 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
149
151
  silhouettes.append((encoded.shape, encoded.dtype))
150
152
  encoded = np.asarray(
151
153
  numcodecs.compat.ensure_contiguous_ndarray_like(
152
- codec.encode((encoded)), flatten=False
154
+ codec.encode(_MaybeChunkedNdArray(encoded) if chunked else encoded),
155
+ flatten=False,
153
156
  )
154
157
  )
155
158
 
156
- decoded = encoded
159
+ decoded = encoded.view(np.ndarray)
157
160
 
158
161
  for codec in reversed(self):
159
162
  shape, dtype = silhouettes.pop()
160
163
  out = np.empty(shape=shape, dtype=dtype)
161
- decoded = codec.decode(decoded, out).view(dtype).reshape(shape)
164
+ decoded = (
165
+ codec.decode(decoded, _MaybeChunkedNdArray(out) if chunked else out)
166
+ .view(dtype)
167
+ .reshape(shape)
168
+ )
169
+
170
+ decoded = decoded.view(np.ndarray)
162
171
 
163
172
  if isinstance(decoded, type(buf)):
164
173
  return decoded
@@ -167,7 +176,8 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
167
176
 
168
177
  def encode_decode_data_array(self, da: "xr.DataArray") -> "xr.DataArray":
169
178
  """
170
- Encode, then decode each chunk (independently) in the data array `da`.
179
+ Encode, then decode the data array `da`. If `da` is chunked, each chunk
180
+ is encoded and decoded *independently*.
171
181
 
172
182
  Since each chunk is encoded *independently*, this method may cause
173
183
  chunk boundary artifacts. Do *not* use this method if the codec
@@ -195,6 +205,8 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
195
205
 
196
206
  import xarray as xr
197
207
 
208
+ chunked = da.chunks is not None
209
+
198
210
  def encode_decode_data_array_single_chunk(
199
211
  da: xr.DataArray,
200
212
  ) -> xr.DataArray:
@@ -205,9 +217,11 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
205
217
  return da.copy(deep=False).chunk(single_chunk)
206
218
 
207
219
  # eagerly compute the input chunk and encode and decode it
208
- decoded = self.encode_decode(da.values) # type: ignore
220
+ decoded = self.encode_decode(_MaybeChunkedNdArray(da.values, chunked)) # type: ignore
209
221
 
210
- return da.copy(deep=False, data=decoded).chunk(single_chunk)
222
+ return da.copy(deep=False, data=np.array(decoded).view(np.ndarray)).chunk(
223
+ single_chunk
224
+ )
211
225
 
212
226
  return xr.map_blocks(encode_decode_data_array_single_chunk, da)
213
227
 
@@ -293,3 +307,22 @@ class CodecStack(Codec, CodecCombinatorMixin, tuple[Codec]):
293
307
 
294
308
 
295
309
  numcodecs.registry.register_codec(CodecStack)
310
+
311
+
312
+ class _MaybeChunkedNdArray(np.ndarray):
313
+ __slots__ = ("_chunked",)
314
+ _chunked: bool
315
+
316
+ def __new__(cls, array, chunked: bool = True):
317
+ obj = np.asarray(array).view(cls)
318
+ obj._chunked = chunked
319
+ return obj
320
+
321
+ def __array_finalize__(self, obj):
322
+ if obj is None:
323
+ return
324
+ self._chunked = getattr(obj, "chunked", True)
325
+
326
+ @property
327
+ def chunked(self) -> bool:
328
+ return self._chunked
@@ -1,6 +1,7 @@
1
1
  import numcodecs
2
2
  import numpy as np
3
3
  import xarray as xr
4
+ from numcodecs.abc import Codec
4
5
 
5
6
  import numcodecs_combinators
6
7
  from numcodecs_combinators.stack import CodecStack
@@ -51,6 +52,39 @@ def test_encode_decode():
51
52
  assert encoded_decoded.equals(xr.DataArray([1.0, 2.0, 3.0]))
52
53
 
53
54
 
55
+ def test_chunked_encode_decode():
56
+ class CheckChunkedCodec(Codec):
57
+ __slots__ = ("is_chunked",)
58
+ is_chunked: bool
59
+
60
+ def __init__(self, is_chunked: bool):
61
+ self.is_chunked = is_chunked
62
+
63
+ def encode(self, buf):
64
+ assert getattr(buf, "chunked", False) == self.is_chunked
65
+ return buf
66
+
67
+ def decode(self, buf, out=None):
68
+ assert getattr(buf, "chunked", False) is False
69
+ assert getattr(out, "chunked", False) == self.is_chunked
70
+ return numcodecs.compat.ndarray_copy(buf, out)
71
+
72
+ stack = CodecStack(CheckChunkedCodec(False))
73
+
74
+ encoded_decoded = stack.encode_decode(np.array([1.0, 2.0, 3.0]))
75
+ assert np.all(encoded_decoded == np.array([1.0, 2.0, 3.0]))
76
+
77
+ encoded_decoded = stack.encode_decode_data_array(xr.DataArray([1.0, 2.0, 3.0]))
78
+ assert encoded_decoded.equals(xr.DataArray([1.0, 2.0, 3.0]))
79
+
80
+ stack = CodecStack(CheckChunkedCodec(True))
81
+
82
+ encoded_decoded = stack.encode_decode_data_array(
83
+ xr.DataArray([1.0, 2.0, 3.0]).chunk(1)
84
+ )
85
+ assert encoded_decoded.equals(xr.DataArray([1.0, 2.0, 3.0]))
86
+
87
+
54
88
  def test_map():
55
89
  stack = CodecStack(numcodecs.Zlib(level=9), numcodecs.CRC32())
56
90