diff --git a/changes/4029.bugfix.md b/changes/4029.bugfix.md deleted file mode 100644 index b4abda7658..0000000000 --- a/changes/4029.bugfix.md +++ /dev/null @@ -1,8 +0,0 @@ -`zarr.storage` is no longer replaced by a `ModuleType` subclass at import time. -The subclass only existed to warn about the long-deprecated -`zarr.storage.default_compressor` attribute, but serializers that dispatch on -the exact type of an object (such as `cloudpickle`, which Dask uses by default) -could not serialize the module and raised -`TypeError: cannot pickle 'VerboseModule' object`. Setting -`zarr.storage.default_compressor` no longer emits a deprecation warning; use -`zarr.config` to configure `array.v2_default_compressor` instead. diff --git a/changes/4505.bugfix.md b/changes/4505.bugfix.md new file mode 100644 index 0000000000..8d50b8efb6 --- /dev/null +++ b/changes/4505.bugfix.md @@ -0,0 +1 @@ +The `zarr.storage` module can now be serialized with `cloudpickle`. Previously, code that referenced the module (for example a Dask task that used `zarr.storage.ObjectStore`) failed with `TypeError: cannot pickle 'VerboseModule' object`. diff --git a/changes/4505.removal.md b/changes/4505.removal.md new file mode 100644 index 0000000000..6549a77c95 --- /dev/null +++ b/changes/4505.removal.md @@ -0,0 +1 @@ +Setting `zarr.storage.default_compressor` no longer emits a deprecation warning. The attribute has had no effect since 3.0.0; configure `array.v2_default_compressor` through `zarr.config` instead. diff --git a/pyproject.toml b/pyproject.toml index 98bdb0ab9c..c68450474e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,7 +109,6 @@ homepage = "https://github.com/zarr-developers/zarr-python" # pins deliberately, e.g. via dependabot or `uv lock --upgrade`. [dependency-groups] test = [ - "cloudpickle==3.1.2", "coverage==7.16.2", "pytest==9.1.1", "pytest-asyncio==1.4.0", diff --git a/tests/test_store/test_core.py b/tests/test_store/test_core.py index c928ba8417..daf62e3843 100644 --- a/tests/test_store/test_core.py +++ b/tests/test_store/test_core.py @@ -1,10 +1,8 @@ import tempfile from collections.abc import Awaitable, Callable, Generator from pathlib import Path -from types import ModuleType from typing import Any, Literal -import cloudpickle import pytest from packaging.version import parse as parse_version @@ -436,15 +434,3 @@ def test_different_open_mode(tmp_path: Path) -> None: match="Store is not read-only but mode is 'r'. Unable to create a read-only copy of the store. Please use a read-only store or a storage class that implements .with_read_only().", ): zarr.open_array(store=zip_store, path="a", zarr_format=2, mode="r") - - -def test_storage_module_is_picklable() -> None: - # `zarr.storage` used to be swapped for a `VerboseModule` subclass of - # `types.ModuleType` in order to warn about the long-deprecated - # `zarr.storage.default_compressor` attribute. cloudpickle dispatches on the - # exact type of an object, so the subclass was not registered and - # serializing the module raised `TypeError: cannot pickle 'VerboseModule' - # object`, which broke passing `zarr.storage.ObjectStore` around with Dask. - # See issue #4029. - assert type(zarr.storage) is ModuleType - assert cloudpickle.loads(cloudpickle.dumps(zarr.storage)) is zarr.storage diff --git a/uv.lock b/uv.lock index 7bbcce0ca0..b14033c5b4 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.12" resolution-markers = [ "python_full_version >= '3.15'", @@ -604,15 +604,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -3679,7 +3670,6 @@ remote = [ dev = [ { name = "astroid" }, { name = "botocore" }, - { name = "cloudpickle" }, { name = "coverage" }, { name = "fsspec" }, { name = "griffe-inherited-docstrings" }, @@ -3733,7 +3723,6 @@ release = [ ] remote-tests = [ { name = "botocore" }, - { name = "cloudpickle" }, { name = "coverage" }, { name = "fsspec" }, { name = "hypothesis" }, @@ -3754,7 +3743,6 @@ remote-tests = [ { name = "uv" }, ] test = [ - { name = "cloudpickle" }, { name = "coverage" }, { name = "hypothesis" }, { name = "numpydoc" }, @@ -3792,7 +3780,6 @@ provides-extras = ["cast-value-rs", "cli", "gpu", "optional", "remote"] dev = [ { name = "astroid", specifier = "==4.3.3" }, { name = "botocore" }, - { name = "cloudpickle", specifier = "==3.1.2" }, { name = "coverage", specifier = "==7.16.2" }, { name = "fsspec", specifier = ">=2023.10.0" }, { name = "griffe-inherited-docstrings", specifier = "==1.1.3" }, @@ -3844,7 +3831,6 @@ docs = [ release = [{ name = "towncrier", specifier = "==26.9.0" }] remote-tests = [ { name = "botocore" }, - { name = "cloudpickle", specifier = "==3.1.2" }, { name = "coverage", specifier = "==7.16.2" }, { name = "fsspec", specifier = ">=2023.10.0" }, { name = "hypothesis", specifier = "==6.168.3" }, @@ -3865,7 +3851,6 @@ remote-tests = [ { name = "uv", specifier = "==0.12.21" }, ] test = [ - { name = "cloudpickle", specifier = "==3.1.2" }, { name = "coverage", specifier = "==7.16.2" }, { name = "hypothesis", specifier = "==6.168.3" }, { name = "numpydoc", specifier = "==1.11.0" },