Skip to content

Commit bd55318

Browse files
committed
lint
1 parent 3095889 commit bd55318

File tree

9 files changed

+237
-122
lines changed

9 files changed

+237
-122
lines changed

.pre-commit-config.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ exclude: ^.cruft.json|.copier-answers.yml$
66

77
repos:
88
- repo: https://github.com/adamchainz/blacken-docs
9-
rev: "1.18.0"
9+
rev: "1.19.1"
1010
hooks:
1111
- id: blacken-docs
1212
additional_dependencies: [black==24.*]
@@ -35,21 +35,21 @@ repos:
3535
- id: rst-inline-touching-normal
3636

3737
- repo: https://github.com/rbubley/mirrors-prettier
38-
rev: "v3.4.2"
38+
rev: "v3.6.2"
3939
hooks:
4040
- id: prettier
4141
types_or: [yaml, markdown, html, css, scss, javascript, json]
4242
args: [--prose-wrap=always]
4343

4444
- repo: https://github.com/astral-sh/ruff-pre-commit
45-
rev: "v0.8.2"
45+
rev: "v0.12.1"
4646
hooks:
4747
- id: ruff-format
4848
- id: ruff
4949
args: ["--fix", "--show-fixes"]
5050

5151
- repo: https://github.com/codespell-project/codespell
52-
rev: "v2.3.0"
52+
rev: "v2.4.1"
5353
hooks:
5454
- id: codespell
5555
exclude: pixi.lock
@@ -68,17 +68,17 @@ repos:
6868
exclude: .pre-commit-config.yaml
6969

7070
- repo: https://github.com/abravalheri/validate-pyproject
71-
rev: "v0.23"
71+
rev: "v0.24.1"
7272
hooks:
7373
- id: validate-pyproject
7474
additional_dependencies: ["validate-pyproject-schema-store[all]"]
7575

7676
- repo: https://github.com/python-jsonschema/check-jsonschema
77-
rev: "0.30.0"
77+
rev: "0.33.1"
7878
hooks:
7979
- id: check-github-workflows
8080

8181
- repo: https://github.com/numpy/numpydoc
82-
rev: "v1.8.0"
82+
rev: "v1.9.0"
8383
hooks:
8484
- id: numpydoc-validation

pixi.lock

Lines changed: 179 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,17 @@ array-api-strict = ">=2.3.1"
6565
numpy = ">=2.1.3"
6666
pytest = ">=8.4.0"
6767
hypothesis = ">=6.131.28"
68-
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
68+
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
6969
# NOTE: don't add cupy, jax, pytorch, or sparse here,
7070
# as they slow down mypy and are not portable across target OSs
7171

7272
[tool.pixi.feature.lint.tasks]
73-
pre-commit-install = { cmd = "pre-commit install", description = "Install pre-commit"}
74-
pre-commit = { cmd = "pre-commit run --all-files", description = "Run pre-commit"}
75-
mypy = { cmd = "mypy", description="Type check with mypy"}
76-
pylint = { cmd = "pylint array_api_extra", cwd = "src" , description = "Lint using pylint"}
77-
pyright = { cmd = "basedpyright", description = "Type check with basedpyright"}
78-
lint = { depends-on = ["pre-commit", "pylint", "mypy", "pyright"] , description = "Run pre-commit, pylint, mypy, and pyright"}
73+
pre-commit-install = { cmd = "pre-commit install", description = "Install pre-commit" }
74+
pre-commit = { cmd = "pre-commit run --all-files", description = "Run pre-commit" }
75+
mypy = { cmd = "mypy", description = "Type check with mypy" }
76+
pylint = { cmd = "pylint array_api_extra", cwd = "src", description = "Lint using pylint" }
77+
pyright = { cmd = "basedpyright", description = "Type check with basedpyright" }
78+
lint = { depends-on = ["pre-commit", "pylint", "mypy", "pyright"], description = "Run pre-commit, pylint, mypy, and pyright" }
7979

8080
[tool.pixi.feature.tests.dependencies]
8181
pytest = ">=8.4.0"
@@ -85,18 +85,18 @@ array-api-strict = ">=2.3.1"
8585
numpy = ">=1.22.0"
8686

8787
[tool.pixi.feature.tests.tasks]
88-
tests = { cmd = "pytest -v", description = "Run tests"}
89-
tests-cov = { cmd="pytest -v -ra --cov --cov-report=xml --cov-report=term --durations=20", description = "Run tests with coverage"}
88+
tests = { cmd = "pytest -v", description = "Run tests" }
89+
tests-cov = { cmd = "pytest -v -ra --cov --cov-report=xml --cov-report=term --durations=20", description = "Run tests with coverage" }
9090

91-
clean-vendor-compat = { cmd = "rm -rf vendor_tests/array_api_compat", description = "Delete the existing vendored version of array-api-compat"}
92-
clean-vendor-extra = { cmd = "rm -rf vendor_tests/array_api_extra" , description = "Delete the existing vendored version of array-api-extra"}
93-
copy-vendor-compat = { cmd = "cp -r $(python -c 'import site; print(site.getsitepackages()[0])')/array_api_compat vendor_tests/", depends-on = ["clean-vendor-compat"] , description = "Vendor a clean copy of array-api-compat"}
94-
copy-vendor-extra = { cmd = "cp -r src/array_api_extra vendor_tests/", depends-on = ["clean-vendor-extra"] , description = "Vendor a clean copy of array-api-extra"}
95-
tests-vendor = { cmd = "pytest -v vendor_tests", depends-on = ["copy-vendor-compat", "copy-vendor-extra"] , description = "Check that array-api-extra and array-api-compat can be vendored together" }
91+
clean-vendor-compat = { cmd = "rm -rf vendor_tests/array_api_compat", description = "Delete the existing vendored version of array-api-compat" }
92+
clean-vendor-extra = { cmd = "rm -rf vendor_tests/array_api_extra", description = "Delete the existing vendored version of array-api-extra" }
93+
copy-vendor-compat = { cmd = "cp -r $(python -c 'import site; print(site.getsitepackages()[0])')/array_api_compat vendor_tests/", depends-on = ["clean-vendor-compat"], description = "Vendor a clean copy of array-api-compat" }
94+
copy-vendor-extra = { cmd = "cp -r src/array_api_extra vendor_tests/", depends-on = ["clean-vendor-extra"], description = "Vendor a clean copy of array-api-extra" }
95+
tests-vendor = { cmd = "pytest -v vendor_tests", depends-on = ["copy-vendor-compat", "copy-vendor-extra"], description = "Check that array-api-extra and array-api-compat can be vendored together" }
9696

97-
tests-ci = { depends-on = ["tests-cov", "tests-vendor"] , description = "Run tests with coverage and vendor tests"}
98-
coverage = { cmd = "coverage html", depends-on = ["tests-cov"], description = "Generate test coverage html report"}
99-
open-coverage = { cmd = "open htmlcov/index.html", depends-on = ["coverage"] , description = "Open test coverage report"}
97+
tests-ci = { depends-on = ["tests-cov", "tests-vendor"], description = "Run tests with coverage and vendor tests" }
98+
coverage = { cmd = "coverage html", depends-on = ["tests-cov"], description = "Generate test coverage html report" }
99+
open-coverage = { cmd = "open htmlcov/index.html", depends-on = ["coverage"], description = "Open test coverage report" }
100100

101101
[tool.pixi.feature.docs.dependencies]
102102
sphinx = ">=7.4.7"
@@ -105,20 +105,20 @@ myst-parser = ">=4.0.1"
105105
sphinx-copybutton = ">=0.5.2"
106106
sphinx-autodoc-typehints = ">=1.25.3"
107107
# Needed to import parsed modules with autodoc
108-
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
108+
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
109109
pytest = ">=8.4.0"
110110
typing-extensions = ">=4.14.0"
111111
numpy = ">=2.1.3"
112112

113113
[tool.pixi.feature.docs.tasks]
114-
docs = { cmd = "sphinx-build -E -W . build/", cwd = "docs" , description = "Build docs"}
115-
open-docs = { cmd = "open build/index.html", cwd = "docs", depends-on = ["docs"] , description = "Open the generated docs"}
114+
docs = { cmd = "sphinx-build -E -W . build/", cwd = "docs", description = "Build docs" }
115+
open-docs = { cmd = "open build/index.html", cwd = "docs", depends-on = ["docs"], description = "Open the generated docs" }
116116

117117
[tool.pixi.feature.dev.dependencies]
118118
ipython = ">=7.33.0"
119119

120120
[tool.pixi.feature.dev.tasks]
121-
ipython = { cmd = "ipython" , description = "Launch ipython"}
121+
ipython = { cmd = "ipython", description = "Launch ipython" }
122122

123123
[tool.pixi.feature.py310.dependencies]
124124
python = "~=3.10.0"
@@ -135,7 +135,7 @@ numpy = "=1.22.0"
135135
# Note: JAX and PyTorch will install CPU variants.
136136
[tool.pixi.feature.backends.dependencies]
137137
pytorch = ">=2.7.0"
138-
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
138+
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
139139
sparse = ">=0.17.0"
140140

141141
[tool.pixi.feature.backends.target.linux-64.dependencies]
@@ -184,7 +184,7 @@ python-freethreading = "~=3.13.0"
184184
pytest-run-parallel = ">=0.4.4"
185185
numpy = ">=2.3.0"
186186
# pytorch = "*" # Not available on Python 3.13t yet
187-
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
187+
dask-core = ">=2025.5.1" # No distributed, tornado, etc.
188188
# sparse = "*" # numba not available on Python 3.13t yet
189189
# jax = "*" # ml_dtypes not available on Python 3.13t yet
190190

@@ -245,7 +245,7 @@ ignore_missing_imports = true
245245

246246
[[tool.mypy.overrides]]
247247
module = ["tests/*"]
248-
disable_error_code = ["no-untyped-def"] # test(...) without -> None
248+
disable_error_code = ["no-untyped-def"] # test(...) without -> None
249249

250250
# pyright
251251

@@ -322,6 +322,7 @@ ignore = [
322322
"N801", # Class name should use CapWords convention
323323
"N802", # Function name should be lowercase
324324
"N806", # Variable in function should be lowercase
325+
"PLC0415", # `import` should be at the top-level of a file
325326
]
326327

327328

src/array_api_extra/_delegation.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,13 +325,15 @@ def quantile(
325325
xp = array_namespace(x, q) if xp is None else xp
326326

327327
try:
328-
import scipy
328+
import scipy # type: ignore[import-untyped]
329329
from packaging import version
330330

331331
# The quantile function in scipy 1.16 supports array API directly, no need
332332
# to delegate
333-
if version.parse(scipy.__version__) >= version.parse("1.16"):
334-
from scipy.stats import quantile as scipy_quantile
333+
if version.parse(scipy.__version__) >= version.parse("1.16"): # pyright: ignore[reportUnknownArgumentType]
334+
from scipy.stats import ( # type: ignore[import-untyped]
335+
quantile as scipy_quantile,
336+
)
335337

336338
return scipy_quantile(x, p=q, axis=axis, keepdims=keepdims, method=method)
337339
except (ImportError, AttributeError):

src/array_api_extra/_lib/_funcs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ def broadcast_shapes(*shapes: tuple[float | None, ...]) -> tuple[int | None, ...
268268
for axis in range(-ndim, 0):
269269
sizes = {shape[axis] for shape in shapes if axis >= -len(shape)}
270270
# Dask uses NaN for unknown shape, which predates the Array API spec for None
271-
none_size = None in sizes or math.nan in sizes
271+
none_size = None in sizes or math.nan in sizes # noqa: PLW0177
272272
sizes -= {1, None, math.nan}
273273
if len(sizes) > 1:
274274
msg = (

src/array_api_extra/_lib/_quantile.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Quantile implementation."""
22

33
from types import ModuleType
4+
from typing import cast
45

56
from ._at import at
67
from ._utils import _compat
@@ -14,7 +15,7 @@ def quantile(
1415
/,
1516
*,
1617
axis: int | None = None,
17-
keepdims: bool = None, # noqa: RUF013
18+
keepdims: bool | None = None,
1819
method: str = "linear",
1920
xp: ModuleType | None = None,
2021
) -> Array: # numpydoc ignore=PR01,RT01
@@ -25,26 +26,27 @@ def quantile(
2526
q_is_scalar = isinstance(q, int | float)
2627
if q_is_scalar:
2728
q = xp.asarray(q, dtype=xp.float64, device=_compat.device(x))
29+
q_arr = cast(Array, q)
2830

2931
if not xp.isdtype(x.dtype, ("integral", "real floating")):
3032
raise ValueError("`x` must have real dtype.") # noqa: EM101
31-
if not xp.isdtype(q.dtype, "real floating"):
33+
if not xp.isdtype(q_arr.dtype, "real floating"):
3234
raise ValueError("`q` must have real floating dtype.") # noqa: EM101
3335

3436
# Promote to common dtype
3537
x = xp.astype(x, xp.float64)
36-
q = xp.astype(q, xp.float64)
37-
q = xp.asarray(q, device=_compat.device(x))
38+
q_arr = xp.astype(q_arr, xp.float64)
39+
q_arr = xp.asarray(q_arr, device=_compat.device(x))
3840

3941
dtype = x.dtype
4042
axis_none = axis is None
41-
ndim = max(x.ndim, q.ndim)
43+
ndim = max(x.ndim, q_arr.ndim)
4244

4345
if axis_none:
4446
x = xp.reshape(x, (-1,))
45-
q = xp.reshape(q, (-1,))
47+
q_arr = xp.reshape(q_arr, (-1,))
4648
axis = 0
47-
elif not isinstance(axis, int):
49+
elif not isinstance(axis, int): # pyright: ignore[reportUnnecessaryIsInstance]
4850
raise ValueError("`axis` must be an integer or None.") # noqa: EM101
4951
elif axis >= ndim or axis < -ndim:
5052
raise ValueError("`axis` is not compatible with the shapes of the inputs.") # noqa: EM101
@@ -63,15 +65,15 @@ def quantile(
6365

6466
# Move axis to the end for easier processing
6567
y = xp.moveaxis(y, axis, -1)
66-
if not (q_is_scalar or q.ndim == 0):
67-
q = xp.moveaxis(q, axis, -1)
68+
if not (q_is_scalar or q_arr.ndim == 0):
69+
q_arr = xp.moveaxis(q_arr, axis, -1)
6870

6971
n = xp.asarray(y.shape[-1], dtype=dtype, device=_compat.device(y))
7072

71-
res = _quantile_hf(y, q, n, method, xp)
73+
res = _quantile_hf(y, q_arr, n, method, xp)
7274

7375
# Handle NaN output for invalid q values
74-
p_mask = (q > 1) | (q < 0) | xp.isnan(q)
76+
p_mask = (q_arr > 1) | (q_arr < 0) | xp.isnan(q_arr)
7577
if xp.any(p_mask):
7678
res = xp.asarray(res, copy=True)
7779
res = at(res, p_mask).set(xp.nan)
@@ -100,7 +102,7 @@ def _quantile_hf(
100102
y: Array, p: Array, n: Array, method: str, xp: ModuleType
101103
) -> Array: # numpydoc ignore=PR01,RT01
102104
"""Helper function for Hyndman-Fan quantile method."""
103-
ms = {
105+
ms: dict[str, Array | int | float] = {
104106
"inverted_cdf": 0,
105107
"averaged_inverted_cdf": 0,
106108
"closest_observation": -0.5,

src/array_api_extra/testing.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,9 @@ def temp_setattr(mod: ModuleType, name: str, func: object) -> None:
297297
# Enable using patch_lazy_xp_function not as a context manager
298298
temp_setattr = monkeypatch.setattr # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
299299

300-
def iter_tagged() -> (
301-
Iterator[tuple[ModuleType, str, Callable[..., Any], dict[str, Any]]]
302-
):
300+
def iter_tagged() -> Iterator[
301+
tuple[ModuleType, str, Callable[..., Any], dict[str, Any]]
302+
]:
303303
for mod in mods:
304304
for name, func in mod.__dict__.items():
305305
tags: dict[str, Any] | None = None

tests/test_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def test_device(self, xp: ModuleType, library: Backend, device: Device):
237237
actual = capabilities(xp, device=device.type) # type: ignore[attr-defined] # pyright: ignore[reportUnknownArgumentType,reportAttributeAccessIssue]
238238

239239

240-
class Wrapper(Generic[T]):
240+
class Wrapper(Generic[T]): # noqa: PLW1641
241241
"""Trivial opaque wrapper. Must be pickleable."""
242242

243243
x: T
@@ -263,7 +263,7 @@ def __reduce__(self) -> tuple[object, ...]:
263263

264264
# Note: NotHashable() instances can be reduced to an
265265
# unserializable local class
266-
class NotHashable:
266+
class NotHashable: # noqa: PLW1641
267267
@override
268268
def __eq__(self, other: object) -> bool:
269269
return isinstance(other, type(self)) and other.__dict__ == self.__dict__

tests/test_testing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def test_patch_lazy_xp_functions_deprecated_monkeypatch(
459459
y = non_materializable5(x)
460460
xp_assert_equal(y, x)
461461

462-
with pytest.warns(DeprecationWarning):
462+
with pytest.warns(DeprecationWarning, match="`monkeypatch` parameter"):
463463
_ = patch_lazy_xp_functions(request, monkeypatch, xp=xp)
464464

465465
with pytest.raises(AssertionError, match=r"dask\.compute.* 1 times"):

0 commit comments

Comments
 (0)