Skip to content

Commit 83ccfff

Browse files
Enforce ruff/flake8-pyi rules (PYI) (#10359)
* Apply ruff/flake8-pyi rule PYI018 PYI018 Private TypeVar is never used * Apply ruff/flake8-pyi rule PYI024 PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple` * Apply ruff/flake8-pyi rule PYI025 PYI025 Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin * Apply ruff/flake8-pyi rule PYI032 PYI032 Prefer `object` to `Any` for the second parameter * Apply ruff/flake8-pyi rule PYI034 PYI034 such methods usually return `self` at runtime * Apply ruff/flake8-pyi rule PYI046 PYI046 Private protocol is never used * Apply ruff/flake8-pyi rule PYI051 PYI051 `Literal["..."]` is redundant in a union with `str` * Apply ruff/flake8-pyi rule PYI055 PYI055 Multiple `type` members in a union. Combine them into one * Apply ruff/flake8-pyi rule PYI063 PYI063 Use PEP 570 syntax for positional-only parameters * Enforce ruff/flake8-pyi rules (PYI) * Manual typing fix By definition `T_Chunks` and `T_Engine` could actually be `None`. * Partially revert "Apply ruff/flake8-pyi rule PYI051" This partially reverts commit 4bc134a.
1 parent 85deeac commit 83ccfff

File tree

15 files changed

+57
-42
lines changed

15 files changed

+57
-42
lines changed

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ extend-select = [
254254
"ISC", # flake8-implicit-str-concat
255255
"PIE", # flake8-pie
256256
"TID", # flake8-tidy-imports (absolute imports)
257+
"PYI", # flake8-pyi
257258
"I", # isort
258259
"PERF", # Perflint
259260
"W", # pycodestyle warnings
@@ -269,6 +270,8 @@ extend-safe-fixes = [
269270
ignore = [
270271
"C40", # unnecessary generator, comprehension, or literal
271272
"PIE790", # unnecessary pass statement
273+
"PYI019", # use `Self` instead of custom TypeVar
274+
"PYI041", # use `float` instead of `int | float`
272275
"PERF203", # try-except within a loop incurs performance overhead
273276
"E402", # module level import not at top of file
274277
"E731", # do not assign a lambda expression, use a def
@@ -284,6 +287,9 @@ ignore = [
284287
[tool.ruff.lint.per-file-ignores]
285288
# don't enforce absolute imports
286289
"asv_bench/**" = ["TID252"]
290+
# looks like ruff bugs
291+
"xarray/core/_typed_ops.py" = ["PYI034"]
292+
"xarray/namedarray/_typing.py" = ["PYI018", "PYI046"]
287293

288294
[tool.ruff.lint.isort]
289295
known-first-party = ["xarray"]

xarray/backends/api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
T_NetcdfEngine = Literal["netcdf4", "scipy", "h5netcdf"]
7272
T_Engine = Union[
7373
T_NetcdfEngine,
74-
Literal["pydap", "zarr"],
74+
Literal["pydap", "zarr"], # noqa: PYI051
7575
type[BackendEntrypoint],
7676
str, # no nice typing support for custom backends
7777
None,
@@ -710,8 +710,8 @@ def open_dataset(
710710
def open_dataarray(
711711
filename_or_obj: str | os.PathLike[Any] | ReadBuffer | AbstractDataStore,
712712
*,
713-
engine: T_Engine | None = None,
714-
chunks: T_Chunks | None = None,
713+
engine: T_Engine = None,
714+
chunks: T_Chunks = None,
715715
cache: bool | None = None,
716716
decode_cf: bool | None = None,
717717
mask_and_scale: bool | None = None,
@@ -1394,7 +1394,7 @@ def open_mfdataset(
13941394
| os.PathLike
13951395
| ReadBuffer
13961396
| NestedSequence[str | os.PathLike | ReadBuffer],
1397-
chunks: T_Chunks | None = None,
1397+
chunks: T_Chunks = None,
13981398
concat_dim: (
13991399
str
14001400
| DataArray
@@ -1406,7 +1406,7 @@ def open_mfdataset(
14061406
) = None,
14071407
compat: CompatOptions = "no_conflicts",
14081408
preprocess: Callable[[Dataset], Dataset] | None = None,
1409-
engine: T_Engine | None = None,
1409+
engine: T_Engine = None,
14101410
data_vars: Literal["all", "minimal", "different"] | list[str] = "all",
14111411
coords="different",
14121412
combine: Literal["by_coords", "nested"] = "by_coords",

xarray/computation/apply_ufunc.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616
Iterator,
1717
Mapping,
1818
Sequence,
19-
Set,
2019
)
21-
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union
20+
from collections.abc import (
21+
Set as AbstractSet,
22+
)
23+
from typing import TYPE_CHECKING, Any, Literal
2224

2325
import numpy as np
2426

25-
_T = TypeVar("_T", bound=Union["Dataset", "DataArray"])
26-
_U = TypeVar("_U", bound=Union["Dataset", "DataArray"])
27-
_V = TypeVar("_V", bound=Union["Dataset", "DataArray"])
28-
2927
from xarray.core import duck_array_ops, utils
3028
from xarray.core.formatting import limit_lines
3129
from xarray.core.indexes import Index, filter_indexes_from_coords
@@ -200,7 +198,7 @@ def _get_coords_list(args: Iterable[Any]) -> list[Coordinates]:
200198
def build_output_coords_and_indexes(
201199
args: Iterable[Any],
202200
signature: _UFuncSignature,
203-
exclude_dims: Set = frozenset(),
201+
exclude_dims: AbstractSet = frozenset(),
204202
combine_attrs: CombineAttrsOptions = "override",
205203
) -> tuple[list[dict[Any, Variable]], list[dict[Any, Index]]]:
206204
"""Build output coordinates and indexes for an operation.
@@ -615,7 +613,7 @@ def apply_groupby_func(func, *args):
615613

616614

617615
def unified_dim_sizes(
618-
variables: Iterable[Variable], exclude_dims: Set = frozenset()
616+
variables: Iterable[Variable], exclude_dims: AbstractSet = frozenset()
619617
) -> dict[Hashable, int]:
620618
dim_sizes: dict[Hashable, int] = {}
621619

@@ -894,7 +892,7 @@ def apply_ufunc(
894892
*args: Any,
895893
input_core_dims: Sequence[Sequence] | None = None,
896894
output_core_dims: Sequence[Sequence] | None = ((),),
897-
exclude_dims: Set = frozenset(),
895+
exclude_dims: AbstractSet = frozenset(),
898896
vectorize: bool = False,
899897
join: JoinOptions = "exact",
900898
dataset_join: str = "exact",

xarray/core/accessor_dt.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import numpy as np
77
import pandas as pd
8+
from typing_extensions import Self
89

910
from xarray.coding.calendar_ops import _decimal_year
1011
from xarray.coding.times import infer_calendar_name
@@ -650,7 +651,7 @@ def total_seconds(self) -> T_DataArray:
650651
class CombinedDatetimelikeAccessor(
651652
DatetimeAccessor[T_DataArray], TimedeltaAccessor[T_DataArray]
652653
):
653-
def __new__(cls, obj: T_DataArray) -> CombinedDatetimelikeAccessor:
654+
def __new__(cls, obj: T_DataArray) -> Self:
654655
# CombinedDatetimelikeAccessor isn't really instantiated. Instead
655656
# we need to choose which parent (datetime or timedelta) is
656657
# appropriate. Since we're checking the dtypes anyway, we'll just

xarray/core/datatree_render.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88

99
from __future__ import annotations
1010

11-
from collections import namedtuple
1211
from collections.abc import Iterable, Iterator
1312
from math import ceil
14-
from typing import TYPE_CHECKING
13+
from typing import TYPE_CHECKING, NamedTuple
1514

1615
if TYPE_CHECKING:
1716
from xarray.core.datatree import DataTree
1817

19-
Row = namedtuple("Row", ("pre", "fill", "node"))
18+
19+
class Row(NamedTuple):
20+
pre: str
21+
fill: str
22+
node: DataTree | str
2023

2124

2225
class AbstractStyle:

xarray/core/indexes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,7 +1652,7 @@ class Indexes(collections.abc.Mapping, Generic[T_PandasOrXarrayIndex]):
16521652
16531653
"""
16541654

1655-
_index_type: type[Index] | type[pd.Index]
1655+
_index_type: type[Index | pd.Index]
16561656
_indexes: dict[Any, T_PandasOrXarrayIndex]
16571657
_variables: dict[Any, Variable]
16581658

@@ -1670,7 +1670,7 @@ def __init__(
16701670
self,
16711671
indexes: Mapping[Any, T_PandasOrXarrayIndex] | None = None,
16721672
variables: Mapping[Any, Variable] | None = None,
1673-
index_type: type[Index] | type[pd.Index] = Index,
1673+
index_type: type[Index | pd.Index] = Index,
16741674
):
16751675
"""Constructor not for public consumption.
16761676

xarray/core/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def copy(
214214

215215
# FYI in some cases we don't allow `None`, which this doesn't take account of.
216216
# FYI the `str` is for a size string, e.g. "16MB", supported by dask.
217-
T_ChunkDim: TypeAlias = str | int | Literal["auto"] | None | tuple[int, ...]
217+
T_ChunkDim: TypeAlias = str | int | Literal["auto"] | None | tuple[int, ...] # noqa: PYI051
218218
T_ChunkDimFreq: TypeAlias = Union["TimeResampler", T_ChunkDim]
219219
T_ChunksFreq: TypeAlias = T_ChunkDim | Mapping[Any, T_ChunkDimFreq]
220220
# We allow the tuple form of this (though arguably we could transition to named dims only)
@@ -329,7 +329,7 @@ def mode(self) -> str:
329329
# for _get_filepath_or_buffer
330330
...
331331

332-
def seek(self, __offset: int, __whence: int = ...) -> int:
332+
def seek(self, offset: int, whence: int = ..., /) -> int:
333333
# with one argument: gzip.GzipFile, bz2.BZ2File
334334
# with two arguments: zip.ZipFile, read_sas
335335
...
@@ -345,7 +345,7 @@ def tell(self) -> int:
345345

346346
@runtime_checkable
347347
class ReadBuffer(BaseBuffer, Protocol[AnyStr_co]):
348-
def read(self, __n: int = ...) -> AnyStr_co:
348+
def read(self, n: int = ..., /) -> AnyStr_co:
349349
# for BytesIOWrapper, gzip.GzipFile, bz2.BZ2File
350350
...
351351

xarray/core/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@
6161
MutableMapping,
6262
MutableSet,
6363
Sequence,
64-
Set,
6564
ValuesView,
6665
)
66+
from collections.abc import (
67+
Set as AbstractSet,
68+
)
6769
from enum import Enum
6870
from pathlib import Path
6971
from types import EllipsisType, ModuleType
@@ -1055,7 +1057,7 @@ def parse_ordered_dims(
10551057
)
10561058

10571059

1058-
def _check_dims(dim: Set[Hashable], all_dims: Set[Hashable]) -> None:
1060+
def _check_dims(dim: AbstractSet[Hashable], all_dims: AbstractSet[Hashable]) -> None:
10591061
wrong_dims = (dim - all_dims) - {...}
10601062
if wrong_dims:
10611063
wrong_dims_str = ", ".join(f"'{d}'" for d in wrong_dims)

xarray/groupers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ def factorize(self, group: T_Group) -> EncodedGroups:
902902
first_items = agged["first"]
903903
counts = agged["count"]
904904

905-
index_class: type[CFTimeIndex] | type[pd.DatetimeIndex]
905+
index_class: type[CFTimeIndex | pd.DatetimeIndex]
906906
if _contains_cftime_datetimes(group.data):
907907
index_class = CFTimeIndex
908908
datetime_class = type(first_n_items(group.data, 1).item())

xarray/namedarray/_typing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class Default(Enum):
3939
_default = Default.token
4040

4141
# https://stackoverflow.com/questions/74633074/how-to-type-hint-a-generic-numpy-array
42-
_T = TypeVar("_T")
4342
_T_co = TypeVar("_T_co", covariant=True)
4443

4544
_dtype = np.dtype
@@ -79,7 +78,7 @@ def dtype(self) -> _DType_co: ...
7978
_NormalizedChunks = tuple[tuple[int, ...], ...]
8079
# FYI in some cases we don't allow `None`, which this doesn't take account of.
8180
# # FYI the `str` is for a size string, e.g. "16MB", supported by dask.
82-
T_ChunkDim: TypeAlias = str | int | Literal["auto"] | None | tuple[int, ...]
81+
T_ChunkDim: TypeAlias = str | int | Literal["auto"] | None | tuple[int, ...] # noqa: PYI051
8382
# We allow the tuple form of this (though arguably we could transition to named dims only)
8483
T_Chunks: TypeAlias = T_ChunkDim | Mapping[Any, T_ChunkDim]
8584

0 commit comments

Comments
 (0)