Skip to content

Commit 1cdfd6d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into tom/feature/object-size
2 parents 8ba85ec + ca46bab commit 1cdfd6d

Some content is hidden

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

46 files changed

+488
-317
lines changed

.github/workflows/gpu_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
strategy:
2626
matrix:
2727
python-version: ['3.11']
28-
numpy-version: ['2.0']
28+
numpy-version: ['2.1']
2929
dependency-set: ["minimal"]
3030

3131
steps:

.github/workflows/hypothesis.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
strategy:
2727
matrix:
2828
python-version: ['3.11']
29-
numpy-version: ['1.26']
29+
numpy-version: ['2.1']
3030
dependency-set: ["optional"]
3131

3232
steps:

.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.3
58+
- uses: pypa/gh-action-pypi-publish@v1.11.0
5959
with:
6060
user: __token__
6161
password: ${{ secrets.pypi_password }}

.github/workflows/test.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,33 @@ concurrency:
1616

1717
jobs:
1818
test:
19-
name: py=${{ matrix.python-version }}, np=${{ matrix.numpy-version }}, deps=${{ matrix.dependency-set }}
19+
name: os=${{ matrix.os }}, py=${{ matrix.python-version }}, np=${{ matrix.numpy-version }}, deps=${{ matrix.dependency-set }}
2020

21-
runs-on: ubuntu-latest
2221
strategy:
2322
matrix:
2423
python-version: ['3.11', '3.12', '3.13']
25-
numpy-version: ['1.25', '1.26', '2.0']
24+
numpy-version: ['1.25', '2.1']
2625
dependency-set: ["minimal", "optional"]
26+
os: ["ubuntu-latest"]
27+
include:
28+
- python-version: '3.11'
29+
numpy-version: '1.25'
30+
dependency-set: 'optional'
31+
os: 'macos-latest'
32+
- python-version: '3.13'
33+
numpy-version: '2.1'
34+
dependency-set: 'optional'
35+
os: 'macos-latest'
36+
# https://github.com/zarr-developers/zarr-python/issues/2438
37+
# - python-version: '3.11'
38+
# numpy-version: '1.25'
39+
# dependency-set: 'optional'
40+
# os: 'windows-latest'
41+
# - python-version: '3.13'
42+
# numpy-version: '2.1'
43+
# dependency-set: 'optional'
44+
# os: 'windows-latest'
45+
runs-on: ${{ matrix.os }}
2746

2847
steps:
2948
- uses: actions/checkout@v4

.pre-commit-config.yaml

Lines changed: 2 additions & 4 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.7.0
10+
rev: v0.7.2
1111
hooks:
1212
- id: ruff
1313
args: ["--fix", "--show-fixes"]
@@ -22,7 +22,7 @@ repos:
2222
hooks:
2323
- id: check-yaml
2424
- repo: https://github.com/pre-commit/mirrors-mypy
25-
rev: v1.12.1
25+
rev: v1.13.0
2626
hooks:
2727
- id: mypy
2828
files: src|tests
@@ -37,8 +37,6 @@ repos:
3737
- universal-pathlib
3838
# Tests
3939
- pytest
40-
# Zarr v2
41-
- types-redis
4240
- repo: https://github.com/scientific-python/cookie
4341
rev: 2024.08.19
4442
hooks:

pyproject.toml

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ test = [
6161
"pytest",
6262
"pytest-cov",
6363
"msgpack",
64-
"lmdb",
6564
"s3fs",
6665
"pytest-asyncio",
6766
"moto[s3]",
@@ -84,21 +83,19 @@ gpu = [
8483
docs = [
8584
'sphinx==8.1.3',
8685
'sphinx-autobuild>=2021.3.14',
87-
'sphinx-autoapi==3.3.2',
86+
'sphinx-autoapi==3.3.3',
8887
'sphinx_design',
8988
'sphinx-issues',
9089
'sphinx-copybutton',
9190
'pydata-sphinx-theme',
9291
'numpydoc',
9392
'numcodecs[msgpack]',
9493
'msgpack',
95-
'lmdb',
9694
]
9795
extra = [
9896
'msgpack',
9997
]
10098
optional = [
101-
'lmdb',
10299
'universal-pathlib>=0.0.22',
103100
]
104101

@@ -135,17 +132,17 @@ features = ["test", "extra"]
135132

136133
[[tool.hatch.envs.test.matrix]]
137134
python = ["3.11", "3.12", "3.13"]
138-
numpy = ["1.25", "1.26", "2.0"]
135+
numpy = ["1.25", "2.1"]
139136
version = ["minimal"]
140137

141138
[[tool.hatch.envs.test.matrix]]
142139
python = ["3.11", "3.12", "3.13"]
143-
numpy = ["1.25", "1.26", "2.0"]
140+
numpy = ["1.25", "2.1"]
144141
features = ["optional"]
145142

146143
[[tool.hatch.envs.test.matrix]]
147144
python = ["3.11", "3.12", "3.13"]
148-
numpy = ["1.25", "1.26", "2.0"]
145+
numpy = ["1.25", "2.1"]
149146
features = ["gpu"]
150147

151148
[tool.hatch.envs.test.scripts]
@@ -166,7 +163,7 @@ features = ["test", "extra", "gpu"]
166163

167164
[[tool.hatch.envs.gputest.matrix]]
168165
python = ["3.11", "3.12", "3.13"]
169-
numpy = ["1.25", "1.26", "2.0"]
166+
numpy = ["1.25", "2.1"]
170167
version = ["minimal"]
171168

172169
[tool.hatch.envs.gputest.scripts]
@@ -272,19 +269,25 @@ extend-exclude = [
272269
extend-select = [
273270
"ANN", # flake8-annotations
274271
"B", # flake8-bugbear
272+
"EXE", # flake8-executable
275273
"C4", # flake8-comprehensions
274+
"FA", # flake8-future-annotations
276275
"FLY", # flynt
277276
"FURB", # refurb
278277
"G", # flake8-logging-format
279278
"I", # isort
280279
"ISC", # flake8-implicit-str-concat
280+
"LOG", # flake8-logging
281281
"PERF", # Perflint
282+
"PIE", # flake8-pie
282283
"PGH", # pygrep-hooks
283284
"PT", # flake8-pytest-style
284285
"PYI", # flake8-pyi
285-
"RSE", # flake8-raise
286286
"RET", # flake8-return
287+
"RSE", # flake8-raise
287288
"RUF",
289+
"SIM", # flake8-simplify
290+
"SLOT", # flake8-slots
288291
"TCH", # flake8-type-checking
289292
"TRY", # tryceratops
290293
"UP", # pyupgrade
@@ -301,6 +304,7 @@ ignore = [
301304
"RET505",
302305
"RET506",
303306
"RUF005",
307+
"SIM108",
304308
"TRY003",
305309
"UP027", # deprecated
306310
"UP038", # https://github.com/astral-sh/ruff/issues/7871
@@ -322,7 +326,7 @@ ignore = [
322326
]
323327

324328
[tool.ruff.lint.extend-per-file-ignores]
325-
"tests/**" = ["ANN001", "ANN201"]
329+
"tests/**" = ["ANN001", "ANN201", "RUF029", "SIM117", "SIM300"]
326330

327331
[tool.mypy]
328332
python_version = "3.11"

src/zarr/abc/codec.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ def validate(self, *, shape: ChunkCoords, dtype: np.dtype[Any], chunk_grid: Chun
106106
chunk_grid : ChunkGrid
107107
The array chunk grid
108108
"""
109-
...
110109

111110
async def _decode_single(self, chunk_data: CodecOutput, chunk_spec: ArraySpec) -> CodecInput:
112111
raise NotImplementedError

src/zarr/abc/metadata.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,5 @@ def from_dict(cls, data: dict[str, JSON]) -> Self:
4242
"""
4343
Create an instance of the model from a dictionary
4444
"""
45-
...
4645

4746
return cls(**data)

src/zarr/abc/store.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ async def _set_many(self, values: Iterable[tuple[str, Buffer]]) -> None:
289289
Insert multiple (key, value) pairs into storage.
290290
"""
291291
await gather(*starmap(self.set, values))
292-
return
293292

294293
@property
295294
@abstractmethod
@@ -347,8 +346,8 @@ def list(self) -> AsyncGenerator[str, None]:
347346
@abstractmethod
348347
def list_prefix(self, prefix: str) -> AsyncGenerator[str, None]:
349348
"""
350-
Retrieve all keys in the store that begin with a given prefix. Keys are returned with the
351-
common leading prefix removed.
349+
Retrieve all keys in the store that begin with a given prefix. Keys are returned relative
350+
to the root of the store.
352351
353352
Parameters
354353
----------
@@ -376,6 +375,20 @@ def list_dir(self, prefix: str) -> AsyncGenerator[str, None]:
376375
"""
377376
...
378377

378+
async def delete_dir(self, prefix: str) -> None:
379+
"""
380+
Remove all keys and prefixes in the store that begin with a given prefix.
381+
"""
382+
if not self.supports_deletes:
383+
raise NotImplementedError
384+
if not self.supports_listing:
385+
raise NotImplementedError
386+
self._check_writable()
387+
if not prefix.endswith("/"):
388+
prefix += "/"
389+
async for key in self.list_prefix(prefix):
390+
await self.delete(key)
391+
379392
def close(self) -> None:
380393
"""Close the store."""
381394
self._is_open = False

src/zarr/api/asynchronous.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from zarr.abc.store import Store
1212
from zarr.core.array import Array, AsyncArray, get_array_metadata
13+
from zarr.core.buffer import NDArrayLike
1314
from zarr.core.common import (
1415
JSON,
1516
AccessModeLiteral,
@@ -31,7 +32,6 @@
3132
from collections.abc import Iterable
3233

3334
from zarr.abc.codec import Codec
34-
from zarr.core.buffer import NDArrayLike
3535
from zarr.core.chunk_key_encodings import ChunkKeyEncoding
3636

3737
# TODO: this type could use some more thought
@@ -393,15 +393,21 @@ async def save_array(
393393
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
394394
or _default_zarr_version()
395395
)
396+
if not isinstance(arr, NDArrayLike):
397+
raise TypeError("arr argument must be numpy or other NDArrayLike array")
396398

397399
mode = kwargs.pop("mode", None)
398400
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
401+
if np.isscalar(arr):
402+
arr = np.array(arr)
403+
shape = arr.shape
404+
chunks = getattr(arr, "chunks", None) # for array-likes with chunks attribute
399405
new = await AsyncArray.create(
400406
store_path,
401407
zarr_format=zarr_format,
402-
shape=arr.shape,
408+
shape=shape,
403409
dtype=arr.dtype,
404-
chunks=arr.shape,
410+
chunks=chunks,
405411
**kwargs,
406412
)
407413
await new.setitem(slice(None), arr)
@@ -443,16 +449,26 @@ async def save_group(
443449
or _default_zarr_version()
444450
)
445451

452+
for arg in args:
453+
if not isinstance(arg, NDArrayLike):
454+
raise TypeError(
455+
"All arguments must be numpy or other NDArrayLike arrays (except store, path, storage_options, and zarr_format)"
456+
)
457+
for k, v in kwargs.items():
458+
if not isinstance(v, NDArrayLike):
459+
raise TypeError(f"Keyword argument '{k}' must be a numpy or other NDArrayLike array")
460+
446461
if len(args) == 0 and len(kwargs) == 0:
447462
raise ValueError("at least one array must be provided")
448463
aws = []
449464
for i, arr in enumerate(args):
465+
_path = f"{path}/arr_{i}" if path is not None else f"arr_{i}"
450466
aws.append(
451467
save_array(
452468
store,
453469
arr,
454470
zarr_format=zarr_format,
455-
path=f"{path}/arr_{i}",
471+
path=_path,
456472
storage_options=storage_options,
457473
)
458474
)
@@ -862,9 +878,8 @@ async def create(
862878
warnings.warn("meta_array is not yet implemented", RuntimeWarning, stacklevel=2)
863879

864880
mode = kwargs.pop("mode", None)
865-
if mode is None:
866-
if not isinstance(store, Store | StorePath):
867-
mode = "a"
881+
if mode is None and not isinstance(store, Store | StorePath):
882+
mode = "a"
868883

869884
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
870885

0 commit comments

Comments
 (0)