From 4cbb17eb1c0e8aaca0fd32fa02de08dcdd1a01ab Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:09:47 +0200 Subject: [PATCH] Apply and enforce more ruff rules (#2053) * Apply ruff/Perflint rule PERF102 PERF102 When using only the keys of a dict use the `keys()` method * Apply ruff/Perflint rule PERF401 PERF401 Use an async list comprehension to create a transformed list * Apply ruff/flake8-pytest-style rule PT022 * Fix pre-commit warning Ignore lint rules conflicting with the ruff formatter * Apply ruff/pygrep-hooks rule PGH003 PGH003 Use specific rule codes when ignoring type issues * Apply ruff/pygrep-hooks rule PGH004 PGH004 Use specific rule codes when using `noqa` * Enforce ruff/pygrep-hooks rules (PGH) * Apply ruff/flake8-comprehensions rule C417 C417 Unnecessary `map` usage (rewrite using a generator expression) * Apply ruff/flake8-pyi rule PYI032 PYI032 Prefer `object` to `Any` for the second parameter to `__eq__` * Apply ruff/flake8-pyi rule PYI036 PYI036 Returning Any from function * Apply ruff/flake8-pyi rule PYI038 * Apply ruff/flake8-pyi rule PYI041 PYI041 Use `complex` instead of `float | complex` PYI041 Use `float` instead of `int | float` * Apply ruff/flake8-pyi rule PYI055 PYI055 Multiple `type` members in a union. Combine them into one. --- pyproject.toml | 28 ++++++++++++++++++---- src/zarr/abc/store.py | 10 ++++++-- src/zarr/api/asynchronous.py | 10 ++++---- src/zarr/codecs/transpose.py | 6 ++--- src/zarr/core/array.py | 7 +++--- src/zarr/core/attributes.py | 2 +- src/zarr/core/buffer/core.py | 6 ++--- src/zarr/core/buffer/cpu.py | 4 ++-- src/zarr/core/buffer/gpu.py | 4 ++-- src/zarr/core/group.py | 12 +++++----- src/zarr/core/indexing.py | 24 +++++++++++-------- src/zarr/core/metadata/v3.py | 12 +++++----- src/zarr/core/sync.py | 4 +--- src/zarr/registry.py | 8 +++---- src/zarr/store/common.py | 13 ++++------ src/zarr/store/local.py | 2 +- src/zarr/store/memory.py | 9 ++++--- src/zarr/store/remote.py | 3 ++- src/zarr/testing/strategies.py | 5 ++-- tests/v3/conftest.py | 7 +++--- tests/v3/test_array.py | 2 +- tests/v3/test_group.py | 12 +++++----- tests/v3/test_indexing.py | 8 +++---- tests/v3/test_metadata/test_v3.py | 2 +- tests/v3/test_properties.py | 9 +++---- tests/v3/test_store/test_remote.py | 5 +--- tests/v3/test_store/test_stateful_store.py | 6 ++--- tests/v3/test_v2.py | 2 +- 28 files changed, 119 insertions(+), 103 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 63a58ac79..ed6c25893 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -207,18 +207,36 @@ extend-exclude = [ [tool.ruff.lint] extend-select = [ - "B", # flake8-bugbear - "I", # isort - "ISC", - "UP", # pyupgrade - "RSE", + "B", # flake8-bugbear + "I", # isort + "ISC", # flake8-implicit-str-concat + "PGH", # pygrep-hooks + "PYI", # flake8-pyi + "RSE", # flake8-raise "RUF", "TCH", # flake8-type-checking "TRY", # tryceratops + "UP", # pyupgrade ] ignore = [ + "PYI013", "RUF005", "TRY003", + # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "W191", + "E111", + "E114", + "E117", + "D206", + "D300", + "Q000", + "Q001", + "Q002", + "Q003", + "COM812", + "COM819", + "ISC001", + "ISC002", ] [tool.mypy] diff --git a/src/zarr/abc/store.py b/src/zarr/abc/store.py index f95ba34ef..2f02ac36a 100644 --- a/src/zarr/abc/store.py +++ b/src/zarr/abc/store.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod from asyncio import gather from collections.abc import AsyncGenerator, Iterable +from types import TracebackType from typing import Any, NamedTuple, Protocol, runtime_checkable from typing_extensions import Self @@ -35,7 +36,7 @@ class Store(ABC): _mode: AccessMode _is_open: bool - def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any): + def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any) -> None: self._is_open = False self._mode = AccessMode.from_literal(mode) @@ -49,7 +50,12 @@ def __enter__(self) -> Self: """Enter a context manager that will close the store upon exiting.""" return self - def __exit__(self, *args: Any) -> None: + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: """Close the store.""" self.close() diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 5fbb38c5e..95adcf293 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -2,7 +2,7 @@ import asyncio import warnings -from typing import TYPE_CHECKING, Any, Literal, Union, cast +from typing import TYPE_CHECKING, Any, Literal, cast import numpy as np import numpy.typing as npt @@ -25,6 +25,10 @@ from zarr.core.buffer import NDArrayLike from zarr.core.chunk_key_encodings import ChunkKeyEncoding + # TODO: this type could use some more thought + ArrayLike = AsyncArray | Array | npt.NDArray[Any] + PathLike = str + __all__ = [ "consolidate_metadata", "copy", @@ -53,10 +57,6 @@ "zeros_like", ] -# TODO: this type could use some more thought, noqa to avoid "Variable "asynchronous.ArrayLike" is not valid as a type" -ArrayLike = Union[AsyncArray | Array | npt.NDArray[Any]] # noqa -PathLike = str - def _get_shape_chunks(a: ArrayLike | Any) -> tuple[ChunkCoords | None, ChunkCoords | None]: """helper function to get the shape and chunks from an array-like object""" diff --git a/src/zarr/codecs/transpose.py b/src/zarr/codecs/transpose.py index 45eb5bbe5..40a4cdbf3 100644 --- a/src/zarr/codecs/transpose.py +++ b/src/zarr/codecs/transpose.py @@ -96,16 +96,14 @@ async def _decode_single( chunk_spec: ArraySpec, ) -> NDBuffer: inverse_order = np.argsort(self.order) - chunk_array = chunk_array.transpose(inverse_order) - return chunk_array + return chunk_array.transpose(inverse_order) async def _encode_single( self, chunk_array: NDBuffer, _chunk_spec: ArraySpec, ) -> NDBuffer | None: - chunk_array = chunk_array.transpose(self.order) - return chunk_array + return chunk_array.transpose(self.order) def compute_encoded_size(self, input_byte_length: int, _chunk_spec: ArraySpec) -> int: return input_byte_length diff --git a/src/zarr/core/array.py b/src/zarr/core/array.py index b825ca4ca..7d1aa3308 100644 --- a/src/zarr/core/array.py +++ b/src/zarr/core/array.py @@ -110,7 +110,7 @@ def __init__( metadata: ArrayMetadata, store_path: StorePath, order: Literal["C", "F"] | None = None, - ): + ) -> None: metadata_parsed = parse_array_metadata(metadata) order_parsed = parse_indexing_order(order or config.get("array.order")) @@ -294,7 +294,7 @@ async def _create_v2( dtype: npt.DTypeLike, chunks: ChunkCoords, dimension_separator: Literal[".", "/"] | None = None, - fill_value: None | int | float = None, + fill_value: None | float = None, order: Literal["C", "F"] | None = None, filters: list[dict[str, JSON]] | None = None, compressor: dict[str, JSON] | None = None, @@ -331,8 +331,7 @@ def from_dict( data: dict[str, JSON], ) -> AsyncArray: metadata = parse_array_metadata(data) - async_array = cls(metadata=metadata, store_path=store_path) - return async_array + return cls(metadata=metadata, store_path=store_path) @classmethod async def open( diff --git a/src/zarr/core/attributes.py b/src/zarr/core/attributes.py index 913a4b74e..7f9864d1b 100644 --- a/src/zarr/core/attributes.py +++ b/src/zarr/core/attributes.py @@ -13,7 +13,7 @@ class Attributes(MutableMapping[str, JSON]): - def __init__(self, obj: Array | Group): + def __init__(self, obj: Array | Group) -> None: # key=".zattrs", read_only=False, cache=True, synchronizer=None self._obj = obj diff --git a/src/zarr/core/buffer/core.py b/src/zarr/core/buffer/core.py index ba629befa..95c4e00e9 100644 --- a/src/zarr/core/buffer/core.py +++ b/src/zarr/core/buffer/core.py @@ -93,7 +93,7 @@ def ravel(self, order: Literal["K", "A", "C", "F"] = ...) -> Self: ... def all(self) -> bool: ... - def __eq__(self, other: Any) -> Self: # type: ignore[explicit-override, override] + def __eq__(self, other: object) -> Self: # type: ignore[explicit-override, override] """Element-wise equal Notes @@ -136,7 +136,7 @@ class Buffer(ABC): array-like object that must be 1-dim, contiguous, and byte dtype. """ - def __init__(self, array_like: ArrayLike): + def __init__(self, array_like: ArrayLike) -> None: if array_like.ndim != 1: raise ValueError("array_like: only 1-dim allowed") if array_like.dtype != np.dtype("b"): @@ -313,7 +313,7 @@ class NDBuffer: ndarray-like object that is convertible to a regular Numpy array. """ - def __init__(self, array: NDArrayLike): + def __init__(self, array: NDArrayLike) -> None: # assert array.ndim > 0 assert array.dtype != object self._data = array diff --git a/src/zarr/core/buffer/cpu.py b/src/zarr/core/buffer/cpu.py index cef16209e..a82584a47 100644 --- a/src/zarr/core/buffer/cpu.py +++ b/src/zarr/core/buffer/cpu.py @@ -45,7 +45,7 @@ class Buffer(core.Buffer): array-like object that must be 1-dim, contiguous, and byte dtype. """ - def __init__(self, array_like: ArrayLike): + def __init__(self, array_like: ArrayLike) -> None: super().__init__(array_like) @classmethod @@ -143,7 +143,7 @@ class NDBuffer(core.NDBuffer): ndarray-like object that is convertible to a regular Numpy array. """ - def __init__(self, array: NDArrayLike): + def __init__(self, array: NDArrayLike) -> None: super().__init__(array) @classmethod diff --git a/src/zarr/core/buffer/gpu.py b/src/zarr/core/buffer/gpu.py index c817431d3..122717514 100644 --- a/src/zarr/core/buffer/gpu.py +++ b/src/zarr/core/buffer/gpu.py @@ -48,7 +48,7 @@ class Buffer(core.Buffer): array-like object that must be 1-dim, contiguous, and byte dtype. """ - def __init__(self, array_like: ArrayLike): + def __init__(self, array_like: ArrayLike) -> None: if cp is None: raise ImportError( "Cannot use zarr.buffer.gpu.Buffer without cupy. Please install cupy." @@ -137,7 +137,7 @@ class NDBuffer(core.NDBuffer): ndarray-like object that is convertible to a regular Numpy array. """ - def __init__(self, array: NDArrayLike): + def __init__(self, array: NDArrayLike) -> None: if cp is None: raise ImportError( "Cannot use zarr.buffer.gpu.NDBuffer without cupy. Please install cupy." diff --git a/src/zarr/core/group.py b/src/zarr/core/group.py index 7c56707a4..b09968b62 100644 --- a/src/zarr/core/group.py +++ b/src/zarr/core/group.py @@ -54,7 +54,7 @@ def parse_zarr_format(data: Any) -> ZarrFormat: def parse_attributes(data: Any) -> dict[str, Any]: if data is None: return {} - elif isinstance(data, dict) and all(map(lambda v: isinstance(v, str), data.keys())): + elif isinstance(data, dict) and all(isinstance(k, str) for k in data): return data msg = f"Expected dict with string keys. Got {type(data)} instead." raise TypeError(msg) @@ -104,7 +104,9 @@ def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]: ), } - def __init__(self, attributes: dict[str, Any] | None = None, zarr_format: ZarrFormat = 3): + def __init__( + self, attributes: dict[str, Any] | None = None, zarr_format: ZarrFormat = 3 + ) -> None: attributes_parsed = parse_attributes(attributes) zarr_format_parsed = parse_zarr_format(zarr_format) @@ -202,11 +204,10 @@ def from_dict( store_path: StorePath, data: dict[str, Any], ) -> AsyncGroup: - group = cls( + return cls( metadata=GroupMetadata.from_dict(data), store_path=store_path, ) - return group async def getitem( self, @@ -888,8 +889,7 @@ def members(self, max_depth: int | None = 0) -> tuple[tuple[str, Array | Group], """ _members = self._sync_iter(self._async_group.members(max_depth=max_depth)) - result = tuple(map(lambda kv: (kv[0], _parse_async_node(kv[1])), _members)) - return result + return tuple((kv[0], _parse_async_node(kv[1])) for kv in _members) def __contains__(self, member: str) -> bool: return self._sync(self._async_group.contains(member)) diff --git a/src/zarr/core/indexing.py b/src/zarr/core/indexing.py index 0ead269f4..3968a057f 100644 --- a/src/zarr/core/indexing.py +++ b/src/zarr/core/indexing.py @@ -54,7 +54,7 @@ class ArrayIndexError(IndexError): class BoundsCheckError(IndexError): _msg = "" - def __init__(self, dim_len: int): + def __init__(self, dim_len: int) -> None: self._msg = f"index out of bounds for dimension with length {dim_len}" @@ -255,7 +255,7 @@ class IntDimIndexer: dim_chunk_len: int nitems: int = 1 - def __init__(self, dim_sel: int, dim_len: int, dim_chunk_len: int): + def __init__(self, dim_sel: int, dim_len: int, dim_chunk_len: int) -> None: object.__setattr__(self, "dim_sel", normalize_integer_selection(dim_sel, dim_len)) object.__setattr__(self, "dim_len", dim_len) object.__setattr__(self, "dim_chunk_len", dim_chunk_len) @@ -279,7 +279,7 @@ class SliceDimIndexer: stop: int step: int - def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int): + def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int) -> None: # normalize start, stop, step = dim_sel.indices(dim_len) if step < 1: @@ -453,7 +453,7 @@ def __init__( selection: BasicSelection, shape: ChunkCoords, chunk_grid: ChunkGrid, - ): + ) -> None: chunk_shape = get_chunk_shape(chunk_grid) # handle ellipsis selection_normalized = replace_ellipsis(selection, shape) @@ -509,7 +509,7 @@ class BoolArrayDimIndexer: nitems: int dim_chunk_ixs: npt.NDArray[np.intp] - def __init__(self, dim_sel: npt.NDArray[np.bool_], dim_len: int, dim_chunk_len: int): + def __init__(self, dim_sel: npt.NDArray[np.bool_], dim_len: int, dim_chunk_len: int) -> None: # check number of dimensions if not is_bool_array(dim_sel, 1): raise IndexError("Boolean arrays in an orthogonal selection must be 1-dimensional only") @@ -626,7 +626,7 @@ def __init__( wraparound: bool = True, boundscheck: bool = True, order: Order = Order.UNKNOWN, - ): + ) -> None: # ensure 1d array dim_sel = np.asanyarray(dim_sel) if not is_integer_array(dim_sel, 1): @@ -766,7 +766,7 @@ class OrthogonalIndexer(Indexer): is_advanced: bool drop_axes: tuple[int, ...] - def __init__(self, selection: Selection, shape: ChunkCoords, chunk_grid: ChunkGrid): + def __init__(self, selection: Selection, shape: ChunkCoords, chunk_grid: ChunkGrid) -> None: chunk_shape = get_chunk_shape(chunk_grid) # handle ellipsis @@ -880,7 +880,9 @@ class BlockIndexer(Indexer): shape: ChunkCoords drop_axes: ChunkCoords - def __init__(self, selection: BasicSelection, shape: ChunkCoords, chunk_grid: ChunkGrid): + def __init__( + self, selection: BasicSelection, shape: ChunkCoords, chunk_grid: ChunkGrid + ) -> None: chunk_shape = get_chunk_shape(chunk_grid) # handle ellipsis @@ -1005,7 +1007,9 @@ class CoordinateIndexer(Indexer): chunk_shape: ChunkCoords drop_axes: ChunkCoords - def __init__(self, selection: CoordinateSelection, shape: ChunkCoords, chunk_grid: ChunkGrid): + def __init__( + self, selection: CoordinateSelection, shape: ChunkCoords, chunk_grid: ChunkGrid + ) -> None: chunk_shape = get_chunk_shape(chunk_grid) cdata_shape: ChunkCoords @@ -1122,7 +1126,7 @@ def __iter__(self) -> Iterator[ChunkProjection]: @dataclass(frozen=True) class MaskIndexer(CoordinateIndexer): - def __init__(self, selection: MaskSelection, shape: ChunkCoords, chunk_grid: ChunkGrid): + 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)) diff --git a/src/zarr/core/metadata/v3.py b/src/zarr/core/metadata/v3.py index 603cd343a..345655cc0 100644 --- a/src/zarr/core/metadata/v3.py +++ b/src/zarr/core/metadata/v3.py @@ -292,35 +292,35 @@ def update_attributes(self, attributes: dict[str, JSON]) -> Self: @overload def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: BOOL_DTYPE, ) -> BOOL: ... @overload def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: INTEGER_DTYPE, ) -> INTEGER: ... @overload def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: FLOAT_DTYPE, ) -> FLOAT: ... @overload def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: COMPLEX_DTYPE, ) -> COMPLEX: ... @overload def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: np.dtype[Any], ) -> Any: # This dtype[Any] is unfortunately necessary right now. @@ -334,7 +334,7 @@ def parse_fill_value( def parse_fill_value( - fill_value: int | float | complex | str | bytes | np.generic | Sequence[Any] | bool | None, + fill_value: complex | str | bytes | np.generic | Sequence[Any] | bool | None, dtype: BOOL_DTYPE | INTEGER_DTYPE | FLOAT_DTYPE | COMPLEX_DTYPE | np.dtype[Any], ) -> BOOL | INTEGER | FLOAT | COMPLEX | Any: """ diff --git a/src/zarr/core/sync.py b/src/zarr/core/sync.py index db3dce79b..755020ef3 100644 --- a/src/zarr/core/sync.py +++ b/src/zarr/core/sync.py @@ -117,9 +117,7 @@ async def _collect_aiterator(data: AsyncIterator[T]) -> tuple[T, ...]: """ Collect an entire async iterator into a tuple """ - result = [] - async for x in data: - result.append(x) + result = [x async for x in data] return tuple(result) diff --git a/src/zarr/registry.py b/src/zarr/registry.py index cde3b7d84..c6566d12b 100644 --- a/src/zarr/registry.py +++ b/src/zarr/registry.py @@ -107,7 +107,7 @@ def fully_qualified_name(cls: type) -> str: def register_codec(key: str, codec_cls: type[Codec]) -> None: - if key not in __codec_registries.keys(): + if key not in __codec_registries: __codec_registries[key] = Registry() __codec_registries[key].register(codec_cls) @@ -158,7 +158,7 @@ def get_pipeline_class(reload_config: bool = False) -> type[CodecPipeline]: if pipeline_class: return pipeline_class raise BadConfigError( - f"Pipeline class '{path}' not found in registered pipelines: {list(__pipeline_registry.keys())}." + f"Pipeline class '{path}' not found in registered pipelines: {list(__pipeline_registry)}." ) @@ -172,7 +172,7 @@ def get_buffer_class(reload_config: bool = False) -> type[Buffer]: if buffer_class: return buffer_class raise BadConfigError( - f"Buffer class '{path}' not found in registered buffers: {list(__buffer_registry.keys())}." + f"Buffer class '{path}' not found in registered buffers: {list(__buffer_registry)}." ) @@ -185,7 +185,7 @@ def get_ndbuffer_class(reload_config: bool = False) -> type[NDBuffer]: if ndbuffer_class: return ndbuffer_class raise BadConfigError( - f"NDBuffer class '{path}' not found in registered buffers: {list(__ndbuffer_registry.keys())}." + f"NDBuffer class '{path}' not found in registered buffers: {list(__ndbuffer_registry)}." ) diff --git a/src/zarr/store/common.py b/src/zarr/store/common.py index 196479dd6..0c126c63d 100644 --- a/src/zarr/store/common.py +++ b/src/zarr/store/common.py @@ -23,15 +23,14 @@ def _dereference_path(root: str, path: str) -> str: assert isinstance(path, str) root = root.rstrip("/") path = f"{root}/{path}" if root else path - path = path.rstrip("/") - return path + return path.rstrip("/") class StorePath: store: Store path: str - def __init__(self, store: Store, path: str | None = None): + def __init__(self, store: Store, path: str | None = None) -> None: self.store = store self.path = path or "" @@ -64,10 +63,9 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"StorePath({self.store.__class__.__name__}, {str(self)!r})" - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: try: - if self.store == other.store and self.path == other.path: - return True + return self.store == other.store and self.path == other.path # type: ignore[attr-defined, no-any-return] except Exception: pass return False @@ -266,8 +264,7 @@ async def contains_array(store_path: StorePath, zarr_format: ZarrFormat) -> bool except (ValueError, KeyError): return False elif zarr_format == 2: - result = await (store_path / ZARRAY_JSON).exists() - return result + return await (store_path / ZARRAY_JSON).exists() msg = f"Invalid zarr_format provided. Got {zarr_format}, expected 2 or 3" raise ValueError(msg) diff --git a/src/zarr/store/local.py b/src/zarr/store/local.py index fd209cd7c..39a94969e 100644 --- a/src/zarr/store/local.py +++ b/src/zarr/store/local.py @@ -79,7 +79,7 @@ class LocalStore(Store): root: Path - def __init__(self, root: Path | str, *, mode: AccessModeLiteral = "r"): + def __init__(self, root: Path | str, *, mode: AccessModeLiteral = "r") -> None: super().__init__(mode=mode) if isinstance(root, str): root = Path(root) diff --git a/src/zarr/store/memory.py b/src/zarr/store/memory.py index 7baa6aee2..83734e894 100644 --- a/src/zarr/store/memory.py +++ b/src/zarr/store/memory.py @@ -30,7 +30,7 @@ def __init__( store_dict: MutableMapping[str, Buffer] | None = None, *, mode: AccessModeLiteral = "r", - ): + ) -> None: super().__init__(mode=mode) if store_dict is None: store_dict = {} @@ -80,8 +80,7 @@ async def get_partial_values( async def _get(key: str, byte_range: tuple[int, int | None]) -> Buffer | None: return await self.get(key, prototype=prototype, byte_range=byte_range) - vals = await concurrent_map(key_ranges, _get, limit=None) - return vals + return await concurrent_map(key_ranges, _get, limit=None) async def exists(self, key: str) -> bool: return key in self._store_dict @@ -137,7 +136,7 @@ async def list_dir(self, prefix: str) -> AsyncGenerator[str, None]: prefix = prefix[:-1] if prefix == "": - keys_unique = set(k.split("/")[0] for k in self._store_dict.keys()) + keys_unique = set(k.split("/")[0] for k in self._store_dict) else: # Our dictionary doesn't contain directory markers, but we want to include # a pseudo directory when there's a nested item and we're listing an @@ -166,7 +165,7 @@ def __init__( store_dict: MutableMapping[str, Buffer] | None = None, *, mode: AccessModeLiteral = "r", - ): + ) -> None: super().__init__(mode=mode) if store_dict: self._store_dict = {k: gpu.Buffer.from_buffer(store_dict[k]) for k in iter(store_dict)} diff --git a/src/zarr/store/remote.py b/src/zarr/store/remote.py index ecb46a31d..02bda6b1d 100644 --- a/src/zarr/store/remote.py +++ b/src/zarr/store/remote.py @@ -39,7 +39,7 @@ def __init__( mode: AccessModeLiteral = "r", path: str = "/", allowed_exceptions: tuple[type[Exception], ...] = ALLOWED_EXCEPTIONS, - ): + ) -> None: """ Parameters ---------- @@ -49,6 +49,7 @@ def __init__( keys, rather than some other IO failure storage_options: passed on to fsspec to make the filesystem instance. If url is a UPath, this must not be used. + """ super().__init__(mode=mode) self.fs = fs diff --git a/src/zarr/testing/strategies.py b/src/zarr/testing/strategies.py index 3f9d1264d..8a83f82b0 100644 --- a/src/zarr/testing/strategies.py +++ b/src/zarr/testing/strategies.py @@ -4,7 +4,7 @@ import hypothesis.extra.numpy as npst import hypothesis.strategies as st import numpy as np -from hypothesis import given, settings # noqa +from hypothesis import given, settings # noqa: F401 from hypothesis.strategies import SearchStrategy from zarr.core.array import Array @@ -171,5 +171,4 @@ def key_ranges(keys: SearchStrategy = node_names) -> SearchStrategy[list]: st.none() | st.integers(min_value=0), st.none() | st.integers(min_value=0) ) key_tuple = st.tuples(keys, byte_ranges) - key_range_st = st.lists(key_tuple, min_size=1, max_size=10) - return key_range_st + return st.lists(key_tuple, min_size=1, max_size=10) diff --git a/tests/v3/conftest.py b/tests/v3/conftest.py index d1ac41075..c3516f676 100644 --- a/tests/v3/conftest.py +++ b/tests/v3/conftest.py @@ -14,8 +14,7 @@ from zarr.store.remote import RemoteStore if TYPE_CHECKING: - from collections.abc import Generator, Iterator - from types import ModuleType + from collections.abc import Generator from typing import Any, Literal from _pytest.compat import LEGACY_PATH @@ -99,13 +98,13 @@ async def async_group(request: pytest.FixtureRequest, tmpdir: LEGACY_PATH) -> As @pytest.fixture(params=["numpy", "cupy"]) -def xp(request: pytest.FixtureRequest) -> Iterator[ModuleType]: +def xp(request: pytest.FixtureRequest) -> Any: """Fixture to parametrize over numpy-like libraries""" if request.param == "cupy": request.node.add_marker(pytest.mark.gpu) - yield pytest.importorskip(request.param) + return pytest.importorskip(request.param) @pytest.fixture(autouse=True) diff --git a/tests/v3/test_array.py b/tests/v3/test_array.py index b3362c52b..06fd791e6 100644 --- a/tests/v3/test_array.py +++ b/tests/v3/test_array.py @@ -27,7 +27,7 @@ def test_array_creation_existing_node( """ spath = StorePath(store) group = Group.from_store(spath, zarr_format=zarr_format) - expected_exception: type[ContainsArrayError] | type[ContainsGroupError] + expected_exception: type[ContainsArrayError | ContainsGroupError] if extant_node == "array": expected_exception = ContainsArrayError _ = group.create_array("extant", shape=(10,), dtype="uint8") diff --git a/tests/v3/test_group.py b/tests/v3/test_group.py index c8310f33e..8beb344b4 100644 --- a/tests/v3/test_group.py +++ b/tests/v3/test_group.py @@ -93,8 +93,8 @@ def test_group_members(store: Store, zarr_format: ZarrFormat) -> None: members_expected["subgroup"] = group.create_group("subgroup") # make a sub-sub-subgroup, to ensure that the children calculation doesn't go # too deep in the hierarchy - subsubgroup = members_expected["subgroup"].create_group("subsubgroup") # type: ignore - subsubsubgroup = subsubgroup.create_group("subsubsubgroup") # type: ignore + subsubgroup = members_expected["subgroup"].create_group("subsubgroup") + subsubsubgroup = subsubgroup.create_group("subsubsubgroup") members_expected["subarray"] = group.create_array( "subarray", shape=(100,), dtype="uint8", chunk_shape=(10,), exists_ok=True @@ -271,7 +271,7 @@ def test_group_iter(store: Store, zarr_format: ZarrFormat) -> None: group = Group.from_store(store, zarr_format=zarr_format) with pytest.raises(NotImplementedError): - [x for x in group] # type: ignore + [x for x in group] def test_group_len(store: Store, zarr_format: ZarrFormat) -> None: @@ -281,7 +281,7 @@ def test_group_len(store: Store, zarr_format: ZarrFormat) -> None: group = Group.from_store(store, zarr_format=zarr_format) with pytest.raises(NotImplementedError): - len(group) # type: ignore + len(group) def test_group_setitem(store: Store, zarr_format: ZarrFormat) -> None: @@ -468,7 +468,7 @@ def test_group_creation_existing_node( """ spath = StorePath(store) group = Group.from_store(spath, zarr_format=zarr_format) - expected_exception: type[ContainsArrayError] | type[ContainsGroupError] + expected_exception: type[ContainsArrayError | ContainsGroupError] attributes: dict[str, JSON] = {"old": True} if extant_node == "array": @@ -550,7 +550,7 @@ async def test_asyncgroup_attrs(store: Store, zarr_format: ZarrFormat) -> None: async def test_asyncgroup_info(store: Store, zarr_format: ZarrFormat) -> None: - agroup = await AsyncGroup.from_store( # noqa + agroup = await AsyncGroup.from_store( # noqa: F841 store, zarr_format=zarr_format, ) diff --git a/tests/v3/test_indexing.py b/tests/v3/test_indexing.py index 81dc67f38..d2cf455e0 100644 --- a/tests/v3/test_indexing.py +++ b/tests/v3/test_indexing.py @@ -36,7 +36,7 @@ @pytest.fixture async def store() -> AsyncGenerator[StorePath]: - yield StorePath(await MemoryStore.open(mode="w")) + return StorePath(await MemoryStore.open(mode="w")) def zarr_array_from_numpy_array( @@ -1782,9 +1782,9 @@ async def test_accessed_chunks( # Combine and generate the cartesian product to determine the chunks keys that # will be accessed - chunks_accessed = [] - for comb in itertools.product(*chunks_per_dim): - chunks_accessed.append(".".join([str(ci) for ci in comb])) + chunks_accessed = [ + ".".join([str(ci) for ci in comb]) for comb in itertools.product(*chunks_per_dim) + ] counts_before = store.counter.copy() diff --git a/tests/v3/test_metadata/test_v3.py b/tests/v3/test_metadata/test_v3.py index d4cf0c73e..f8e2ebd7b 100644 --- a/tests/v3/test_metadata/test_v3.py +++ b/tests/v3/test_metadata/test_v3.py @@ -303,7 +303,7 @@ def test_parse_invalid_dtype_raises(data): @pytest.mark.parametrize( "data_type,fill_value", [("uint8", -1), ("int32", 22.5), ("float32", "foo")] ) -async def test_invalid_fill_value_raises(data_type: str, fill_value: int | float) -> None: +async def test_invalid_fill_value_raises(data_type: str, fill_value: float) -> None: metadata_dict = { "zarr_format": 3, "node_type": "array", diff --git a/tests/v3/test_properties.py b/tests/v3/test_properties.py index a78e9207b..250ede67b 100644 --- a/tests/v3/test_properties.py +++ b/tests/v3/test_properties.py @@ -4,10 +4,11 @@ pytest.importorskip("hypothesis") -import hypothesis.extra.numpy as npst # noqa -import hypothesis.strategies as st # noqa -from hypothesis import given, settings # noqa -from zarr.testing.strategies import arrays, np_arrays, basic_indices # noqa +import hypothesis.extra.numpy as npst # noqa: E402 +import hypothesis.strategies as st # noqa: E402 +from hypothesis import given # noqa: E402 + +from zarr.testing.strategies import arrays, basic_indices, np_arrays # noqa: E402 @given(st.data()) diff --git a/tests/v3/test_store/test_remote.py b/tests/v3/test_store/test_remote.py index 495a5e5c4..a6457cfeb 100644 --- a/tests/v3/test_store/test_remote.py +++ b/tests/v3/test_store/test_remote.py @@ -86,10 +86,7 @@ def s3(s3_base: None) -> Generator[s3fs.S3FileSystem, None, None]: async def alist(it): - out = [] - async for a in it: - out.append(a) - return out + return [a async for a in it] async def test_basic() -> None: diff --git a/tests/v3/test_store/test_stateful_store.py b/tests/v3/test_store/test_stateful_store.py index 1ecbd87cc..85a31f9eb 100644 --- a/tests/v3/test_store/test_stateful_store.py +++ b/tests/v3/test_store/test_stateful_store.py @@ -131,7 +131,7 @@ def get(self, key: str, data: DataObject) -> None: @rule(key=paths, data=st.data()) def get_invalid_keys(self, key: str, data: DataObject) -> None: note("(get_invalid)") - assume(key not in self.model.keys()) + assume(key not in self.model) assert self.store.get(key, self.prototype) is None @precondition(lambda self: len(self.model.keys()) > 0) @@ -202,9 +202,9 @@ def check_paths_equal(self) -> None: @invariant() def check_vals_equal(self) -> None: note("Checking values equal") - for key, _val in self.model.items(): + for key, val in self.model.items(): store_item = self.store.get(key, self.prototype).to_bytes() - assert self.model[key].to_bytes() == store_item + assert val.to_bytes() == store_item @invariant() def check_num_keys_equal(self) -> None: diff --git a/tests/v3/test_v2.py b/tests/v3/test_v2.py index 9ddde68e2..c67f991a0 100644 --- a/tests/v3/test_v2.py +++ b/tests/v3/test_v2.py @@ -9,7 +9,7 @@ @pytest.fixture async def store() -> Iterator[StorePath]: - yield StorePath(await MemoryStore.open(mode="w")) + return StorePath(await MemoryStore.open(mode="w")) def test_simple(store: StorePath) -> None: