diff --git a/src/blosc2/core.py b/src/blosc2/core.py index 093a8264..2f5ab25b 100644 --- a/src/blosc2/core.py +++ b/src/blosc2/core.py @@ -1447,7 +1447,7 @@ def decompress2(src: object, dst: object | bytearray = None, **kwargs: dict) -> Decompression parameters. The default values are in :class:`blosc2.DParams`. Keyword arguments supported: - cparams: :class:`blosc2.DParams` + dparams: :class:`blosc2.DParams` All the decompression parameters that you want to use as a :class:`blosc2.DParams` instance. others: Any diff --git a/src/blosc2/ndarray.py b/src/blosc2/ndarray.py index 4964a3f4..52bfdab5 100644 --- a/src/blosc2/ndarray.py +++ b/src/blosc2/ndarray.py @@ -1289,13 +1289,15 @@ def copy(self, dtype: np.dtype = None, **kwargs: dict) -> NDArray: """ if dtype is None: dtype = self.dtype - kwargs["cparams"] = kwargs.get("cparams", asdict(self.schunk.cparams)).copy() - kwargs["dparams"] = kwargs.get("dparams", asdict(self.schunk.dparams)).copy() + kwargs["cparams"] = kwargs.get("cparams").copy() if isinstance(kwargs.get("cparams"), dict) \ + else asdict(self.schunk.cparams) + kwargs["dparams"] = kwargs.get("dparams").copy() if isinstance(kwargs.get("dparams"), dict) \ + else asdict(self.schunk.dparams) if "meta" not in kwargs: # Copy metalayers as well meta_dict = {meta: self.schunk.meta[meta] for meta in self.schunk.meta} kwargs["meta"] = meta_dict - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) return super().copy(dtype, **kwargs) @@ -1370,7 +1372,7 @@ def slice(self, key: int | slice | Sequence[slice], **kwargs: dict) -> NDArray: >>> print(type(c)) """ - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) key, mask = process_key(key, self.shape) start, stop, step = get_ndarray_start_stop(self.ndim, key, self.shape) key = (start, stop) @@ -2329,7 +2331,7 @@ def empty(shape: int | tuple | list, dtype: np.dtype = np.uint8, **kwargs: dict) dtype('int32') """ shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2362,7 +2364,7 @@ def uninit(shape: int | tuple | list, dtype: np.dtype = np.uint8, **kwargs: dict dtype('float64') """ shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2395,7 +2397,7 @@ def nans(shape: int | tuple | list, dtype: np.dtype = np.float64, **kwargs: dict dtype('float64') """ shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2434,7 +2436,7 @@ def zeros(shape: int | tuple | list, dtype: np.dtype = np.uint8, **kwargs: dict) dtype('float64') """ shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2487,7 +2489,7 @@ def full(shape: int | tuple | list, fill_value: bytes | int | float | bool, dtyp if dtype is None: dtype = np.dtype(type(fill_value)) shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2534,7 +2536,7 @@ def frombuffer( >>> a = blosc2.frombuffer(buffer, shape, chunks=chunks, dtype=dtype) """ shape = _check_shape(shape) - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) chunks, blocks = compute_chunks_blocks(shape, chunks, blocks, dtype, **kwargs) @@ -2597,7 +2599,7 @@ def asarray(array: np.ndarray | blosc2.C2Array, **kwargs: dict | list) -> NDArra >>> # Create a NDArray from a NumPy array >>> nda = blosc2.asarray(a) """ - _check_ndarray_kwargs(**kwargs) + kwargs = _check_ndarray_kwargs(**kwargs) chunks = kwargs.pop("chunks", None) blocks = kwargs.pop("blocks", None) # Use the chunks and blocks from the array if they are not passed @@ -2648,6 +2650,20 @@ def asarray(array: np.ndarray | blosc2.C2Array, **kwargs: dict | list) -> NDArra def _check_ndarray_kwargs(**kwargs): + if "storage" in kwargs: + for key in kwargs: + if key in list(blosc2.Storage.__annotations__): + raise AttributeError("Cannot pass both `storage` and other kwargs already included in Storage") + storage = kwargs.get("storage") + del kwargs["storage"] + kwargs = {**kwargs, **asdict(storage)} + else: + cparams = kwargs.get("cparams", {}) + cparams = cparams if isinstance(cparams, dict) else asdict(cparams) + dparams = kwargs.get("dparams", {}) + dparams = dparams if isinstance(dparams, dict) else asdict(dparams) + kwargs["cparams"] = cparams + kwargs["dparams"] = dparams supported_keys = [ "chunks", "blocks", @@ -2659,17 +2675,21 @@ def _check_ndarray_kwargs(**kwargs): "mode", "mmap_mode", "initial_mapping_size", + "storage", ] for key in kwargs: if key not in supported_keys: raise KeyError( f"Only {supported_keys} are supported as keyword arguments, and you passed '{key}'" ) + if "cparams" in kwargs and "chunks" in kwargs["cparams"]: raise ValueError("You cannot pass chunks in cparams, use `chunks` argument instead") if "cparams" in kwargs and "blocks" in kwargs["cparams"]: raise ValueError("You cannot pass chunks in cparams, use `blocks` argument instead") + return kwargs + def get_slice_nchunks(schunk: blosc2.SChunk, key: tuple[(int, int)] | int | slice | Sequence[slice] diff --git a/src/blosc2/storage.py b/src/blosc2/storage.py index ccb001fe..59292032 100644 --- a/src/blosc2/storage.py +++ b/src/blosc2/storage.py @@ -44,7 +44,7 @@ class CParams: Parameters ---------- - codec: :class:`Codec` + codec: :class:`Codec` or int The compressor code. Default is :py:obj:`Codec.ZSTD `. codec_meta: int The metadata for the compressor code, 0 by default. @@ -65,7 +65,7 @@ class CParams: splitmode: :class:`SplitMode` The split mode for the blocks. The default value is :py:obj:`SplitMode.ALWAYS_SPLIT `. - filters: :class:`Filter` list + filters: :class:`Filter` or int list The sequence of filters. Default: [:py:obj:`Filter.NOFILTER `, :py:obj:`Filter.NOFILTER `, :py:obj:`Filter.NOFILTER `, :py:obj:`Filter.NOFILTER `, :py:obj:`Filter.NOFILTER `, :py:obj:`Filter.SHUFFLE `]. @@ -74,7 +74,7 @@ class CParams: tuner: :class:`Tuner` The tuner to use. Default: :py:obj:`Tuner.STUNE `. """ - codec: blosc2.Codec = blosc2.Codec.ZSTD + codec: blosc2.Codec | int = blosc2.Codec.ZSTD codec_meta: int = 0 clevel: int = 1 use_dict: bool = False @@ -82,7 +82,7 @@ class CParams: nthreads: int = field(default_factory=default_nthreads) blocksize: int = 0 splitmode: blosc2.SplitMode = blosc2.SplitMode.ALWAYS_SPLIT - filters: list[blosc2.Filter] = field(default_factory=default_filters) + filters: list[blosc2.Filter | int] = field(default_factory=default_filters) filters_meta: list[int] = field(default_factory=default_filters_meta) tuner: blosc2.Tuner = blosc2.Tuner.STUNE @@ -95,6 +95,10 @@ def __post_init__(self): if len(self.filters) > len(self.filters_meta): raise ValueError("Number of filters cannot exceed number of filters meta") + for i in range(len(self.filters)): + if self.filters_meta[i] == 0 and self.filters[i] == blosc2.Filter.BYTEDELTA: + self.filters_meta[i] = self.typesize + @dataclass class DParams: diff --git a/tests/ndarray/test_c2array_udf.py b/tests/ndarray/test_c2array_udf.py index d9c7357f..9e562e97 100644 --- a/tests/ndarray/test_c2array_udf.py +++ b/tests/ndarray/test_c2array_udf.py @@ -95,9 +95,7 @@ def test_getitem(chunks, blocks, slices, urlpath, contiguous, chunked_eval, c2su chunked_eval=chunked_eval, chunks=chunks, blocks=blocks, - urlpath=urlpath, - contiguous=contiguous, - dparams=dparams, + storage=blosc2.Storage(urlpath=urlpath, contiguous=contiguous, dparams=dparams), ) lazy_eval = expr[slices] np.testing.assert_allclose(lazy_eval, npc[slices]) @@ -107,6 +105,6 @@ def test_getitem(chunks, blocks, slices, urlpath, contiguous, chunked_eval, c2su assert res.schunk.urlpath is None assert res.schunk.contiguous == contiguous # Check dparams after a getitem and an eval - assert res.schunk.dparams["nthreads"] == dparams["nthreads"] + assert res.schunk.dparams.nthreads == dparams["nthreads"] blosc2.remove_urlpath(urlpath) diff --git a/tests/ndarray/test_copy.py b/tests/ndarray/test_copy.py index a5c6681d..5bf773de 100644 --- a/tests/ndarray/test_copy.py +++ b/tests/ndarray/test_copy.py @@ -27,7 +27,7 @@ def test_copy(shape, chunks1, blocks1, chunks2, blocks2, dtype): typesize = dtype.itemsize size = int(np.prod(shape)) buffer = bytes(size * typesize) - cparams1 = {"clevel": 2} + cparams1 = blosc2.CParams(clevel=2) a = blosc2.frombuffer(buffer, shape, dtype=dtype, chunks=chunks1, blocks=blocks1, cparams=cparams1) cparams2 = {"clevel": 5, "filters": [blosc2.Filter.BITSHUFFLE], "filters_meta": [0]} b = a.copy(chunks=chunks2, blocks=blocks2, cparams=cparams2) @@ -63,7 +63,7 @@ def test_copy_numpy(shape, chunks1, blocks1, chunks2, blocks2, dtype): else: nparray = np.arange(size, dtype=dtype).reshape(shape) a = blosc2.asarray(nparray, chunks=chunks1, blocks=blocks1) - cparams = {"clevel": 5, "filters": [blosc2.Filter.BITSHUFFLE], "filters_meta": [0]} + cparams = blosc2.CParams(clevel=5, filters=[blosc2.Filter.BITSHUFFLE], filters_meta=[0]) b = a.copy(chunks=chunks2, blocks=blocks2, cparams=cparams) assert b.dtype == nparray.dtype if dtype.kind == "V": diff --git a/tests/ndarray/test_empty.py b/tests/ndarray/test_empty.py index bbca8568..aada0083 100644 --- a/tests/ndarray/test_empty.py +++ b/tests/ndarray/test_empty.py @@ -65,16 +65,17 @@ def test_empty(shape, chunks, blocks, dtype, cparams, urlpath, contiguous): blosc2.remove_urlpath(urlpath) filters = cparams["filters"] - cparams["filters_meta"] = [0] * len(filters) + storage = blosc2.Storage(cparams=blosc2.CParams(**cparams), + dparams={"nthreads": 2}, + urlpath=urlpath, + contiguous=contiguous, + ) a = blosc2.empty( shape, chunks=chunks, blocks=blocks, dtype=dtype, - cparams=cparams, - dparams={"nthreads": 2}, - urlpath=urlpath, - contiguous=contiguous, + storage=storage, ) dtype = np.dtype(dtype) diff --git a/tests/ndarray/test_full.py b/tests/ndarray/test_full.py index adf326fe..a33bb8bc 100644 --- a/tests/ndarray/test_full.py +++ b/tests/ndarray/test_full.py @@ -33,7 +33,7 @@ (10, 10), b"sun", None, - {"codec": blosc2.Codec.LZ4HC, "clevel": 8, "use_dict": False, "nthreads": 2}, + blosc2.CParams(codec=blosc2.Codec.LZ4HC, clevel=8, use_dict=False, nthreads=2), {"nthreads": 2}, "full.b2nd", True, @@ -55,7 +55,7 @@ (11, 11), 123456789, None, - {"codec": blosc2.Codec.LZ4HC, "clevel": 8, "use_dict": False, "nthreads": 2}, + blosc2.CParams(codec=blosc2.Codec.LZ4HC, clevel=8, use_dict=False, nthreads=2), {"nthreads": 2}, None, True, @@ -71,7 +71,7 @@ def test_full(shape, chunks, blocks, fill_value, cparams, dparams, dtype, urlpat blocks=blocks, dtype=dtype, cparams=cparams, - dparams=dparams, + dparams=blosc2.DParams(**dparams), urlpath=urlpath, contiguous=contiguous, ) diff --git a/tests/ndarray/test_lazyexpr.py b/tests/ndarray/test_lazyexpr.py index 3659600e..0c0edbb4 100644 --- a/tests/ndarray/test_lazyexpr.py +++ b/tests/ndarray/test_lazyexpr.py @@ -448,14 +448,14 @@ def test_params(array_fixture): urlpath = "eval_expr.b2nd" blosc2.remove_urlpath(urlpath) - cparams = {"nthreads": 2} + cparams = blosc2.CParams(nthreads=2) dparams = {"nthreads": 4} chunks = tuple(i // 2 for i in nres.shape) blocks = tuple(i // 4 for i in nres.shape) res = expr.eval(urlpath=urlpath, cparams=cparams, dparams=dparams, chunks=chunks, blocks=blocks) np.testing.assert_allclose(res[:], nres) assert res.schunk.urlpath == urlpath - assert res.schunk.cparams.nthreads == cparams["nthreads"] + assert res.schunk.cparams.nthreads == cparams.nthreads assert res.schunk.dparams.nthreads == dparams["nthreads"] assert res.chunks == chunks assert res.blocks == blocks @@ -493,8 +493,8 @@ def test_save(): chunks = tuple(i // 2 for i in nres.shape) blocks = tuple(i // 4 for i in nres.shape) urlpath_eval = "eval_expr.b2nd" - res = expr.eval( - urlpath=urlpath_eval, cparams=cparams, dparams=dparams, mode="w", chunks=chunks, blocks=blocks + res = expr.eval(storage=blosc2.Storage(urlpath=urlpath_eval, cparams=cparams, dparams=dparams, mode="w"), + chunks=chunks, blocks=blocks ) np.testing.assert_allclose(res[:], nres, rtol=tol, atol=tol) diff --git a/tests/test_storage.py b/tests/test_storage.py new file mode 100644 index 00000000..519af538 --- /dev/null +++ b/tests/test_storage.py @@ -0,0 +1,153 @@ +####################################################################### +# Copyright (c) 2019-present, Blosc Development Team +# All rights reserved. +# +# This source code is licensed under a BSD-style license (found in the +# LICENSE file in the root directory of this source tree) +####################################################################### + +from dataclasses import asdict, fields + +import pytest + +import blosc2 + + +@pytest.mark.parametrize( + "urlpath, contiguous, mode, mmap_mode, cparams, dparams", + [ + (None, None, "w", None, blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4), blosc2.DParams()), + (None, False, "a", None, {"typesize": 4}, blosc2.DParams()), + (None, None, "r", None, blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4), blosc2.DParams(nthreads=4)), + (None, True, "a", None, blosc2.CParams(splitmode=blosc2.SplitMode.ALWAYS_SPLIT, nthreads=5, typesize=4), {}), + ("b2frame", None, "r", None, {"codec": blosc2.Codec.LZ4HC, "typesize": 4}, blosc2.DParams()), + ("b2frame", False, "a", None, blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4), blosc2.DParams(nthreads=4)), + ("b2frame", True, "w", None, blosc2.CParams(splitmode=blosc2.SplitMode.ALWAYS_SPLIT, nthreads=5, typesize=4), {}), + ("b2frame", True, "r", "r", blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4), blosc2.DParams()), + ("b2frame", None, "w", "w+", {"typesize": 4}, {}), + ], +) +def test_storage_values(contiguous, urlpath, mode, mmap_mode, cparams, dparams): + storage = blosc2.Storage(contiguous=contiguous, urlpath=urlpath, mode=mode, mmap_mode=mmap_mode, + cparams=cparams, dparams=dparams) + if contiguous is None: + if urlpath is not None: + assert storage.contiguous + else: + assert not storage.contiguous + else: + assert storage.contiguous == contiguous + + assert storage.urlpath == urlpath + assert storage.mode == mode + assert storage.mmap_mode == mmap_mode + assert storage.cparams == cparams + assert storage.dparams == dparams + + +def test_storage_defaults(): + storage = blosc2.Storage() + assert storage.contiguous == False + assert storage.urlpath is None + assert storage.mode == "a" + assert storage.mmap_mode is None + assert storage.initial_mapping_size is None + assert storage.cparams == blosc2.CParams() + assert storage.dparams == blosc2.DParams() + assert storage.meta is None + + +@pytest.mark.parametrize( + "urlpath, contiguous, cparams, dparams", + [ + (None, False, blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4), blosc2.DParams()), + (None, True, {"typesize": 4}, blosc2.DParams(nthreads=4)), + ("b2frame", False, blosc2.CParams(splitmode=blosc2.SplitMode.ALWAYS_SPLIT, nthreads=5, typesize=4), {}), + ("b2frame", True, {"codec": blosc2.Codec.LZ4HC, "typesize": 4}, {}), + ], +) +def test_raises_storage(contiguous, urlpath, cparams, dparams): + storage = blosc2.Storage(contiguous=contiguous, urlpath=urlpath, + cparams=cparams, dparams=dparams) + blosc2.remove_urlpath(urlpath) + + for field in fields(blosc2.Storage): + with pytest.raises(AttributeError): + _ = blosc2.SChunk(storage=storage, **{str(field.name): {}}) + with pytest.raises(TypeError): + _ = blosc2.SChunk(**{str(field.name): {}}, **asdict(storage)) + + +@pytest.mark.parametrize( + "cparams", + [ + blosc2.CParams(codec=blosc2.Codec.LZ4, filters=[blosc2.Filter.BITSHUFFLE], tuner=blosc2.Tuner.BTUNE), + {"typesize": 4, 'filters': [blosc2.Filter.TRUNC_PREC, blosc2.Filter.DELTA], 'filters_meta': [0, 0]}, + blosc2.CParams(nthreads=5, filters=[blosc2.Filter.BITSHUFFLE, blosc2.Filter.BYTEDELTA], filters_meta=[0] * 3), + {"codec": blosc2.Codec.LZ4HC, "typesize": 4, 'filters': [blosc2.Filter.BYTEDELTA], 'tuner': blosc2.Tuner.BTUNE}, + ], +) +def test_cparams_values(cparams): + schunk = blosc2.SChunk(cparams=cparams) + cparams_dataclass = cparams if isinstance(cparams, blosc2.CParams) else blosc2.CParams(**cparams) + + for field in fields(cparams_dataclass): + if field.name in ['filters', 'filters_meta']: + assert getattr(schunk.cparams, field.name)[:len(getattr(cparams_dataclass, field.name))] == getattr(cparams_dataclass, field.name) + else: + assert getattr(schunk.cparams, field.name) == getattr(cparams_dataclass, field.name) + + +def test_cparams_defaults(): + cparams = blosc2.CParams() + assert cparams.codec == blosc2.Codec.ZSTD + assert cparams.codec_meta == 0 + assert cparams.splitmode == blosc2.SplitMode.ALWAYS_SPLIT + assert cparams.clevel == 1 + assert cparams.typesize == 8 + assert cparams.nthreads == blosc2.nthreads + assert cparams.filters == [blosc2.Filter.NOFILTER] * 5 + [blosc2.Filter.SHUFFLE] + assert cparams.filters_meta == [0] * 6 + assert not cparams.use_dict + assert cparams.blocksize == 0 + assert cparams.tuner == blosc2.Tuner.STUNE + + +def test_raises_cparams(): + cparams = blosc2.CParams(codec=blosc2.Codec.LZ4, clevel=6, typesize=4) + for field in fields(blosc2.CParams): + with pytest.raises(ValueError): + _ = blosc2.SChunk(cparams=cparams, **{str(field.name): {}}) + with pytest.raises(AttributeError): + _ = blosc2.compress2(b"12345678" * 1000, cparams=cparams, **{str(field.name): {}}) + + +@pytest.mark.parametrize( + "dparams", + [ + (blosc2.DParams()), + (blosc2.DParams(nthreads=2)), + ({}), + ({'nthreads': 2}), + ], +) +def test_dparams_values(dparams): + schunk = blosc2.SChunk(dparams=dparams) + dparams_dataclass = dparams if isinstance(dparams, blosc2.DParams) else blosc2.DParams(**dparams) + + for field in fields(dparams_dataclass): + assert getattr(schunk.dparams, field.name) == getattr(dparams_dataclass, field.name) + + +def test_dparams_defaults(): + dparams = blosc2.DParams() + assert dparams.nthreads == blosc2.nthreads + + +def test_raises_dparams(): + dparams = blosc2.DParams() + for field in fields(blosc2.DParams): + with pytest.raises(ValueError): + _ = blosc2.SChunk(dparams=dparams, **{str(field.name): {}}) + with pytest.raises(AttributeError): + _ = blosc2.decompress2(b"12345678" * 1000, dparams=dparams, **{str(field.name): {}}) diff --git a/tests/test_ucodecs.py b/tests/test_ucodecs.py index d4ff8f07..a20e9abe 100644 --- a/tests/test_ucodecs.py +++ b/tests/test_ucodecs.py @@ -40,6 +40,7 @@ def test_ucodecs(contiguous, urlpath, cparams, nchunks, codec_name, id, dtype): chunk_len = 20 * 1000 blocksize = chunk_len * dtype.itemsize / 10 cparams["blocksize"] = blocksize + cparams["typesize"] = dtype.itemsize def encoder1(input, output, meta, schunk): nd_input = input.view(dtype) @@ -71,7 +72,7 @@ def decoder1(input, output, meta, schunk): data=data, contiguous=contiguous, urlpath=urlpath, - cparams=cparams, + cparams=blosc2.CParams(**cparams), dparams=dparams, ) @@ -149,5 +150,5 @@ def test_dynamic_ucodecs_error(cparams, dparams): chunksize=chunk_len * dtype.itemsize, data=data, cparams=cparams, - dparams=dparams, + dparams=blosc2.DParams(**dparams), ) diff --git a/tests/test_ufilters.py b/tests/test_ufilters.py index 27218b26..90b925bb 100644 --- a/tests/test_ufilters.py +++ b/tests/test_ufilters.py @@ -82,7 +82,7 @@ def backward2(input, output, meta, schunk): contiguous=contiguous, urlpath=urlpath, cparams=cparams, - dparams=dparams, + dparams=blosc2.DParams(**dparams), ) out = np.empty(chunk_len * nchunks, dtype=dtype) @@ -129,7 +129,7 @@ def backward(input, output, meta, schunk): _ = blosc2.SChunk( chunksize=chunk_len * dtype.itemsize, data=data, - cparams=cparams, + cparams=blosc2.CParams(**cparams), dparams=dparams, )