diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 705cd31cb5..84bb89d82a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -57,7 +57,22 @@ body: id: reproduce attributes: label: Steps to reproduce - description: Minimal, reproducible code sample, a copy-pastable example if possible. + description: Minimal, reproducible code sample. Must list dependencies in [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#example). When put in a file named `issue.py` calling `uv run issue.py` should show the issue. + value: | + ```python + # /// script + # requires-python = ">=3.11" + # dependencies = [ + # "zarr@git+https://github.com/zarr-developers/zarr-python.git@main", + # ] + # /// + # + # This script automatically imports the development branch of zarr to check for issues + + import zarr + # your reproducer code + # zarr.print_debug_info() + ``` validations: required: true - type: textarea diff --git a/changes/3081.feature.rst b/changes/3081.feature.rst new file mode 100644 index 0000000000..8cf83ea7c2 --- /dev/null +++ b/changes/3081.feature.rst @@ -0,0 +1 @@ +Adds ``fill_value`` to the list of attributes displayed in the output of the ``AsyncArray.info()`` method. \ No newline at end of file diff --git a/docs/user-guide/arrays.rst b/docs/user-guide/arrays.rst index e6d1bcdc54..5bd6b1500f 100644 --- a/docs/user-guide/arrays.rst +++ b/docs/user-guide/arrays.rst @@ -183,6 +183,7 @@ which can be used to print useful diagnostics, e.g.:: Type : Array Zarr format : 3 Data type : DataType.int32 + Fill value : 0 Shape : (10000, 10000) Chunk shape : (1000, 1000) Order : C @@ -200,6 +201,7 @@ prints additional diagnostics, e.g.:: Type : Array Zarr format : 3 Data type : DataType.int32 + Fill value : 0 Shape : (10000, 10000) Chunk shape : (1000, 1000) Order : C @@ -287,6 +289,7 @@ Here is an example using a delta filter with the Blosc compressor:: Type : Array Zarr format : 3 Data type : DataType.int32 + Fill value : 0 Shape : (10000, 10000) Chunk shape : (1000, 1000) Order : C @@ -601,6 +604,7 @@ Sharded arrays can be created by providing the ``shards`` parameter to :func:`za Type : Array Zarr format : 3 Data type : DataType.uint8 + Fill value : 0 Shape : (10000, 10000) Shard shape : (1000, 1000) Chunk shape : (100, 100) diff --git a/docs/user-guide/groups.rst b/docs/user-guide/groups.rst index d5a0a7ccee..99234bad4e 100644 --- a/docs/user-guide/groups.rst +++ b/docs/user-guide/groups.rst @@ -129,6 +129,7 @@ property. E.g.:: Type : Array Zarr format : 3 Data type : DataType.int64 + Fill value : 0 Shape : (1000000,) Chunk shape : (100000,) Order : C @@ -145,6 +146,7 @@ property. E.g.:: Type : Array Zarr format : 3 Data type : DataType.float32 + Fill value : 0.0 Shape : (1000, 1000) Chunk shape : (100, 100) Order : C diff --git a/docs/user-guide/performance.rst b/docs/user-guide/performance.rst index 42d830780f..88329f11b8 100644 --- a/docs/user-guide/performance.rst +++ b/docs/user-guide/performance.rst @@ -92,6 +92,7 @@ To use sharding, you need to specify the ``shards`` parameter when creating the Type : Array Zarr format : 3 Data type : DataType.uint8 + Fill value : 0 Shape : (10000, 10000, 1000) Shard shape : (1000, 1000, 1000) Chunk shape : (100, 100, 100) @@ -122,6 +123,7 @@ ratios, depending on the correlation structure within the data. E.g.:: Type : Array Zarr format : 3 Data type : DataType.int32 + Fill value : 0 Shape : (10000, 10000) Chunk shape : (1000, 1000) Order : C @@ -141,6 +143,7 @@ ratios, depending on the correlation structure within the data. E.g.:: Type : Array Zarr format : 3 Data type : DataType.int32 + Fill value : 0 Shape : (10000, 10000) Chunk shape : (1000, 1000) Order : F diff --git a/pyproject.toml b/pyproject.toml index f1c290e1b1..a43e51abd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -291,8 +291,8 @@ extend-exclude = [ extend-select = [ "ANN", # flake8-annotations "B", # flake8-bugbear - "EXE", # flake8-executable "C4", # flake8-comprehensions + "EXE", # flake8-executable "FA", # flake8-future-annotations "FLY", # flynt "FURB", # refurb @@ -364,6 +364,7 @@ module = [ "tests.test_store.test_local", "tests.test_store.test_fsspec", "tests.test_store.test_memory", + "tests.test_codecs.test_codecs", ] strict = false @@ -371,7 +372,6 @@ strict = false # and fix the errors [[tool.mypy.overrides]] module = [ - "tests.test_codecs.test_codecs", "tests.test_metadata.*", "tests.test_store.test_core", "tests.test_store.test_logging", diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 4f3c9c3f8f..5b9c0bee3d 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -329,7 +329,7 @@ async def open( try: metadata_dict = await get_array_metadata(store_path, zarr_format=zarr_format) # TODO: remove this cast when we fix typing for array metadata dicts - _metadata_dict = cast(ArrayMetadataDict, metadata_dict) + _metadata_dict = cast("ArrayMetadataDict", metadata_dict) # for v2, the above would already have raised an exception if not an array zarr_format = _metadata_dict["zarr_format"] is_v3_array = zarr_format == 3 and _metadata_dict.get("node_type") == "array" diff --git a/src/zarr/api/synchronous.py b/src/zarr/api/synchronous.py index d4b652ad6e..1fe9487ed6 100644 --- a/src/zarr/api/synchronous.py +++ b/src/zarr/api/synchronous.py @@ -6,7 +6,6 @@ import zarr.api.asynchronous as async_api import zarr.core.array -from zarr._compat import _deprecate_positional_args from zarr.core.array import Array, AsyncArray, CompressorLike from zarr.core.group import Group from zarr.core.sync import sync @@ -154,7 +153,6 @@ def load( ) -@_deprecate_positional_args def open( store: StoreLike | None = None, *, @@ -248,7 +246,6 @@ def save( ) -@_deprecate_positional_args def save_array( store: StoreLike, arr: NDArrayLike, @@ -380,7 +377,6 @@ def array(data: npt.ArrayLike | Array, **kwargs: Any) -> Array: return Array(sync(async_api.array(data=data, **kwargs))) -@_deprecate_positional_args def group( store: StoreLike | None = None, *, @@ -448,7 +444,6 @@ def group( ) -@_deprecate_positional_args def open_group( store: StoreLike | None = None, *, diff --git a/src/zarr/codecs/crc32c_.py b/src/zarr/codecs/crc32c_.py index ab8a57eba7..6da673ceac 100644 --- a/src/zarr/codecs/crc32c_.py +++ b/src/zarr/codecs/crc32c_.py @@ -40,7 +40,9 @@ async def _decode_single( inner_bytes = data[:-4] # Need to do a manual cast until https://github.com/numpy/numpy/issues/26783 is resolved - computed_checksum = np.uint32(crc32c(cast(typing_extensions.Buffer, inner_bytes))).tobytes() + computed_checksum = np.uint32( + crc32c(cast("typing_extensions.Buffer", inner_bytes)) + ).tobytes() stored_checksum = bytes(crc32_bytes) if computed_checksum != stored_checksum: raise ValueError( @@ -55,7 +57,7 @@ async def _encode_single( ) -> Buffer | None: data = chunk_bytes.as_numpy_array() # Calculate the checksum and "cast" it to a numpy array - checksum = np.array([crc32c(cast(typing_extensions.Buffer, data))], dtype=np.uint32) + checksum = np.array([crc32c(cast("typing_extensions.Buffer", data))], dtype=np.uint32) # Append the checksum (as bytes) to the data return chunk_spec.prototype.buffer.from_array_like(np.append(data, checksum.view("B"))) diff --git a/src/zarr/codecs/sharding.py b/src/zarr/codecs/sharding.py index bee36b3160..4638d973cb 100644 --- a/src/zarr/codecs/sharding.py +++ b/src/zarr/codecs/sharding.py @@ -115,7 +115,7 @@ class _ShardIndex(NamedTuple): def chunks_per_shard(self) -> ChunkCoords: result = tuple(self.offsets_and_lengths.shape[0:-1]) # The cast is required until https://github.com/numpy/numpy/pull/27211 is merged - return cast(ChunkCoords, result) + return cast("ChunkCoords", result) def _localize_chunk(self, chunk_coords: ChunkCoords) -> ChunkCoords: return tuple( diff --git a/src/zarr/codecs/transpose.py b/src/zarr/codecs/transpose.py index 1aa1eb40e2..85e4526b8b 100644 --- a/src/zarr/codecs/transpose.py +++ b/src/zarr/codecs/transpose.py @@ -23,7 +23,7 @@ def parse_transpose_order(data: JSON | Iterable[int]) -> tuple[int, ...]: raise TypeError(f"Expected an iterable. Got {data} instead.") if not all(isinstance(a, int) for a in data): raise TypeError(f"Expected an iterable of integers. Got {data} instead.") - return tuple(cast(Iterable[int], data)) + return tuple(cast("Iterable[int]", data)) @dataclass(frozen=True) diff --git a/src/zarr/core/_info.py b/src/zarr/core/_info.py index 845552c8be..ee953d4591 100644 --- a/src/zarr/core/_info.py +++ b/src/zarr/core/_info.py @@ -67,7 +67,7 @@ def byte_info(size: int) -> str: return f"{size} ({human_readable_size(size)})" -@dataclasses.dataclass(kw_only=True) +@dataclasses.dataclass(kw_only=True, frozen=True, slots=True) class ArrayInfo: """ Visual summary for an Array. @@ -79,6 +79,7 @@ class ArrayInfo: _type: Literal["Array"] = "Array" _zarr_format: ZarrFormat _data_type: np.dtype[Any] | DataType + _fill_value: object _shape: tuple[int, ...] _shard_shape: tuple[int, ...] | None = None _chunk_shape: tuple[int, ...] | None = None @@ -97,6 +98,7 @@ def __repr__(self) -> str: Type : {_type} Zarr format : {_zarr_format} Data type : {_data_type} + Fill value : {_fill_value} Shape : {_shape}""") if self._shard_shape is not None: diff --git a/src/zarr/core/array.py b/src/zarr/core/array.py index 78b5e92ed6..b3e0e56428 100644 --- a/src/zarr/core/array.py +++ b/src/zarr/core/array.py @@ -26,7 +26,6 @@ from typing_extensions import deprecated import zarr -from zarr._compat import _deprecate_positional_args from zarr.abc.codec import ArrayArrayCodec, ArrayBytesCodec, BytesBytesCodec, Codec from zarr.abc.store import Store, set_or_delete from zarr.codecs._v2 import V2Codec @@ -395,7 +394,6 @@ async def create( @classmethod @deprecated("Use zarr.api.asynchronous.create_array instead.") - @_deprecate_positional_args async def create( cls, store: StoreLike, @@ -903,7 +901,7 @@ async def open( store_path = await make_store_path(store) metadata_dict = await get_array_metadata(store_path, zarr_format=zarr_format) # TODO: remove this cast when we have better type hints - _metadata_dict = cast(ArrayV3MetadataDict, metadata_dict) + _metadata_dict = cast("ArrayV3MetadataDict", metadata_dict) return cls(store_path=store_path, metadata=_metadata_dict) @property @@ -1399,7 +1397,7 @@ async def _set_selection( if isinstance(array_like, np._typing._SupportsArrayFunc): # TODO: need to handle array types that don't support __array_function__ # like PyTorch and JAX - array_like_ = cast(np._typing._SupportsArrayFunc, array_like) + array_like_ = cast("np._typing._SupportsArrayFunc", array_like) value = np.asanyarray(value, dtype=self.metadata.dtype, like=array_like_) else: if not hasattr(value, "shape"): @@ -1413,7 +1411,7 @@ async def _set_selection( value = value.astype(dtype=self.metadata.dtype, order="A") else: value = np.array(value, dtype=self.metadata.dtype, order="A") - value = cast(NDArrayLike, value) + value = cast("NDArrayLike", value) # We accept any ndarray like object from the user and convert it # to a NDBuffer (or subclass). From this point onwards, we only pass # Buffer and NDBuffer between components. @@ -1702,6 +1700,7 @@ def _info( return ArrayInfo( _zarr_format=self.metadata.zarr_format, _data_type=_data_type, + _fill_value=self.metadata.fill_value, _shape=self.shape, _order=self.order, _shard_shape=self.shards, @@ -1728,7 +1727,6 @@ class Array: @classmethod @deprecated("Use zarr.create_array instead.") - @_deprecate_positional_args def create( cls, store: StoreLike, @@ -2436,11 +2434,11 @@ def __getitem__(self, selection: Selection) -> NDArrayLikeOrScalar: """ fields, pure_selection = pop_fields(selection) if is_pure_fancy_indexing(pure_selection, self.ndim): - return self.vindex[cast(CoordinateSelection | MaskSelection, selection)] + return self.vindex[cast("CoordinateSelection | MaskSelection", selection)] elif is_pure_orthogonal_indexing(pure_selection, self.ndim): return self.get_orthogonal_selection(pure_selection, fields=fields) else: - return self.get_basic_selection(cast(BasicSelection, pure_selection), fields=fields) + return self.get_basic_selection(cast("BasicSelection", pure_selection), fields=fields) def __setitem__(self, selection: Selection, value: npt.ArrayLike) -> None: """Modify data for an item or region of the array. @@ -2535,13 +2533,12 @@ def __setitem__(self, selection: Selection, value: npt.ArrayLike) -> None: """ fields, pure_selection = pop_fields(selection) if is_pure_fancy_indexing(pure_selection, self.ndim): - self.vindex[cast(CoordinateSelection | MaskSelection, selection)] = value + self.vindex[cast("CoordinateSelection | MaskSelection", selection)] = value elif is_pure_orthogonal_indexing(pure_selection, self.ndim): self.set_orthogonal_selection(pure_selection, value, fields=fields) else: - self.set_basic_selection(cast(BasicSelection, pure_selection), value, fields=fields) + self.set_basic_selection(cast("BasicSelection", pure_selection), value, fields=fields) - @_deprecate_positional_args def get_basic_selection( self, selection: BasicSelection = Ellipsis, @@ -2665,7 +2662,6 @@ def get_basic_selection( ) ) - @_deprecate_positional_args def set_basic_selection( self, selection: BasicSelection, @@ -2761,7 +2757,6 @@ def set_basic_selection( indexer = BasicIndexer(selection, self.shape, self.metadata.chunk_grid) sync(self._async_array._set_selection(indexer, value, fields=fields, prototype=prototype)) - @_deprecate_positional_args def get_orthogonal_selection( self, selection: OrthogonalSelection, @@ -2886,7 +2881,6 @@ def get_orthogonal_selection( ) ) - @_deprecate_positional_args def set_orthogonal_selection( self, selection: OrthogonalSelection, @@ -2997,7 +2991,6 @@ def set_orthogonal_selection( self._async_array._set_selection(indexer, value, fields=fields, prototype=prototype) ) - @_deprecate_positional_args def get_mask_selection( self, mask: MaskSelection, @@ -3080,7 +3073,6 @@ def get_mask_selection( ) ) - @_deprecate_positional_args def set_mask_selection( self, mask: MaskSelection, @@ -3159,7 +3151,6 @@ def set_mask_selection( indexer = MaskIndexer(mask, self.shape, self.metadata.chunk_grid) sync(self._async_array._set_selection(indexer, value, fields=fields, prototype=prototype)) - @_deprecate_positional_args def get_coordinate_selection( self, selection: CoordinateSelection, @@ -3249,7 +3240,6 @@ def get_coordinate_selection( out_array = np.array(out_array).reshape(indexer.sel_shape) return out_array - @_deprecate_positional_args def set_coordinate_selection( self, selection: CoordinateSelection, @@ -3347,7 +3337,6 @@ def set_coordinate_selection( sync(self._async_array._set_selection(indexer, value, fields=fields, prototype=prototype)) - @_deprecate_positional_args def get_block_selection( self, selection: BasicSelection, @@ -3446,7 +3435,6 @@ def get_block_selection( ) ) - @_deprecate_positional_args def set_block_selection( self, selection: BasicSelection, @@ -3657,7 +3645,7 @@ def update_attributes(self, new_attributes: dict[str, JSON]) -> Array: # TODO: remove this cast when type inference improves new_array = sync(self._async_array.update_attributes(new_attributes)) # TODO: remove this cast when type inference improves - _new_array = cast(AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata], new_array) + _new_array = cast("AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]", new_array) return type(self)(_new_array) def __repr__(self) -> str: @@ -4252,7 +4240,7 @@ async def init_array( serializer=serializer, dtype=dtype_parsed, ) - sub_codecs = cast(tuple[Codec, ...], (*array_array, array_bytes, *bytes_bytes)) + sub_codecs = cast("tuple[Codec, ...]", (*array_array, array_bytes, *bytes_bytes)) codecs_out: tuple[Codec, ...] if shard_shape_parsed is not None: index_location = None @@ -4523,7 +4511,7 @@ def _parse_keep_array_attr( compressors = "auto" if serializer == "keep": if zarr_format == 3 and data.metadata.zarr_format == 3: - serializer = cast(SerializerLike, data.serializer) + serializer = cast("SerializerLike", data.serializer) else: serializer = "auto" if fill_value is None: @@ -4701,7 +4689,7 @@ def _parse_chunk_encoding_v3( if isinstance(filters, dict | Codec): maybe_array_array = (filters,) else: - maybe_array_array = cast(Iterable[Codec | dict[str, JSON]], filters) + maybe_array_array = cast("Iterable[Codec | dict[str, JSON]]", filters) out_array_array = tuple(_parse_array_array_codec(c) for c in maybe_array_array) if serializer == "auto": @@ -4718,7 +4706,7 @@ def _parse_chunk_encoding_v3( if isinstance(compressors, dict | Codec): maybe_bytes_bytes = (compressors,) else: - maybe_bytes_bytes = cast(Iterable[Codec | dict[str, JSON]], compressors) + maybe_bytes_bytes = cast("Iterable[Codec | dict[str, JSON]]", compressors) out_bytes_bytes = tuple(_parse_bytes_bytes_codec(c) for c in maybe_bytes_bytes) diff --git a/src/zarr/core/array_spec.py b/src/zarr/core/array_spec.py index 59d3cc6b40..6cd27b30eb 100644 --- a/src/zarr/core/array_spec.py +++ b/src/zarr/core/array_spec.py @@ -64,7 +64,7 @@ def from_dict(cls, data: ArrayConfigParams) -> Self: """ kwargs_out: ArrayConfigParams = {} for f in fields(ArrayConfig): - field_name = cast(Literal["order", "write_empty_chunks"], f.name) + field_name = cast("Literal['order', 'write_empty_chunks']", f.name) if field_name not in data: kwargs_out[field_name] = zarr_config.get(f"array.{field_name}") else: diff --git a/src/zarr/core/buffer/core.py b/src/zarr/core/buffer/core.py index 94cd91f026..d0a2d992d2 100644 --- a/src/zarr/core/buffer/core.py +++ b/src/zarr/core/buffer/core.py @@ -159,7 +159,7 @@ def create_zero_length(cls) -> Self: if cls is Buffer: raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'") return cls( - cast(ArrayLike, None) + cast("ArrayLike", None) ) # This line will never be reached, but it satisfies the type checker @classmethod @@ -207,7 +207,7 @@ def from_buffer(cls, buffer: Buffer) -> Self: if cls is Buffer: raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'") return cls( - cast(ArrayLike, None) + cast("ArrayLike", None) ) # This line will never be reached, but it satisfies the type checker @classmethod @@ -227,7 +227,7 @@ def from_bytes(cls, bytes_like: BytesLike) -> Self: if cls is Buffer: raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'") return cls( - cast(ArrayLike, None) + cast("ArrayLike", None) ) # This line will never be reached, but it satisfies the type checker def as_array_like(self) -> ArrayLike: @@ -371,7 +371,7 @@ def create( "Cannot call abstract method on the abstract class 'NDBuffer'" ) return cls( - cast(NDArrayLike, None) + cast("NDArrayLike", None) ) # This line will never be reached, but it satisfies the type checker @classmethod @@ -408,7 +408,7 @@ def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self: "Cannot call abstract method on the abstract class 'NDBuffer'" ) return cls( - cast(NDArrayLike, None) + cast("NDArrayLike", None) ) # This line will never be reached, but it satisfies the type checker def as_ndarray_like(self) -> NDArrayLike: @@ -440,7 +440,7 @@ def as_scalar(self) -> ScalarType: """Returns the buffer as a scalar value""" if self._data.size != 1: raise ValueError("Buffer does not contain a single scalar value") - return cast(ScalarType, self.as_numpy_array()[()]) + return cast("ScalarType", self.as_numpy_array()[()]) @property def dtype(self) -> np.dtype[Any]: diff --git a/src/zarr/core/buffer/gpu.py b/src/zarr/core/buffer/gpu.py index 77d2731c71..88746c5fac 100644 --- a/src/zarr/core/buffer/gpu.py +++ b/src/zarr/core/buffer/gpu.py @@ -103,7 +103,7 @@ def from_bytes(cls, bytes_like: BytesLike) -> Self: return cls.from_array_like(cp.frombuffer(bytes_like, dtype="B")) def as_numpy_array(self) -> npt.NDArray[Any]: - return cast(npt.NDArray[Any], cp.asnumpy(self._data)) + return cast("npt.NDArray[Any]", cp.asnumpy(self._data)) def __add__(self, other: core.Buffer) -> Self: other_array = other.as_array_like() @@ -204,7 +204,7 @@ def as_numpy_array(self) -> npt.NDArray[Any]: ------- NumPy array of this buffer (might be a data copy) """ - return cast(npt.NDArray[Any], cp.asnumpy(self._data)) + return cast("npt.NDArray[Any]", cp.asnumpy(self._data)) def __getitem__(self, key: Any) -> Self: return self.__class__(self._data.__getitem__(key)) diff --git a/src/zarr/core/chunk_key_encodings.py b/src/zarr/core/chunk_key_encodings.py index 103472c3b4..91dfc90365 100644 --- a/src/zarr/core/chunk_key_encodings.py +++ b/src/zarr/core/chunk_key_encodings.py @@ -20,7 +20,7 @@ def parse_separator(data: JSON) -> SeparatorLiteral: if data not in (".", "/"): raise ValueError(f"Expected an '.' or '/' separator. Got {data} instead.") - return cast(SeparatorLiteral, data) + return cast("SeparatorLiteral", data) class ChunkKeyEncodingParams(TypedDict): @@ -48,7 +48,7 @@ def from_dict(cls, data: dict[str, JSON] | ChunkKeyEncodingLike) -> ChunkKeyEnco data = {"name": data["name"], "configuration": {"separator": data["separator"]}} # TODO: remove this cast when we are statically typing the JSON metadata completely. - data = cast(dict[str, JSON], data) + data = cast("dict[str, JSON]", data) # configuration is optional for chunk key encodings name_parsed, config_parsed = parse_named_configuration(data, require_configuration=False) diff --git a/src/zarr/core/common.py b/src/zarr/core/common.py index a670834206..be37dc5109 100644 --- a/src/zarr/core/common.py +++ b/src/zarr/core/common.py @@ -158,7 +158,7 @@ def parse_fill_value(data: Any) -> Any: def parse_order(data: Any) -> Literal["C", "F"]: if data in ("C", "F"): - return cast(Literal["C", "F"], data) + return cast("Literal['C', 'F']", data) raise ValueError(f"Expected one of ('C', 'F'), got {data} instead.") @@ -202,4 +202,4 @@ def _warn_order_kwarg() -> None: def _default_zarr_format() -> ZarrFormat: """Return the default zarr_version""" - return cast(ZarrFormat, int(zarr_config.get("default_zarr_format", 3))) + return cast("ZarrFormat", int(zarr_config.get("default_zarr_format", 3))) diff --git a/src/zarr/core/config.py b/src/zarr/core/config.py index c565cb0708..2a10943d80 100644 --- a/src/zarr/core/config.py +++ b/src/zarr/core/config.py @@ -134,6 +134,6 @@ def enable_gpu(self) -> ConfigSet: def parse_indexing_order(data: Any) -> Literal["C", "F"]: if data in ("C", "F"): - return cast(Literal["C", "F"], data) + return cast("Literal['C', 'F']", data) msg = f"Expected one of ('C', 'F'), got {data} instead." raise ValueError(msg) diff --git a/src/zarr/core/group.py b/src/zarr/core/group.py index 5c470e29ca..39278edfb5 100644 --- a/src/zarr/core/group.py +++ b/src/zarr/core/group.py @@ -7,7 +7,6 @@ import logging import warnings from collections import defaultdict -from collections.abc import Iterator, Mapping from dataclasses import asdict, dataclass, field, fields, replace from itertools import accumulate from typing import TYPE_CHECKING, Literal, TypeVar, assert_never, cast, overload @@ -17,7 +16,6 @@ from typing_extensions import deprecated import zarr.api.asynchronous as async_api -from zarr._compat import _deprecate_positional_args from zarr.abc.metadata import Metadata from zarr.abc.store import Store, set_or_delete from zarr.core._info import GroupInfo @@ -65,6 +63,8 @@ Coroutine, Generator, Iterable, + Iterator, + Mapping, ) from typing import Any @@ -81,7 +81,7 @@ def parse_zarr_format(data: Any) -> ZarrFormat: """Parse the zarr_format field from metadata.""" if data in (2, 3): - return cast(ZarrFormat, data) + return cast("ZarrFormat", data) msg = f"Invalid zarr_format. Expected one of 2 or 3. Got {data}." raise ValueError(msg) @@ -89,7 +89,7 @@ def parse_zarr_format(data: Any) -> ZarrFormat: def parse_node_type(data: Any) -> NodeType: """Parse the node_type field from metadata.""" if data in ("array", "group"): - return cast(Literal["array", "group"], data) + return cast("Literal['array', 'group']", data) raise MetadataValidationError("node_type", "array or group", data) @@ -362,7 +362,7 @@ def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]: # it's an array if isinstance(v.get("fill_value", None), np.void): v["fill_value"] = base64.standard_b64encode( - cast(bytes, v["fill_value"]) + cast("bytes", v["fill_value"]) ).decode("ascii") else: v = _replace_special_floats(v) @@ -2365,7 +2365,6 @@ def create(self, *args: Any, **kwargs: Any) -> Array: # Backwards compatibility for 2.x return self.create_array(*args, **kwargs) - @_deprecate_positional_args def create_array( self, name: str, @@ -2571,7 +2570,6 @@ def require_array(self, name: str, *, shape: ShapeLike, **kwargs: Any) -> Array: """ return Array(self._sync(self._async_group.require_array(name, shape=shape, **kwargs))) - @_deprecate_positional_args def empty(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """Create an empty array with the specified shape in this Group. The contents will be filled with the array's fill value or zeros if no fill value is provided. @@ -2593,7 +2591,6 @@ def empty(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """ return Array(self._sync(self._async_group.empty(name=name, shape=shape, **kwargs))) - @_deprecate_positional_args def zeros(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """Create an array, with zero being used as the default value for uninitialized portions of the array. @@ -2613,7 +2610,6 @@ def zeros(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """ return Array(self._sync(self._async_group.zeros(name=name, shape=shape, **kwargs))) - @_deprecate_positional_args def ones(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """Create an array, with one being used as the default value for uninitialized portions of the array. @@ -2633,7 +2629,6 @@ def ones(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array: """ return Array(self._sync(self._async_group.ones(name=name, shape=shape, **kwargs))) - @_deprecate_positional_args def full( self, *, name: str, shape: ChunkCoords, fill_value: Any | None, **kwargs: Any ) -> Array: @@ -2661,7 +2656,6 @@ def full( ) ) - @_deprecate_positional_args def empty_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array: """Create an empty sub-array like `data`. The contents will be filled with the array's fill value or zeros if no fill value is provided. @@ -2688,7 +2682,6 @@ def empty_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> """ return Array(self._sync(self._async_group.empty_like(name=name, data=data, **kwargs))) - @_deprecate_positional_args def zeros_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array: """Create a sub-array of zeros like `data`. @@ -2709,7 +2702,6 @@ def zeros_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> return Array(self._sync(self._async_group.zeros_like(name=name, data=data, **kwargs))) - @_deprecate_positional_args def ones_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array: """Create a sub-array of ones like `data`. @@ -2729,7 +2721,6 @@ def ones_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> A """ return Array(self._sync(self._async_group.ones_like(name=name, data=data, **kwargs))) - @_deprecate_positional_args def full_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array: """Create a sub-array like `data` filled with the `fill_value` of `data` . @@ -2759,7 +2750,6 @@ def move(self, source: str, dest: str) -> None: return self._sync(self._async_group.move(source, dest)) @deprecated("Use Group.create_array instead.") - @_deprecate_positional_args def array( self, name: str, @@ -3246,8 +3236,7 @@ def _ensure_consistent_zarr_format( raise ValueError(msg) return cast( - Mapping[str, GroupMetadata | ArrayV2Metadata] - | Mapping[str, GroupMetadata | ArrayV3Metadata], + "Mapping[str, GroupMetadata | ArrayV2Metadata] | Mapping[str, GroupMetadata | ArrayV3Metadata]", data, ) diff --git a/src/zarr/core/indexing.py b/src/zarr/core/indexing.py index 998fe156a1..c11889f7f4 100644 --- a/src/zarr/core/indexing.py +++ b/src/zarr/core/indexing.py @@ -466,7 +466,7 @@ def replace_ellipsis(selection: Any, shape: ChunkCoords) -> SelectionNormalized: # check selection not too long check_selection_length(selection, shape) - return cast(SelectionNormalized, selection) + return cast("SelectionNormalized", selection) def replace_lists(selection: SelectionNormalized) -> SelectionNormalized: @@ -481,7 +481,7 @@ def replace_lists(selection: SelectionNormalized) -> SelectionNormalized: def ensure_tuple(v: Any) -> SelectionNormalized: if not isinstance(v, tuple): v = (v,) - return cast(SelectionNormalized, v) + return cast("SelectionNormalized", v) class ChunkProjection(NamedTuple): @@ -818,7 +818,7 @@ def ix_(selection: Any, shape: ChunkCoords) -> npt.NDArray[np.intp]: # now get numpy to convert to a coordinate selection selection = np.ix_(*selection) - return cast(npt.NDArray[np.intp], selection) + return cast("npt.NDArray[np.intp]", selection) def oindex(a: npt.NDArray[Any], selection: Selection) -> npt.NDArray[Any]: @@ -948,7 +948,7 @@ def __getitem__(self, selection: OrthogonalSelection | Array) -> NDArrayLikeOrSc new_selection = ensure_tuple(new_selection) new_selection = replace_lists(new_selection) return self.array.get_orthogonal_selection( - cast(OrthogonalSelection, new_selection), fields=fields + cast("OrthogonalSelection", new_selection), fields=fields ) def __setitem__(self, selection: OrthogonalSelection, value: npt.ArrayLike) -> None: @@ -956,7 +956,7 @@ def __setitem__(self, selection: OrthogonalSelection, value: npt.ArrayLike) -> N new_selection = ensure_tuple(new_selection) new_selection = replace_lists(new_selection) return self.array.set_orthogonal_selection( - cast(OrthogonalSelection, new_selection), value, fields=fields + cast("OrthogonalSelection", new_selection), value, fields=fields ) @@ -1050,14 +1050,14 @@ def __getitem__(self, selection: BasicSelection) -> NDArrayLikeOrScalar: fields, new_selection = pop_fields(selection) new_selection = ensure_tuple(new_selection) new_selection = replace_lists(new_selection) - return self.array.get_block_selection(cast(BasicSelection, new_selection), fields=fields) + return self.array.get_block_selection(cast("BasicSelection", new_selection), fields=fields) def __setitem__(self, selection: BasicSelection, value: npt.ArrayLike) -> None: fields, new_selection = pop_fields(selection) new_selection = ensure_tuple(new_selection) new_selection = replace_lists(new_selection) return self.array.set_block_selection( - cast(BasicSelection, new_selection), value, fields=fields + cast("BasicSelection", new_selection), value, fields=fields ) @@ -1105,12 +1105,12 @@ def __init__( nchunks = reduce(operator.mul, cdata_shape, 1) # some initial normalization - selection_normalized = cast(CoordinateSelectionNormalized, ensure_tuple(selection)) + selection_normalized = cast("CoordinateSelectionNormalized", ensure_tuple(selection)) selection_normalized = tuple( np.asarray([i]) if is_integer(i) else i for i in selection_normalized ) selection_normalized = cast( - CoordinateSelectionNormalized, replace_lists(selection_normalized) + "CoordinateSelectionNormalized", replace_lists(selection_normalized) ) # validation @@ -1214,8 +1214,8 @@ def __iter__(self) -> Iterator[ChunkProjection]: class MaskIndexer(CoordinateIndexer): def __init__(self, selection: MaskSelection, shape: ChunkCoords, chunk_grid: ChunkGrid) -> None: # some initial normalization - selection_normalized = cast(tuple[MaskSelection], ensure_tuple(selection)) - selection_normalized = cast(tuple[MaskSelection], replace_lists(selection_normalized)) + selection_normalized = cast("tuple[MaskSelection]", ensure_tuple(selection)) + selection_normalized = cast("tuple[MaskSelection]", replace_lists(selection_normalized)) # validation if not is_mask_selection(selection_normalized, shape): @@ -1311,14 +1311,14 @@ def pop_fields(selection: SelectionWithFields) -> tuple[Fields | None, Selection elif not isinstance(selection, tuple): # single selection item, no fields # leave selection as-is - return None, cast(Selection, selection) + return None, cast("Selection", selection) else: # multiple items, split fields from selection items fields: Fields = [f for f in selection if isinstance(f, str)] fields = fields[0] if len(fields) == 1 else fields selection_tuple = tuple(s for s in selection if not isinstance(s, str)) selection = cast( - Selection, selection_tuple[0] if len(selection_tuple) == 1 else selection_tuple + "Selection", selection_tuple[0] if len(selection_tuple) == 1 else selection_tuple ) return fields, selection @@ -1380,12 +1380,12 @@ def get_indexer( new_selection = ensure_tuple(selection) new_selection = replace_lists(new_selection) if is_coordinate_selection(new_selection, shape): - return CoordinateIndexer(cast(CoordinateSelection, selection), shape, chunk_grid) + return CoordinateIndexer(cast("CoordinateSelection", selection), shape, chunk_grid) elif is_mask_selection(new_selection, shape): - return MaskIndexer(cast(MaskSelection, selection), shape, chunk_grid) + return MaskIndexer(cast("MaskSelection", selection), shape, chunk_grid) else: raise VindexInvalidSelectionError(new_selection) elif is_pure_orthogonal_indexing(pure_selection, len(shape)): - return OrthogonalIndexer(cast(OrthogonalSelection, selection), shape, chunk_grid) + return OrthogonalIndexer(cast("OrthogonalSelection", selection), shape, chunk_grid) else: - return BasicIndexer(cast(BasicSelection, selection), shape, chunk_grid) + return BasicIndexer(cast("BasicSelection", selection), shape, chunk_grid) diff --git a/src/zarr/core/metadata/v2.py b/src/zarr/core/metadata/v2.py index 029a3e09a7..a8f4f4abb4 100644 --- a/src/zarr/core/metadata/v2.py +++ b/src/zarr/core/metadata/v2.py @@ -378,7 +378,7 @@ def _serialize_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> JSON: # There's a relationship between dtype and fill_value # that mypy isn't aware of. The fact that we have S or V dtype here # means we should have a bytes-type fill_value. - serialized = base64.standard_b64encode(cast(bytes, fill_value)).decode("ascii") + serialized = base64.standard_b64encode(cast("bytes", fill_value)).decode("ascii") elif isinstance(fill_value, np.datetime64): serialized = np.datetime_as_string(fill_value) elif isinstance(fill_value, numbers.Integral): @@ -448,7 +448,7 @@ def _default_compressor( else: raise ValueError(f"Unsupported dtype kind {dtype.kind}") - return cast(dict[str, JSON] | None, default_compressor.get(dtype_key, None)) + return cast("dict[str, JSON] | None", default_compressor.get(dtype_key, None)) def _default_filters( @@ -470,4 +470,4 @@ def _default_filters( else: raise ValueError(f"Unsupported dtype kind {dtype.kind}") - return cast(list[dict[str, JSON]] | None, default_filters.get(dtype_key, None)) + return cast("list[dict[str, JSON]] | None", default_filters.get(dtype_key, None)) diff --git a/src/zarr/core/metadata/v3.py b/src/zarr/core/metadata/v3.py index 63f6515e44..dcbf44f89b 100644 --- a/src/zarr/core/metadata/v3.py +++ b/src/zarr/core/metadata/v3.py @@ -273,7 +273,7 @@ def __init__( fill_value = default_fill_value(data_type_parsed) # we pass a string here rather than an enum to make mypy happy fill_value_parsed = parse_fill_value( - fill_value, dtype=cast(ALL_DTYPES, data_type_parsed.value) + fill_value, dtype=cast("ALL_DTYPES", data_type_parsed.value) ) attributes_parsed = parse_attributes(attributes) codecs_parsed_partial = parse_codecs(codecs) @@ -524,7 +524,7 @@ def parse_fill_value( return np.bytes_(fill_value) # the rest are numeric types - np_dtype = cast(np.dtype[Any], data_type.to_numpy()) + np_dtype = cast("np.dtype[Any]", data_type.to_numpy()) if isinstance(fill_value, Sequence) and not isinstance(fill_value, str): if data_type in (DataType.complex64, DataType.complex128): @@ -588,7 +588,7 @@ def default_fill_value(dtype: DataType) -> str | bytes | np.generic: return b"" else: np_dtype = dtype.to_numpy() - np_dtype = cast(np.dtype[Any], np_dtype) + np_dtype = cast("np.dtype[Any]", np_dtype) return np_dtype.type(0) # type: ignore[misc] diff --git a/src/zarr/core/strings.py b/src/zarr/core/strings.py index ffca0c3b0c..15c5fddfee 100644 --- a/src/zarr/core/strings.py +++ b/src/zarr/core/strings.py @@ -30,7 +30,7 @@ def cast_array( data: np.ndarray[Any, np.dtype[Any]], ) -> np.ndarray[Any, np.dtypes.StringDType | np.dtypes.ObjectDType]: out = data.astype(_STRING_DTYPE, copy=False) - return cast(np.ndarray[Any, np.dtypes.StringDType], out) + return cast("np.ndarray[Any, np.dtypes.StringDType]", out) except AttributeError: # if not available, we fall back on an object array of strings, as in Zarr < 3 @@ -41,7 +41,7 @@ def cast_array( data: np.ndarray[Any, np.dtype[Any]], ) -> np.ndarray[Any, Union["np.dtypes.StringDType", "np.dtypes.ObjectDType"]]: out = data.astype(_STRING_DTYPE, copy=False) - return cast(np.ndarray[Any, np.dtypes.ObjectDType], out) + return cast("np.ndarray[Any, np.dtypes.ObjectDType]", out) def cast_to_string_dtype( diff --git a/src/zarr/testing/utils.py b/src/zarr/testing/utils.py index 7cf57ab9d6..afc15d742c 100644 --- a/src/zarr/testing/utils.py +++ b/src/zarr/testing/utils.py @@ -30,7 +30,7 @@ def has_cupy() -> bool: try: import cupy - return cast(bool, cupy.cuda.runtime.getDeviceCount() > 0) + return cast("bool", cupy.cuda.runtime.getDeviceCount() > 0) except ImportError: return False except cupy.cuda.runtime.CUDARuntimeError: diff --git a/tests/test_api.py b/tests/test_api.py index 640478e9c1..4efb47fccd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -13,7 +13,6 @@ from zarr.core.common import JSON, MemoryOrder, ZarrFormat import contextlib -import warnings from typing import Literal import numpy as np @@ -259,7 +258,7 @@ def test_save_errors() -> None: save_group("data/group.zarr") with pytest.raises(TypeError): # no array provided - save_array("data/group.zarr") + save_array("data/group.zarr") # type: ignore[call-arg] with pytest.raises(ValueError): # no arrays provided save("data/group.zarr") @@ -1080,40 +1079,6 @@ def test_tree() -> None: # copy(source["foo"], dest, dry_run=True, log=True) -def test_open_positional_args_deprecated() -> None: - store = MemoryStore() - with pytest.warns(FutureWarning, match="pass"): - zarr.api.synchronous.open(store, "w", shape=(1,)) - - -def test_save_array_positional_args_deprecated() -> None: - store = MemoryStore() - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message="zarr_version is deprecated", category=DeprecationWarning - ) - with pytest.warns(FutureWarning, match="pass"): - save_array( - store, - np.ones( - 1, - ), - 3, - ) - - -def test_group_positional_args_deprecated() -> None: - store = MemoryStore() - with pytest.warns(FutureWarning, match="pass"): - group(store, True) - - -def test_open_group_positional_args_deprecated() -> None: - store = MemoryStore() - with pytest.warns(FutureWarning, match="pass"): - open_group(store, "w") - - def test_open_falls_back_to_open_group() -> None: # https://github.com/zarr-developers/zarr-python/issues/2309 store = MemoryStore() @@ -1144,9 +1109,9 @@ def test_open_modes_creates_group(tmp_path: pathlib.Path, mode: str) -> None: if mode in ["r", "r+"]: # Expect FileNotFoundError to be raised if 'r' or 'r+' mode with pytest.raises(FileNotFoundError): - zarr.open(store=zarr_dir, mode=mode) + zarr.open(store=zarr_dir, mode=mode) # type: ignore[arg-type] else: - group = zarr.open(store=zarr_dir, mode=mode) + group = zarr.open(store=zarr_dir, mode=mode) # type: ignore[arg-type] assert isinstance(group, Group) diff --git a/tests/test_array.py b/tests/test_array.py index 32f7887007..69654a6471 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -39,7 +39,6 @@ create_array, ) from zarr.core.buffer import NDArrayLike, NDArrayLikeOrScalar, default_buffer_prototype -from zarr.core.buffer.cpu import NDBuffer from zarr.core.chunk_grids import _auto_partition from zarr.core.chunk_key_encodings import ChunkKeyEncodingParams from zarr.core.common import JSON, MemoryOrder, ZarrFormat @@ -228,50 +227,6 @@ def test_array_v3_fill_value(store: MemoryStore, fill_value: int, dtype_str: str assert arr.fill_value.dtype == arr.dtype -async def test_create_deprecated() -> None: - with pytest.warns(DeprecationWarning): - with pytest.warns(FutureWarning, match=re.escape("Pass shape=(2, 2) as keyword args")): - await zarr.AsyncArray.create(MemoryStore(), (2, 2), dtype="f8") # type: ignore[call-overload] - with pytest.warns(DeprecationWarning): - with pytest.warns(FutureWarning, match=re.escape("Pass shape=(2, 2) as keyword args")): - zarr.Array.create(MemoryStore(), (2, 2), dtype="f8") - - -def test_selection_positional_args_deprecated() -> None: - store = MemoryStore() - arr = zarr.create_array(store, shape=(2, 2), dtype="f8") - - with pytest.warns(FutureWarning, match="Pass out"): - arr.get_basic_selection(..., NDBuffer(array=np.empty((2, 2)))) - - with pytest.warns(FutureWarning, match="Pass fields"): - arr.set_basic_selection(..., 1, None) - - with pytest.warns(FutureWarning, match="Pass out"): - arr.get_orthogonal_selection(..., NDBuffer(array=np.empty((2, 2)))) - - with pytest.warns(FutureWarning, match="Pass"): - arr.set_orthogonal_selection(..., 1, None) - - with pytest.warns(FutureWarning, match="Pass"): - arr.get_mask_selection(np.zeros((2, 2), dtype=bool), NDBuffer(array=np.empty((0,)))) - - with pytest.warns(FutureWarning, match="Pass"): - arr.set_mask_selection(np.zeros((2, 2), dtype=bool), 1, None) - - with pytest.warns(FutureWarning, match="Pass"): - arr.get_coordinate_selection(([0, 1], [0, 1]), NDBuffer(array=np.empty((2,)))) - - with pytest.warns(FutureWarning, match="Pass"): - arr.set_coordinate_selection(([0, 1], [0, 1]), 1, None) - - with pytest.warns(FutureWarning, match="Pass"): - arr.get_block_selection((0, slice(None)), NDBuffer(array=np.empty((2, 2)))) - - with pytest.warns(FutureWarning, match="Pass"): - arr.set_block_selection((0, slice(None)), 1, None) - - @pytest.mark.parametrize("store", ["memory"], indirect=True) async def test_array_v3_nan_fill_value(store: MemoryStore) -> None: shape = (10,) @@ -458,47 +413,44 @@ async def test_nbytes_stored_async() -> None: assert result == 902 # the size with all chunks filled. -def test_default_fill_values() -> None: - a = zarr.Array.create(MemoryStore(), shape=5, chunk_shape=5, dtype=" None: + a = zarr.Array.create(store=MemoryStore(), shape=(5,), chunk_shape=(5,), dtype=dtype_str) + assert a.fill_value == fill_value_expected def test_vlen_errors() -> None: + shape = (5,) + chunks = (5,) + dtype = " None: @@ -522,6 +474,7 @@ def test_info_v2(self, chunks: tuple[int, int], shards: tuple[int, int] | None) expected = ArrayInfo( _zarr_format=2, _data_type=np.dtype("float64"), + _fill_value=arr.fill_value, _shape=(8, 8), _chunk_shape=chunks, _shard_shape=None, @@ -539,6 +492,7 @@ def test_info_v3(self, chunks: tuple[int, int], shards: tuple[int, int] | None) expected = ArrayInfo( _zarr_format=3, _data_type=DataType.parse("float64"), + _fill_value=arr.fill_value, _shape=(8, 8), _chunk_shape=chunks, _shard_shape=shards, @@ -564,6 +518,7 @@ def test_info_complete(self, chunks: tuple[int, int], shards: tuple[int, int] | expected = ArrayInfo( _zarr_format=3, _data_type=DataType.parse("float64"), + _fill_value=arr.fill_value, _shape=(8, 8), _chunk_shape=chunks, _shard_shape=shards, @@ -599,6 +554,7 @@ async def test_info_v2_async( expected = ArrayInfo( _zarr_format=2, _data_type=np.dtype("float64"), + _fill_value=arr.metadata.fill_value, _shape=(8, 8), _chunk_shape=(2, 2), _shard_shape=None, @@ -624,6 +580,7 @@ async def test_info_v3_async( expected = ArrayInfo( _zarr_format=3, _data_type=DataType.parse("float64"), + _fill_value=arr.metadata.fill_value, _shape=(8, 8), _chunk_shape=chunks, _shard_shape=shards, @@ -651,6 +608,7 @@ async def test_info_complete_async( expected = ArrayInfo( _zarr_format=3, _data_type=DataType.parse("float64"), + _fill_value=arr.metadata.fill_value, _shape=(8, 8), _chunk_shape=chunks, _shard_shape=shards, diff --git a/tests/test_codecs/test_codecs.py b/tests/test_codecs/test_codecs.py index b8122b4ac2..468f395254 100644 --- a/tests/test_codecs/test_codecs.py +++ b/tests/test_codecs/test_codecs.py @@ -2,7 +2,7 @@ import json from dataclasses import dataclass -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any import numpy as np import pytest @@ -18,32 +18,33 @@ TransposeCodec, ) from zarr.core.buffer import default_buffer_prototype -from zarr.core.indexing import Selection, morton_order_iter +from zarr.core.indexing import BasicSelection, morton_order_iter +from zarr.core.metadata.v3 import ArrayV3Metadata from zarr.storage import StorePath if TYPE_CHECKING: from zarr.abc.store import Store - from zarr.core.buffer import NDArrayLike - from zarr.core.common import MemoryOrder + from zarr.core.buffer.core import NDArrayLikeOrScalar + from zarr.core.common import ChunkCoords, MemoryOrder @dataclass(frozen=True) class _AsyncArrayProxy: - array: AsyncArray + array: AsyncArray[Any] - def __getitem__(self, selection: Selection) -> _AsyncArraySelectionProxy: + def __getitem__(self, selection: BasicSelection) -> _AsyncArraySelectionProxy: return _AsyncArraySelectionProxy(self.array, selection) @dataclass(frozen=True) class _AsyncArraySelectionProxy: - array: AsyncArray - selection: Selection + array: AsyncArray[Any] + selection: BasicSelection - async def get(self) -> NDArrayLike: + async def get(self) -> NDArrayLikeOrScalar: return await self.array.getitem(self.selection) - async def set(self, value: np.ndarray) -> None: + async def set(self, value: np.ndarray[Any, Any]) -> None: return await self.array.setitem(self.selection, value) @@ -101,6 +102,7 @@ async def test_order( read_data = await _AsyncArrayProxy(a)[:, :].get() assert np.array_equal(data, read_data) + assert isinstance(read_data, np.ndarray) if runtime_read_order == "F": assert read_data.flags["F_CONTIGUOUS"] assert not read_data.flags["C_CONTIGUOUS"] @@ -142,6 +144,7 @@ def test_order_implicit( read_data = a[:, :] assert np.array_equal(data, read_data) + assert isinstance(read_data, np.ndarray) if runtime_read_order == "F": assert read_data.flags["F_CONTIGUOUS"] assert not read_data.flags["C_CONTIGUOUS"] @@ -209,7 +212,7 @@ def test_morton() -> None: [3, 2, 1, 6, 4, 5, 2], ], ) -def test_morton2(shape) -> None: +def test_morton2(shape: ChunkCoords) -> None: order = list(morton_order_iter(shape)) for i, x in enumerate(order): assert x not in order[:i] # no duplicates @@ -263,7 +266,10 @@ async def test_dimension_names(store: Store) -> None: dimension_names=("x", "y"), ) - assert (await zarr.api.asynchronous.open_array(store=spath)).metadata.dimension_names == ( + assert isinstance( + meta := (await zarr.api.asynchronous.open_array(store=spath)).metadata, ArrayV3Metadata + ) + assert meta.dimension_names == ( "x", "y", ) @@ -277,7 +283,8 @@ async def test_dimension_names(store: Store) -> None: fill_value=0, ) - assert (await AsyncArray.open(spath2)).metadata.dimension_names is None + assert isinstance(meta := (await AsyncArray.open(spath2)).metadata, ArrayV3Metadata) + assert meta.dimension_names is None zarr_json_buffer = await store.get(f"{path2}/zarr.json", prototype=default_buffer_prototype()) assert zarr_json_buffer is not None assert "dimension_names" not in json.loads(zarr_json_buffer.to_bytes()) diff --git a/tests/test_group.py b/tests/test_group.py index b4dace2568..1c281ef2d4 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -1444,26 +1444,6 @@ def test_update_attrs() -> None: assert root.attrs["foo"] == "bar" -@pytest.mark.parametrize("method", ["empty", "zeros", "ones", "full"]) -def test_group_deprecated_positional_args(method: str) -> None: - if method == "full": - kwargs = {"fill_value": 0} - else: - kwargs = {} - - root = zarr.group() - with pytest.warns(FutureWarning, match=r"Pass name=.* as keyword args."): - arr = getattr(root, method)("foo", shape=1, **kwargs) - assert arr.shape == (1,) - - method += "_like" - data = np.ones(1) - - with pytest.warns(FutureWarning, match=r"Pass name=.*, data=.* as keyword args."): - arr = getattr(root, method)("foo_like", data, **kwargs) - assert arr.shape == data.shape - - @pytest.mark.parametrize("store", ["local", "memory"], indirect=["store"]) def test_delitem_removes_children(store: Store, zarr_format: ZarrFormat) -> None: # https://github.com/zarr-developers/zarr-python/issues/2191 diff --git a/tests/test_info.py b/tests/test_info.py index db0fd0ef76..04e6339092 100644 --- a/tests/test_info.py +++ b/tests/test_info.py @@ -54,6 +54,7 @@ def test_array_info(zarr_format: ZarrFormat) -> None: info = ArrayInfo( _zarr_format=zarr_format, _data_type=np.dtype("int32"), + _fill_value=0, _shape=(100, 100), _chunk_shape=(10, 100), _order="C", @@ -66,6 +67,7 @@ def test_array_info(zarr_format: ZarrFormat) -> None: Type : Array Zarr format : {zarr_format} Data type : int32 + Fill value : 0 Shape : (100, 100) Chunk shape : (10, 100) Order : C @@ -92,6 +94,7 @@ def test_array_info_complete( info = ArrayInfo( _zarr_format=zarr_format, _data_type=np.dtype("int32"), + _fill_value=0, _shape=(100, 100), _chunk_shape=(10, 100), _order="C", @@ -107,6 +110,7 @@ def test_array_info_complete( Type : Array Zarr format : {zarr_format} Data type : int32 + Fill value : 0 Shape : (100, 100) Chunk shape : (10, 100) Order : C diff --git a/tests/test_sync.py b/tests/test_sync.py index 13b475f8da..c5eadb0f4f 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -15,7 +15,6 @@ loop, sync, ) -from zarr.storage import MemoryStore @pytest.fixture(params=[True, False]) @@ -143,12 +142,6 @@ def bar(self) -> list[int]: assert foo.bar() == list(range(10)) -def test_open_positional_args_deprecate(): - store = MemoryStore() - with pytest.warns(FutureWarning, match="pass"): - zarr.open(store, "w", shape=(1,)) - - @pytest.mark.parametrize("workers", [None, 1, 2]) def test_threadpool_executor(clean_state, workers: int | None) -> None: with zarr.config.set({"threading.max_workers": workers}):