Skip to content

Commit a8de6d6

Browse files
committed
Merge branch 'v3' into fix-hypothesis-complex-warning
* v3: fix: opening a group with unspecified format finds either v2 or v3 (zarr-developers#2183) test: check that store, array, and group classes are serializable (zarr-developers#2006) feature(store): V3 ZipStore (zarr-developers#2078) More typing fixes for tests (zarr-developers#2173) refactor: split metadata into v2 and v3 modules (zarr-developers#2163) Accept dictionaries for `store` argument (zarr-developers#2164) Simplify mypy config for tests (zarr-developers#2156) Fixed path segment duplication in open_array (zarr-developers#2167) Fixed test warnings (zarr-developers#2168) chore: update pre-commit hooks (zarr-developers#2165) Ensure that store_dict used for empty dicts (zarr-developers#2162) Bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1 in the actions group (zarr-developers#2160)
2 parents b5e4f9c + b1ecdd5 commit a8de6d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1259
-802
lines changed

.github/workflows/releases.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
with:
5656
name: releases
5757
path: dist
58-
- uses: pypa/gh-action-pypi-publish@v1.10.0
58+
- uses: pypa/gh-action-pypi-publish@v1.10.1
5959
with:
6060
user: __token__
6161
password: ${{ secrets.pypi_password }}

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ default_language_version:
77
python: python3
88
repos:
99
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.6.3
10+
rev: v0.6.4
1111
hooks:
1212
- id: ruff
1313
args: ["--fix", "--show-fixes"]
@@ -25,7 +25,7 @@ repos:
2525
rev: v1.11.2
2626
hooks:
2727
- id: mypy
28-
files: src|tests/v3/test_(api|array|buffer).py
28+
files: src|tests
2929
additional_dependencies:
3030
# Package dependencies
3131
- asciitree
@@ -41,7 +41,7 @@ repos:
4141
# Zarr v2
4242
- types-redis
4343
- repo: https://github.com/scientific-python/cookie
44-
rev: 2024.04.23
44+
rev: 2024.08.19
4545
hooks:
4646
- id: sp-repo-review
4747
- repo: https://github.com/pre-commit/pygrep-hooks

pyproject.toml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ serve = "sphinx-autobuild docs docs/_build --host 0.0.0.0"
181181

182182
[tool.ruff]
183183
line-length = 100
184-
src = ["src"]
185184
force-exclude = true
186185
extend-exclude = [
187186
".bzr",
@@ -226,6 +225,7 @@ python_version = "3.10"
226225
ignore_missing_imports = true
227226
namespace_packages = false
228227

228+
229229
strict = true
230230
warn_unreachable = true
231231

@@ -237,6 +237,23 @@ module = [
237237
]
238238
ignore_errors = true
239239

240+
[[tool.mypy.overrides]]
241+
module = [
242+
"tests.v2.*",
243+
"tests.v3.package_with_entrypoint.*",
244+
"tests.v3.test_codecs.test_codecs",
245+
"tests.v3.test_codecs.test_transpose",
246+
"tests.v3.test_metadata.*",
247+
"tests.v3.test_store.*",
248+
"tests.v3.test_config",
249+
"tests.v3.test_group",
250+
"tests.v3.test_indexing",
251+
"tests.v3.test_properties",
252+
"tests.v3.test_sync",
253+
"tests.v3.test_v2",
254+
]
255+
ignore_errors = true
256+
240257
[tool.pytest.ini_options]
241258
minversion = "7"
242259
testpaths = ["tests"]
@@ -263,6 +280,6 @@ markers = [
263280

264281
[tool.repo-review]
265282
ignore = [
266-
"PC111", # fix Python code in documentation - enable later
283+
"PC111", # fix Python code in documentation - enable later
267284
"PC180", # for JavaScript - not interested
268285
]

src/zarr/abc/store.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212

1313
class AccessMode(NamedTuple):
14+
str: AccessModeLiteral
1415
readonly: bool
1516
overwrite: bool
1617
create: bool
@@ -20,6 +21,7 @@ class AccessMode(NamedTuple):
2021
def from_literal(cls, mode: AccessModeLiteral) -> Self:
2122
if mode in ("r", "r+", "a", "w", "w-"):
2223
return cls(
24+
str=mode,
2325
readonly=mode == "r",
2426
overwrite=mode == "w",
2527
create=mode in ("a", "w", "w-"),
@@ -42,6 +44,14 @@ async def open(cls, *args: Any, **kwargs: Any) -> Self:
4244
await store._open()
4345
return store
4446

47+
def __enter__(self) -> Self:
48+
"""Enter a context manager that will close the store upon exiting."""
49+
return self
50+
51+
def __exit__(self, *args: Any) -> None:
52+
"""Close the store."""
53+
self.close()
54+
4555
async def _open(self) -> None:
4656
if self._is_open:
4757
raise ValueError("store is already open")
@@ -73,6 +83,11 @@ def _check_writable(self) -> None:
7383
if self.mode.readonly:
7484
raise ValueError("store mode does not support writing")
7585

86+
@abstractmethod
87+
def __eq__(self, value: object) -> bool:
88+
"""Equality comparison."""
89+
...
90+
7691
@abstractmethod
7792
async def get(
7893
self,
@@ -143,6 +158,12 @@ async def set(self, key: str, value: Buffer) -> None:
143158
"""
144159
...
145160

161+
@property
162+
@abstractmethod
163+
def supports_deletes(self) -> bool:
164+
"""Does the store support deletes?"""
165+
...
166+
146167
@abstractmethod
147168
async def delete(self, key: str) -> None:
148169
"""Remove a key from the store
@@ -221,7 +242,6 @@ def list_dir(self, prefix: str) -> AsyncGenerator[str, None]:
221242
def close(self) -> None:
222243
"""Close the store."""
223244
self._is_open = False
224-
pass
225245

226246

227247
@runtime_checkable

src/zarr/api/asynchronous.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
from zarr.core.array import Array, AsyncArray
1111
from zarr.core.common import JSON, AccessModeLiteral, ChunkCoords, MemoryOrder, ZarrFormat
12+
from zarr.core.config import config
1213
from zarr.core.group import AsyncGroup
13-
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
14+
from zarr.core.metadata.v2 import ArrayV2Metadata
15+
from zarr.core.metadata.v3 import ArrayV3Metadata
1416
from zarr.store import (
1517
StoreLike,
1618
make_store_path,
@@ -125,8 +127,7 @@ def _handle_zarr_version_or_format(
125127

126128
def _default_zarr_version() -> ZarrFormat:
127129
"""return the default zarr_version"""
128-
# TODO: set default value from config
129-
return 3
130+
return cast(ZarrFormat, int(config.get("default_zarr_version", 3)))
130131

131132

132133
async def consolidate_metadata(*args: Any, **kwargs: Any) -> AsyncGroup:
@@ -336,7 +337,10 @@ async def save_group(
336337
kwargs
337338
NumPy arrays with data to save.
338339
"""
339-
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
340+
zarr_format = (
341+
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
342+
or _default_zarr_version()
343+
)
340344

341345
if len(args) == 0 and len(kwargs) == 0:
342346
raise ValueError("at least one array must be provided")
@@ -447,10 +451,7 @@ async def group(
447451
The new group.
448452
"""
449453

450-
zarr_format = (
451-
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
452-
or _default_zarr_version()
453-
)
454+
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
454455

455456
store_path = await make_store_path(store)
456457
if path is not None:
@@ -473,7 +474,7 @@ async def group(
473474
except (KeyError, FileNotFoundError):
474475
return await AsyncGroup.create(
475476
store=store_path,
476-
zarr_format=zarr_format,
477+
zarr_format=zarr_format or _default_zarr_version(),
477478
exists_ok=overwrite,
478479
attributes=attributes,
479480
)
@@ -482,7 +483,7 @@ async def group(
482483
async def open_group(
483484
*, # Note: this is a change from v2
484485
store: StoreLike | None = None,
485-
mode: AccessModeLiteral | None = None, # not used
486+
mode: AccessModeLiteral | None = None,
486487
cache_attrs: bool | None = None, # not used, default changed
487488
synchronizer: Any = None, # not used
488489
path: str | None = None,
@@ -497,8 +498,18 @@ async def open_group(
497498
498499
Parameters
499500
----------
500-
store : Store or string, optional
501+
store : Store, string, or mapping, optional
501502
Store or path to directory in file system or name of zip file.
503+
504+
Strings are interpreted as paths on the local file system
505+
and used as the ``root`` argument to :class:`zarr.store.LocalStore`.
506+
507+
Dictionaries are used as the ``store_dict`` argument in
508+
:class:`zarr.store.MemoryStore``.
509+
510+
By default (``store=None``) a new :class:`zarr.store.MemoryStore`
511+
is created.
512+
502513
mode : {'r', 'r+', 'a', 'w', 'w-'}, optional
503514
Persistence mode: 'r' means read only (must exist); 'r+' means
504515
read/write (must exist); 'a' means read/write (create if doesn't
@@ -527,10 +538,7 @@ async def open_group(
527538
The new group.
528539
"""
529540

530-
zarr_format = (
531-
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
532-
or _default_zarr_version()
533-
)
541+
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
534542

535543
if cache_attrs is not None:
536544
warnings.warn("cache_attrs is not yet implemented", RuntimeWarning, stacklevel=2)
@@ -554,7 +562,10 @@ async def open_group(
554562
return await AsyncGroup.open(store_path, zarr_format=zarr_format)
555563
except (KeyError, FileNotFoundError):
556564
return await AsyncGroup.create(
557-
store_path, zarr_format=zarr_format, exists_ok=True, attributes=attributes
565+
store_path,
566+
zarr_format=zarr_format or _default_zarr_version(),
567+
exists_ok=True,
568+
attributes=attributes,
558569
)
559570

560571

@@ -676,7 +687,7 @@ async def create(
676687

677688
if zarr_format == 2 and chunks is None:
678689
chunks = shape
679-
if zarr_format == 3 and chunk_shape is None:
690+
elif zarr_format == 3 and chunk_shape is None:
680691
if chunks is not None:
681692
chunk_shape = chunks
682693
chunks = None
@@ -897,8 +908,7 @@ async def open_array(
897908
if store_path.store.mode.create:
898909
return await create(
899910
store=store_path,
900-
path=path,
901-
zarr_format=zarr_format,
911+
zarr_format=zarr_format or _default_zarr_version(),
902912
overwrite=store_path.store.mode.overwrite,
903913
**kwargs,
904914
)

src/zarr/codecs/sharding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
get_indexer,
4545
morton_order_iter,
4646
)
47-
from zarr.core.metadata import parse_codecs
47+
from zarr.core.metadata.v3 import parse_codecs
4848
from zarr.registry import get_ndbuffer_class, get_pipeline_class, register_codec
4949

5050
if TYPE_CHECKING:

src/zarr/core/array.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
is_scalar,
5656
pop_fields,
5757
)
58-
from zarr.core.metadata import ArrayMetadata, ArrayV2Metadata, ArrayV3Metadata
58+
from zarr.core.metadata.v2 import ArrayV2Metadata
59+
from zarr.core.metadata.v3 import ArrayV3Metadata
5960
from zarr.core.sync import sync
6061
from zarr.registry import get_pipeline_class
6162
from zarr.store import StoreLike, StorePath, make_store_path
@@ -67,6 +68,7 @@
6768
from collections.abc import Iterable
6869

6970
from zarr.abc.codec import Codec, CodecPipeline
71+
from zarr.core.metadata.common import ArrayMetadata
7072

7173
# Array and AsyncArray are defined in the base ``zarr`` namespace
7274
__all__ = ["parse_array_metadata", "create_codec_pipeline"]

src/zarr/core/buffer/core.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ def from_buffer(cls, buffer: Buffer) -> Self:
195195
-------
196196
A new buffer representing the content of the input buffer
197197
198-
Note
199-
----
198+
Notes
199+
-----
200200
Subclasses of `Buffer` must override this method to implement
201201
more optimal conversions that avoid copies where possible
202202
"""
@@ -281,6 +281,12 @@ def __add__(self, other: Buffer) -> Self:
281281
"""Concatenate two buffers"""
282282
...
283283

284+
def __eq__(self, other: object) -> bool:
285+
# Another Buffer class can override this to choose a more efficient path
286+
return isinstance(other, Buffer) and np.array_equal(
287+
self.as_numpy_array(), other.as_numpy_array()
288+
)
289+
284290

285291
class NDBuffer:
286292
"""An n-dimensional memory block

src/zarr/core/buffer/cpu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ def from_buffer(cls, buffer: core.Buffer) -> Self:
7373
-------
7474
A new buffer representing the content of the input buffer
7575
76-
Note
77-
----
76+
Notes
77+
-----
7878
Subclasses of `Buffer` must override this method to implement
7979
more optimal conversions that avoid copies where possible
8080
"""

src/zarr/core/buffer/gpu.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class Buffer(core.Buffer):
3838
array-like instance can be copied/converted to a regular Numpy array
3939
(host memory).
4040
41-
Note
42-
----
41+
Notes
42+
-----
4343
This buffer is untyped, so all indexing and sizes are in bytes.
4444
4545
Parameters
@@ -123,8 +123,8 @@ class NDBuffer(core.NDBuffer):
123123
ndarray-like instance can be copied/converted to a regular Numpy array
124124
(host memory).
125125
126-
Note
127-
----
126+
Notes
127+
-----
128128
The two buffer classes Buffer and NDBuffer are very similar. In fact, Buffer
129129
is a special case of NDBuffer where dim=1, stride=1, and dtype="b". However,
130130
in order to use Python's type system to differentiate between the contiguous
@@ -193,8 +193,8 @@ def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self:
193193
def as_numpy_array(self) -> npt.NDArray[Any]:
194194
"""Returns the buffer as a NumPy array (host memory).
195195
196-
Warning
197-
-------
196+
Warnings
197+
--------
198198
Might have to copy data, consider using `.as_ndarray_like()` instead.
199199
200200
Returns

0 commit comments

Comments
 (0)