Skip to content

Commit 4cbb17e

Browse files
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.
1 parent 5e57f75 commit 4cbb17e

28 files changed

+119
-103
lines changed

pyproject.toml

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,18 +207,36 @@ extend-exclude = [
207207

208208
[tool.ruff.lint]
209209
extend-select = [
210-
"B", # flake8-bugbear
211-
"I", # isort
212-
"ISC",
213-
"UP", # pyupgrade
214-
"RSE",
210+
"B", # flake8-bugbear
211+
"I", # isort
212+
"ISC", # flake8-implicit-str-concat
213+
"PGH", # pygrep-hooks
214+
"PYI", # flake8-pyi
215+
"RSE", # flake8-raise
215216
"RUF",
216217
"TCH", # flake8-type-checking
217218
"TRY", # tryceratops
219+
"UP", # pyupgrade
218220
]
219221
ignore = [
222+
"PYI013",
220223
"RUF005",
221224
"TRY003",
225+
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
226+
"W191",
227+
"E111",
228+
"E114",
229+
"E117",
230+
"D206",
231+
"D300",
232+
"Q000",
233+
"Q001",
234+
"Q002",
235+
"Q003",
236+
"COM812",
237+
"COM819",
238+
"ISC001",
239+
"ISC002",
222240
]
223241

224242
[tool.mypy]

src/zarr/abc/store.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABC, abstractmethod
22
from asyncio import gather
33
from collections.abc import AsyncGenerator, Iterable
4+
from types import TracebackType
45
from typing import Any, NamedTuple, Protocol, runtime_checkable
56

67
from typing_extensions import Self
@@ -35,7 +36,7 @@ class Store(ABC):
3536
_mode: AccessMode
3637
_is_open: bool
3738

38-
def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any):
39+
def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any) -> None:
3940
self._is_open = False
4041
self._mode = AccessMode.from_literal(mode)
4142

@@ -49,7 +50,12 @@ def __enter__(self) -> Self:
4950
"""Enter a context manager that will close the store upon exiting."""
5051
return self
5152

52-
def __exit__(self, *args: Any) -> None:
53+
def __exit__(
54+
self,
55+
exc_type: type[BaseException] | None,
56+
exc_value: BaseException | None,
57+
traceback: TracebackType | None,
58+
) -> None:
5359
"""Close the store."""
5460
self.close()
5561

src/zarr/api/asynchronous.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import asyncio
44
import warnings
5-
from typing import TYPE_CHECKING, Any, Literal, Union, cast
5+
from typing import TYPE_CHECKING, Any, Literal, cast
66

77
import numpy as np
88
import numpy.typing as npt
@@ -25,6 +25,10 @@
2525
from zarr.core.buffer import NDArrayLike
2626
from zarr.core.chunk_key_encodings import ChunkKeyEncoding
2727

28+
# TODO: this type could use some more thought
29+
ArrayLike = AsyncArray | Array | npt.NDArray[Any]
30+
PathLike = str
31+
2832
__all__ = [
2933
"consolidate_metadata",
3034
"copy",
@@ -53,10 +57,6 @@
5357
"zeros_like",
5458
]
5559

56-
# TODO: this type could use some more thought, noqa to avoid "Variable "asynchronous.ArrayLike" is not valid as a type"
57-
ArrayLike = Union[AsyncArray | Array | npt.NDArray[Any]] # noqa
58-
PathLike = str
59-
6060

6161
def _get_shape_chunks(a: ArrayLike | Any) -> tuple[ChunkCoords | None, ChunkCoords | None]:
6262
"""helper function to get the shape and chunks from an array-like object"""

src/zarr/codecs/transpose.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,14 @@ async def _decode_single(
9696
chunk_spec: ArraySpec,
9797
) -> NDBuffer:
9898
inverse_order = np.argsort(self.order)
99-
chunk_array = chunk_array.transpose(inverse_order)
100-
return chunk_array
99+
return chunk_array.transpose(inverse_order)
101100

102101
async def _encode_single(
103102
self,
104103
chunk_array: NDBuffer,
105104
_chunk_spec: ArraySpec,
106105
) -> NDBuffer | None:
107-
chunk_array = chunk_array.transpose(self.order)
108-
return chunk_array
106+
return chunk_array.transpose(self.order)
109107

110108
def compute_encoded_size(self, input_byte_length: int, _chunk_spec: ArraySpec) -> int:
111109
return input_byte_length

src/zarr/core/array.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def __init__(
110110
metadata: ArrayMetadata,
111111
store_path: StorePath,
112112
order: Literal["C", "F"] | None = None,
113-
):
113+
) -> None:
114114
metadata_parsed = parse_array_metadata(metadata)
115115
order_parsed = parse_indexing_order(order or config.get("array.order"))
116116

@@ -294,7 +294,7 @@ async def _create_v2(
294294
dtype: npt.DTypeLike,
295295
chunks: ChunkCoords,
296296
dimension_separator: Literal[".", "/"] | None = None,
297-
fill_value: None | int | float = None,
297+
fill_value: None | float = None,
298298
order: Literal["C", "F"] | None = None,
299299
filters: list[dict[str, JSON]] | None = None,
300300
compressor: dict[str, JSON] | None = None,
@@ -331,8 +331,7 @@ def from_dict(
331331
data: dict[str, JSON],
332332
) -> AsyncArray:
333333
metadata = parse_array_metadata(data)
334-
async_array = cls(metadata=metadata, store_path=store_path)
335-
return async_array
334+
return cls(metadata=metadata, store_path=store_path)
336335

337336
@classmethod
338337
async def open(

src/zarr/core/attributes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
class Attributes(MutableMapping[str, JSON]):
16-
def __init__(self, obj: Array | Group):
16+
def __init__(self, obj: Array | Group) -> None:
1717
# key=".zattrs", read_only=False, cache=True, synchronizer=None
1818
self._obj = obj
1919

src/zarr/core/buffer/core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def ravel(self, order: Literal["K", "A", "C", "F"] = ...) -> Self: ...
9393

9494
def all(self) -> bool: ...
9595

96-
def __eq__(self, other: Any) -> Self: # type: ignore[explicit-override, override]
96+
def __eq__(self, other: object) -> Self: # type: ignore[explicit-override, override]
9797
"""Element-wise equal
9898
9999
Notes
@@ -136,7 +136,7 @@ class Buffer(ABC):
136136
array-like object that must be 1-dim, contiguous, and byte dtype.
137137
"""
138138

139-
def __init__(self, array_like: ArrayLike):
139+
def __init__(self, array_like: ArrayLike) -> None:
140140
if array_like.ndim != 1:
141141
raise ValueError("array_like: only 1-dim allowed")
142142
if array_like.dtype != np.dtype("b"):
@@ -313,7 +313,7 @@ class NDBuffer:
313313
ndarray-like object that is convertible to a regular Numpy array.
314314
"""
315315

316-
def __init__(self, array: NDArrayLike):
316+
def __init__(self, array: NDArrayLike) -> None:
317317
# assert array.ndim > 0
318318
assert array.dtype != object
319319
self._data = array

src/zarr/core/buffer/cpu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Buffer(core.Buffer):
4545
array-like object that must be 1-dim, contiguous, and byte dtype.
4646
"""
4747

48-
def __init__(self, array_like: ArrayLike):
48+
def __init__(self, array_like: ArrayLike) -> None:
4949
super().__init__(array_like)
5050

5151
@classmethod
@@ -143,7 +143,7 @@ class NDBuffer(core.NDBuffer):
143143
ndarray-like object that is convertible to a regular Numpy array.
144144
"""
145145

146-
def __init__(self, array: NDArrayLike):
146+
def __init__(self, array: NDArrayLike) -> None:
147147
super().__init__(array)
148148

149149
@classmethod

src/zarr/core/buffer/gpu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Buffer(core.Buffer):
4848
array-like object that must be 1-dim, contiguous, and byte dtype.
4949
"""
5050

51-
def __init__(self, array_like: ArrayLike):
51+
def __init__(self, array_like: ArrayLike) -> None:
5252
if cp is None:
5353
raise ImportError(
5454
"Cannot use zarr.buffer.gpu.Buffer without cupy. Please install cupy."
@@ -137,7 +137,7 @@ class NDBuffer(core.NDBuffer):
137137
ndarray-like object that is convertible to a regular Numpy array.
138138
"""
139139

140-
def __init__(self, array: NDArrayLike):
140+
def __init__(self, array: NDArrayLike) -> None:
141141
if cp is None:
142142
raise ImportError(
143143
"Cannot use zarr.buffer.gpu.NDBuffer without cupy. Please install cupy."

src/zarr/core/group.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def parse_zarr_format(data: Any) -> ZarrFormat:
5454
def parse_attributes(data: Any) -> dict[str, Any]:
5555
if data is None:
5656
return {}
57-
elif isinstance(data, dict) and all(map(lambda v: isinstance(v, str), data.keys())):
57+
elif isinstance(data, dict) and all(isinstance(k, str) for k in data):
5858
return data
5959
msg = f"Expected dict with string keys. Got {type(data)} instead."
6060
raise TypeError(msg)
@@ -104,7 +104,9 @@ def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]:
104104
),
105105
}
106106

107-
def __init__(self, attributes: dict[str, Any] | None = None, zarr_format: ZarrFormat = 3):
107+
def __init__(
108+
self, attributes: dict[str, Any] | None = None, zarr_format: ZarrFormat = 3
109+
) -> None:
108110
attributes_parsed = parse_attributes(attributes)
109111
zarr_format_parsed = parse_zarr_format(zarr_format)
110112

@@ -202,11 +204,10 @@ def from_dict(
202204
store_path: StorePath,
203205
data: dict[str, Any],
204206
) -> AsyncGroup:
205-
group = cls(
207+
return cls(
206208
metadata=GroupMetadata.from_dict(data),
207209
store_path=store_path,
208210
)
209-
return group
210211

211212
async def getitem(
212213
self,
@@ -888,8 +889,7 @@ def members(self, max_depth: int | None = 0) -> tuple[tuple[str, Array | Group],
888889
"""
889890
_members = self._sync_iter(self._async_group.members(max_depth=max_depth))
890891

891-
result = tuple(map(lambda kv: (kv[0], _parse_async_node(kv[1])), _members))
892-
return result
892+
return tuple((kv[0], _parse_async_node(kv[1])) for kv in _members)
893893

894894
def __contains__(self, member: str) -> bool:
895895
return self._sync(self._async_group.contains(member))

src/zarr/core/indexing.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ArrayIndexError(IndexError):
5454
class BoundsCheckError(IndexError):
5555
_msg = ""
5656

57-
def __init__(self, dim_len: int):
57+
def __init__(self, dim_len: int) -> None:
5858
self._msg = f"index out of bounds for dimension with length {dim_len}"
5959

6060

@@ -255,7 +255,7 @@ class IntDimIndexer:
255255
dim_chunk_len: int
256256
nitems: int = 1
257257

258-
def __init__(self, dim_sel: int, dim_len: int, dim_chunk_len: int):
258+
def __init__(self, dim_sel: int, dim_len: int, dim_chunk_len: int) -> None:
259259
object.__setattr__(self, "dim_sel", normalize_integer_selection(dim_sel, dim_len))
260260
object.__setattr__(self, "dim_len", dim_len)
261261
object.__setattr__(self, "dim_chunk_len", dim_chunk_len)
@@ -279,7 +279,7 @@ class SliceDimIndexer:
279279
stop: int
280280
step: int
281281

282-
def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int):
282+
def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int) -> None:
283283
# normalize
284284
start, stop, step = dim_sel.indices(dim_len)
285285
if step < 1:
@@ -453,7 +453,7 @@ def __init__(
453453
selection: BasicSelection,
454454
shape: ChunkCoords,
455455
chunk_grid: ChunkGrid,
456-
):
456+
) -> None:
457457
chunk_shape = get_chunk_shape(chunk_grid)
458458
# handle ellipsis
459459
selection_normalized = replace_ellipsis(selection, shape)
@@ -509,7 +509,7 @@ class BoolArrayDimIndexer:
509509
nitems: int
510510
dim_chunk_ixs: npt.NDArray[np.intp]
511511

512-
def __init__(self, dim_sel: npt.NDArray[np.bool_], dim_len: int, dim_chunk_len: int):
512+
def __init__(self, dim_sel: npt.NDArray[np.bool_], dim_len: int, dim_chunk_len: int) -> None:
513513
# check number of dimensions
514514
if not is_bool_array(dim_sel, 1):
515515
raise IndexError("Boolean arrays in an orthogonal selection must be 1-dimensional only")
@@ -626,7 +626,7 @@ def __init__(
626626
wraparound: bool = True,
627627
boundscheck: bool = True,
628628
order: Order = Order.UNKNOWN,
629-
):
629+
) -> None:
630630
# ensure 1d array
631631
dim_sel = np.asanyarray(dim_sel)
632632
if not is_integer_array(dim_sel, 1):
@@ -766,7 +766,7 @@ class OrthogonalIndexer(Indexer):
766766
is_advanced: bool
767767
drop_axes: tuple[int, ...]
768768

769-
def __init__(self, selection: Selection, shape: ChunkCoords, chunk_grid: ChunkGrid):
769+
def __init__(self, selection: Selection, shape: ChunkCoords, chunk_grid: ChunkGrid) -> None:
770770
chunk_shape = get_chunk_shape(chunk_grid)
771771

772772
# handle ellipsis
@@ -880,7 +880,9 @@ class BlockIndexer(Indexer):
880880
shape: ChunkCoords
881881
drop_axes: ChunkCoords
882882

883-
def __init__(self, selection: BasicSelection, shape: ChunkCoords, chunk_grid: ChunkGrid):
883+
def __init__(
884+
self, selection: BasicSelection, shape: ChunkCoords, chunk_grid: ChunkGrid
885+
) -> None:
884886
chunk_shape = get_chunk_shape(chunk_grid)
885887

886888
# handle ellipsis
@@ -1005,7 +1007,9 @@ class CoordinateIndexer(Indexer):
10051007
chunk_shape: ChunkCoords
10061008
drop_axes: ChunkCoords
10071009

1008-
def __init__(self, selection: CoordinateSelection, shape: ChunkCoords, chunk_grid: ChunkGrid):
1010+
def __init__(
1011+
self, selection: CoordinateSelection, shape: ChunkCoords, chunk_grid: ChunkGrid
1012+
) -> None:
10091013
chunk_shape = get_chunk_shape(chunk_grid)
10101014

10111015
cdata_shape: ChunkCoords
@@ -1122,7 +1126,7 @@ def __iter__(self) -> Iterator[ChunkProjection]:
11221126

11231127
@dataclass(frozen=True)
11241128
class MaskIndexer(CoordinateIndexer):
1125-
def __init__(self, selection: MaskSelection, shape: ChunkCoords, chunk_grid: ChunkGrid):
1129+
def __init__(self, selection: MaskSelection, shape: ChunkCoords, chunk_grid: ChunkGrid) -> None:
11261130
# some initial normalization
11271131
selection_normalized = cast(tuple[MaskSelection], ensure_tuple(selection))
11281132
selection_normalized = cast(tuple[MaskSelection], replace_lists(selection_normalized))

0 commit comments

Comments
 (0)