From b66d969570904d8a838d9e9bded30cd6bf16d1e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 16:36:23 +0000 Subject: [PATCH 1/2] Fix mypy failures on Python 3.12+ CI jobs mypy was pinned to python_version = "3.11", which made it reject newer numpy stubs that use PEP 695 `type` statements (valid only on 3.12+), failing the 3.12/3.13/3.14 matrix jobs with a syntax error. Drop the pin so mypy targets whichever interpreter it runs under, matching the CI matrix. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_015tLcNE8iW7jycc6HZMaZx8 --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 82e0acc..e788a61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,8 +115,10 @@ select = ["E", "F", "I", "B", "UP", "N", "RUF", "TID"] "scripts/inspect_r_api_update.py" = ["TID251"] "scripts/verify_release_provenance.py" = ["TID251"] +# No python_version pin: mypy targets the interpreter it runs under. The CI +# matrix runs mypy on 3.11-3.14, and pinning to 3.11 made mypy reject newer +# numpy stubs that use PEP 695 `type` statements (valid only on 3.12+). [tool.mypy] -python_version = "3.11" strict = true files = ["src/nns", "tests"] mypy_path = ["tests"] From 9ff5fa6df667cc2a96b5a060711c9912b992916b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 16:59:18 +0000 Subject: [PATCH 2/2] Fix latent mypy errors exposed by 3.12+ numpy stubs Dropping the python_version pin let mypy target 3.12-3.14 in CI, where the numpy stubs are more precise and surfaced two real type errors: - norm.py: the explicit cast() became redundant (warn_redundant_casts) - central_tendencies.py: a return value was inferred as Any (no-any-return) Replace the cast with an annotated assignment and annotate the sorted-modes result. Both narrow the type without an explicit cast, so they satisfy mypy on 3.11 (Any-returning stubs) and 3.12+ (precise stubs) alike. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_015tLcNE8iW7jycc6HZMaZx8 --- src/nns/central_tendencies.py | 8 +++++--- src/nns/norm.py | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/nns/central_tendencies.py b/src/nns/central_tendencies.py index f3c4cd4..f89746e 100644 --- a/src/nns/central_tendencies.py +++ b/src/nns/central_tendencies.py @@ -135,10 +135,12 @@ def _discrete_mode(values: NDArray[np.float64], multi: bool) -> float | NDArray[ integerized = _nearest_int_half_up_array(values) modes, counts = np.unique(integerized, return_counts=True) tied_modes = modes[counts == int(np.max(counts))] - tied_modes = np.sort(tied_modes.astype(np.float64)) + # Annotate the sorted result so the multi return is typed rather than Any + # under the more precise numpy stubs that mypy sees when targeting 3.12+. + sorted_modes: NDArray[np.float64] = np.sort(tied_modes.astype(np.float64)) if multi: - return tied_modes - return float(np.mean(tied_modes)) + return sorted_modes + return float(np.mean(sorted_modes)) def _continuous_mode(values: NDArray[np.float64], multi: bool) -> float | NDArray[np.float64]: diff --git a/src/nns/norm.py b/src/nns/norm.py index b9c4496..07dc3f7 100644 --- a/src/nns/norm.py +++ b/src/nns/norm.py @@ -22,7 +22,12 @@ def nns_norm(x: NDArray[np.float64], linear: bool = False) -> NDArray[np.float64 scale_factor = _scale_factor(values) scales = np.mean(ratio_grid * scale_factor, axis=0) - return cast(NDArray[np.float64], values * scales[np.newaxis, :]) + # Annotated assignment instead of cast(): under mypy targeting 3.12+ the + # numpy stubs type this product precisely, making an explicit cast redundant + # (warn_redundant_casts), while on 3.11 the stubs return Any and the + # annotation still narrows it without a no-any-return error. + scaled: NDArray[np.float64] = values * scales[np.newaxis, :] + return scaled def _scale_factor(values: NDArray[np.float64]) -> NDArray[np.float64]: