From 3b4ca71c4145178b48955fadf335122783fb66a6 Mon Sep 17 00:00:00 2001 From: ayaan Date: Tue, 20 Oct 2026 23:54:29 +0530 Subject: [PATCH 1/9] feat: expose blosc.use_threads via zarr.config --- changes/4531.feature.md | 1 + src/zarr/codecs/blosc.py | 4 ++-- src/zarr/core/config.py | 21 ++++++++++++++++++--- tests/test_config.py | 9 +++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 changes/4531.feature.md diff --git a/changes/4531.feature.md b/changes/4531.feature.md new file mode 100644 index 0000000000..549f96ea9f --- /dev/null +++ b/changes/4531.feature.md @@ -0,0 +1 @@ +Expose blosc.use_threads via zarr.config, allowing users to enable Blosc's internal threading for faster decoding of large single chunks. diff --git a/src/zarr/codecs/blosc.py b/src/zarr/codecs/blosc.py index ee45632153..fd5bd1d60c 100644 --- a/src/zarr/codecs/blosc.py +++ b/src/zarr/codecs/blosc.py @@ -13,6 +13,7 @@ from zarr.codecs._deprecated_enum import _coerce_enum_input, _DeprecatedStrEnumMeta from zarr.core.buffer.cpu import as_numpy_array_wrapper from zarr.core.common import JSON, NamedRequiredConfig, parse_named_configuration +from zarr.core.config import config from zarr.core.dtype.common import HasItemSize from zarr.core.json_parse import parse_field @@ -101,8 +102,7 @@ class BloscCname(metaclass=_DeprecatedStrEnumMeta): # See https://zarr.readthedocs.io/en/stable/user-guide/performance.html#configuring-blosc -numcodecs.blosc.use_threads = False - +numcodecs.blosc.use_threads = config.get("blosc.use_threads", False) def parse_typesize(data: JSON) -> int: parsed: int = parse_field(data, int, "typesize", error=TypeError) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index c0ebb31353..235857c713 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -57,19 +57,31 @@ class Config(DConfig): # type: ignore[misc] """ + def set(self, arg=None, **kwargs): + # Call the original donfig set method to handle the config state + result = super().set(arg, **kwargs) + # Sync the numcodecs blosc threads setting + import numcodecs + numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) + return result + + def refresh(self, *args, **kwargs): + result = super().refresh(*args, **kwargs) + import numcodecs + numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) + return result + def reset(self) -> None: self.clear() self.refresh() - def enable_gpu(self) -> ConfigSet: + def enable_gpu(self) -> "ConfigSet": """ Configure Zarr to use GPUs where possible. """ return self.set( {"buffer": "zarr.buffer.gpu.Buffer", "ndbuffer": "zarr.buffer.gpu.NDBuffer"} ) - - # these keys were removed from the config as part of the 3.1.0 release. # these deprecations should be removed in 3.1.1 or thereabouts. deprecations = { @@ -119,6 +131,9 @@ def enable_gpu(self) -> ConfigSet: # Only read by FusedCodecPipeline (BatchedCodecPipeline ignores it). "max_workers": None, }, + "blosc": { + "use_threads": False, + }, "codecs": { "blosc": "zarr.codecs.blosc.BloscCodec", "gzip": "zarr.codecs.gzip.GzipCodec", diff --git a/tests/test_config.py b/tests/test_config.py index 47f71a798e..ceb72fbb99 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -68,6 +68,9 @@ def test_config_defaults_set() -> None: "batch_size": 1, "max_workers": None, }, + "blosc": { + "use_threads": False, + }, "codecs": { "blosc": "zarr.codecs.blosc.BloscCodec", "gzip": "zarr.codecs.gzip.GzipCodec", @@ -483,3 +486,9 @@ def test_deprecated_config(key: str) -> None: with pytest.raises(ValueError): with zarr.config.set({key: "foo"}): pass + +def test_blosc_use_threads_config() -> None: + import numcodecs + assert numcodecs.blosc.use_threads is False + with config.set({"blosc.use_threads": True}): + assert numcodecs.blosc.use_threads is True From 060e5c4da9a194fa79a92a1ab5581fe22d821f78 Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 00:04:11 +0530 Subject: [PATCH 2/9] fix: move numcodecs import to top level and remove local imports --- src/zarr/core/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 235857c713..b5e4eb8e12 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -29,6 +29,7 @@ from __future__ import annotations +import numcodecs from typing import TYPE_CHECKING, Any, Literal, cast from donfig import Config as DConfig @@ -61,13 +62,11 @@ def set(self, arg=None, **kwargs): # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) # Sync the numcodecs blosc threads setting - import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) - import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result From 5f1211909da0834151b91c98c885280fa44b7a6a Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 00:10:12 +0530 Subject: [PATCH 3/9] fix: use noqa for local numcodecs import to avoid circular dependency --- src/zarr/core/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index b5e4eb8e12..1af42f2ba8 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -29,7 +29,6 @@ from __future__ import annotations -import numcodecs from typing import TYPE_CHECKING, Any, Literal, cast from donfig import Config as DConfig @@ -62,11 +61,13 @@ def set(self, arg=None, **kwargs): # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) # Sync the numcodecs blosc threads setting + import numcodecs # noqa: PLC0415 numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) + import numcodecs # noqa: PLC0415 numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result From b75010fd9d3efaff12acbad3f20c23e596eee5ef Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 00:15:26 +0530 Subject: [PATCH 4/9] fix: resolve lint and docs build by using local blosc imports --- src/zarr/core/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 1af42f2ba8..81ae2b80b7 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -37,6 +37,7 @@ if TYPE_CHECKING: from donfig.config_obj import ConfigSet + import numcodecs class BadConfigError(ValueError): @@ -61,13 +62,13 @@ def set(self, arg=None, **kwargs): # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) # Sync the numcodecs blosc threads setting - import numcodecs # noqa: PLC0415 + import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) - import numcodecs # noqa: PLC0415 + import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result From e43c0fb38512c23d72f2a0822973503b58a31205 Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 00:21:08 +0530 Subject: [PATCH 5/9] fix: move numcodecs import to top level to satisfy lint --- src/zarr/core/config.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 81ae2b80b7..9fb4696774 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -29,6 +29,7 @@ from __future__ import annotations +import numcodecs from typing import TYPE_CHECKING, Any, Literal, cast from donfig import Config as DConfig @@ -37,8 +38,6 @@ if TYPE_CHECKING: from donfig.config_obj import ConfigSet - import numcodecs - class BadConfigError(ValueError): _msg = "bad Config: %r" @@ -62,13 +61,11 @@ def set(self, arg=None, **kwargs): # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) # Sync the numcodecs blosc threads setting - import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) - import numcodecs numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result From 0abb755519226e2e947d06c3ba2790b239a273cc Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 00:29:58 +0530 Subject: [PATCH 6/9] style: apply ruff formatting --- src/zarr/core/config.py | 5 ++++- tests/test_config.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 9fb4696774..2143382273 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -39,6 +39,7 @@ if TYPE_CHECKING: from donfig.config_obj import ConfigSet + class BadConfigError(ValueError): _msg = "bad Config: %r" @@ -73,13 +74,15 @@ def reset(self) -> None: self.clear() self.refresh() - def enable_gpu(self) -> "ConfigSet": + def enable_gpu(self) -> ConfigSet: """ Configure Zarr to use GPUs where possible. """ return self.set( {"buffer": "zarr.buffer.gpu.Buffer", "ndbuffer": "zarr.buffer.gpu.NDBuffer"} ) + + # these keys were removed from the config as part of the 3.1.0 release. # these deprecations should be removed in 3.1.1 or thereabouts. deprecations = { diff --git a/tests/test_config.py b/tests/test_config.py index ceb72fbb99..458ce6b5e0 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -489,6 +489,7 @@ def test_deprecated_config(key: str) -> None: def test_blosc_use_threads_config() -> None: import numcodecs + assert numcodecs.blosc.use_threads is False with config.set({"blosc.use_threads": True}): - assert numcodecs.blosc.use_threads is True + assert numcodecs.blosc.use_threads is True \ No newline at end of file From ba9c4c6a550a1792fd26d20b2e38a5a31e258e90 Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 13:23:55 +0530 Subject: [PATCH 7/9] fix: use importlib for lazy numcodecs import and fix formatting --- src/zarr/core/config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 2143382273..b45b2e8dac 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -62,11 +62,15 @@ def set(self, arg=None, **kwargs): # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) # Sync the numcodecs blosc threads setting + import importlib + numcodecs = importlib.import_module("numcodecs") numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) + import importlib + numcodecs = importlib.import_module("numcodecs") numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result @@ -82,7 +86,7 @@ def enable_gpu(self) -> ConfigSet: {"buffer": "zarr.buffer.gpu.Buffer", "ndbuffer": "zarr.buffer.gpu.NDBuffer"} ) - + # these keys were removed from the config as part of the 3.1.0 release. # these deprecations should be removed in 3.1.1 or thereabouts. deprecations = { From 2e839942b215077641b5784bdee8d40951126c90 Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 13:40:19 +0530 Subject: [PATCH 8/9] fix: use importlib and update comments --- src/zarr/core/config.py | 14 ++++++-------- tests/test_config.py | 10 ---------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index b45b2e8dac..58c69d0bc8 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -29,7 +29,8 @@ from __future__ import annotations -import numcodecs +import importlib + from typing import TYPE_CHECKING, Any, Literal, cast from donfig import Config as DConfig @@ -58,18 +59,15 @@ class Config(DConfig): # type: ignore[misc] """ + def set(self, arg=None, **kwargs): - # Call the original donfig set method to handle the config state result = super().set(arg, **kwargs) - # Sync the numcodecs blosc threads setting - import importlib numcodecs = importlib.import_module("numcodecs") numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result def refresh(self, *args, **kwargs): result = super().refresh(*args, **kwargs) - import importlib numcodecs = importlib.import_module("numcodecs") numcodecs.blosc.use_threads = self.get("blosc.use_threads", False) return result @@ -124,6 +122,9 @@ def enable_gpu(self) -> ConfigSet: "async": {"concurrency": 10, "timeout": None}, "threading": {"max_workers": None}, "json_indent": 2, + "blosc": { + "use_threads": False, + }, "codec_pipeline": { # FusedCodecPipeline is the faster synchronous pipeline, but it stays # opt-in for now so behavior is unchanged for existing users. Early @@ -136,9 +137,6 @@ def enable_gpu(self) -> ConfigSet: # Only read by FusedCodecPipeline (BatchedCodecPipeline ignores it). "max_workers": None, }, - "blosc": { - "use_threads": False, - }, "codecs": { "blosc": "zarr.codecs.blosc.BloscCodec", "gzip": "zarr.codecs.gzip.GzipCodec", diff --git a/tests/test_config.py b/tests/test_config.py index 458ce6b5e0..47f71a798e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -68,9 +68,6 @@ def test_config_defaults_set() -> None: "batch_size": 1, "max_workers": None, }, - "blosc": { - "use_threads": False, - }, "codecs": { "blosc": "zarr.codecs.blosc.BloscCodec", "gzip": "zarr.codecs.gzip.GzipCodec", @@ -486,10 +483,3 @@ def test_deprecated_config(key: str) -> None: with pytest.raises(ValueError): with zarr.config.set({key: "foo"}): pass - -def test_blosc_use_threads_config() -> None: - import numcodecs - - assert numcodecs.blosc.use_threads is False - with config.set({"blosc.use_threads": True}): - assert numcodecs.blosc.use_threads is True \ No newline at end of file From b16374929603fca6d664782bbf8ca375780dfea3 Mon Sep 17 00:00:00 2001 From: ayaan Date: Wed, 21 Oct 2026 13:53:39 +0530 Subject: [PATCH 9/9] fix: restore files and fix formatting perfectly --- changes/4531.feature.md | 2 +- src/zarr/codecs/blosc.py | 4 ++-- src/zarr/core/config.py | 1 - tests/test_config.py | 8 ++++++++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/changes/4531.feature.md b/changes/4531.feature.md index 549f96ea9f..693d339e46 100644 --- a/changes/4531.feature.md +++ b/changes/4531.feature.md @@ -1 +1 @@ -Expose blosc.use_threads via zarr.config, allowing users to enable Blosc's internal threading for faster decoding of large single chunks. +Expose `blosc.use_threads` via `zarr.config`, allowing users to enable Blosc's internal threading for faster decoding of large single chunks. diff --git a/src/zarr/codecs/blosc.py b/src/zarr/codecs/blosc.py index fd5bd1d60c..ee45632153 100644 --- a/src/zarr/codecs/blosc.py +++ b/src/zarr/codecs/blosc.py @@ -13,7 +13,6 @@ from zarr.codecs._deprecated_enum import _coerce_enum_input, _DeprecatedStrEnumMeta from zarr.core.buffer.cpu import as_numpy_array_wrapper from zarr.core.common import JSON, NamedRequiredConfig, parse_named_configuration -from zarr.core.config import config from zarr.core.dtype.common import HasItemSize from zarr.core.json_parse import parse_field @@ -102,7 +101,8 @@ class BloscCname(metaclass=_DeprecatedStrEnumMeta): # See https://zarr.readthedocs.io/en/stable/user-guide/performance.html#configuring-blosc -numcodecs.blosc.use_threads = config.get("blosc.use_threads", False) +numcodecs.blosc.use_threads = False + def parse_typesize(data: JSON) -> int: parsed: int = parse_field(data, int, "typesize", error=TypeError) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index 58c69d0bc8..564a7fbcfc 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -28,7 +28,6 @@ """ from __future__ import annotations - import importlib from typing import TYPE_CHECKING, Any, Literal, cast diff --git a/tests/test_config.py b/tests/test_config.py index 47f71a798e..4d3e0ff254 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -483,3 +483,11 @@ def test_deprecated_config(key: str) -> None: with pytest.raises(ValueError): with zarr.config.set({key: "foo"}): pass + + +def test_blosc_use_threads_config() -> None: + import numcodecs + + assert numcodecs.blosc.use_threads is False + with config.set({"blosc.use_threads": True}): + assert numcodecs.blosc.use_threads is True