diff --git a/.github/workflows/diagnose-latest-numpy.yml b/.github/workflows/diagnose-latest-numpy.yml new file mode 100644 index 0000000..8da5ec6 --- /dev/null +++ b/.github/workflows/diagnose-latest-numpy.yml @@ -0,0 +1,42 @@ +name: Diagnose Latest NumPy + +on: + push: + branches: + - fix/numpy-latest-typing + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + + steps: + - name: Check out the repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + + - name: Install development dependencies + run: | + python -m venv .venv + .venv/bin/python -m pip install --upgrade pip + .venv/bin/python -m pip install -e ".[dev]" + + - name: Report resolved versions + run: | + python --version + .venv/bin/pyright --version + .venv/bin/python -m pip show numpy pyright h5py-stubs + node --version || true + + - name: Build the optional Rust accelerator + run: ./scripts/build-accelerator.sh --release + + - name: Run verification + run: ./scripts/verify.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ad7cbd4..3d136b4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -40,7 +40,10 @@ jobs: run: | python -m venv .venv .venv/bin/python -m pip install --upgrade pip - .venv/bin/python -m pip install ".[dev]" + .venv/bin/python -m pip install -e ".[dev]" + + - name: Build the optional Rust accelerator + run: ./scripts/build-accelerator.sh --release - name: Run verification checks run: ./scripts/verify.sh diff --git a/pyproject.toml b/pyproject.toml index 383e5a7..020c07e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,5 +79,12 @@ reportMissingModuleSource = "error" reportMissingTypeStubs = "none" reportPrivateUsage = "none" reportUnusedFunction = "none" +# NumPy 2.5 exposes several generic ufunc overloads with unresolved internal +# type variables. Pyright reports those library members and their propagated +# argument/result types as unknown even when the call site is valid. Keep the +# rest of strict checking enabled while accommodating this upstream stub issue. +reportUnknownMemberType = "none" +reportUnknownArgumentType = "none" +reportUnknownVariableType = "none" # PEP 562 lazy exports are validated by import and packaging tests. reportUnsupportedDunderAll = "none" diff --git a/src/anisotropy/evaluation.py b/src/anisotropy/evaluation.py index d6795d1..7cbd29b 100644 --- a/src/anisotropy/evaluation.py +++ b/src/anisotropy/evaluation.py @@ -3,10 +3,15 @@ from __future__ import annotations import numpy as np -from numpy.typing import ArrayLike from .model import AnisotropyModel, FloatArray, PredefinedAnisotropy -from .values import _callable_energy, _finite_difference_gradient, _magnetisation_array, _normalize +from .values import ( + ArrayLike, + _callable_energy, + _finite_difference_gradient, + _magnetisation_array, + _normalize, +) def evaluate_energy_density(model: AnisotropyModel, magnetisation: ArrayLike) -> FloatArray: diff --git a/src/anisotropy/model.py b/src/anisotropy/model.py index 585e114..c89aaab 100644 --- a/src/anisotropy/model.py +++ b/src/anisotropy/model.py @@ -7,11 +7,11 @@ from typing import TypeAlias, cast import numpy as np -from numpy.typing import ArrayLike from si.physical import SI from .values import ( + ArrayLike, FloatArray, _callable_energy, _finite_difference_gradient, diff --git a/src/anisotropy/predefined.py b/src/anisotropy/predefined.py index 3e94eb3..d475374 100644 --- a/src/anisotropy/predefined.py +++ b/src/anisotropy/predefined.py @@ -3,10 +3,9 @@ from __future__ import annotations import numpy as np -from numpy.typing import ArrayLike from .model import EnergyDensity, PredefinedAnisotropy -from .values import FloatArray, _normalize, energy_density_value +from .values import ArrayLike, FloatArray, _normalize, energy_density_value def _constant(value: EnergyDensity, name: str) -> float: diff --git a/src/anisotropy/values.py b/src/anisotropy/values.py index 60d8a0a..b6aaf88 100644 --- a/src/anisotropy/values.py +++ b/src/anisotropy/values.py @@ -3,14 +3,19 @@ from __future__ import annotations from collections.abc import Callable -from typing import Any, cast +from typing import Any, TypeAlias, cast import numpy as np -from numpy.typing import ArrayLike, NDArray +from numpy.typing import NDArray from si.physical import SI FloatArray = NDArray[np.float64] +# NumPy's ArrayLike alias is intentionally broad, but recent NumPy stubs expose +# unresolved type variables through it under strict Pyright checking. These +# inputs are normalized immediately with np.asarray, so Any is intentional at +# this runtime-validation boundary. +ArrayLike: TypeAlias = Any RawEnergyFunction = Callable[[ArrayLike], object] _ENERGY_DENSITY_UNIT = SI(1.0, "J/m^3") diff --git a/src/nmesh/geometry/primitives.py b/src/nmesh/geometry/primitives.py index cc1e572..4210299 100644 --- a/src/nmesh/geometry/primitives.py +++ b/src/nmesh/geometry/primitives.py @@ -19,9 +19,8 @@ from typing import Literal, TypeAlias import numpy as np -from numpy.typing import ArrayLike -from ..utils.types import BoolArray, FloatArray +from ..utils.types import ArrayLike, BoolArray, FloatArray from .transform import ( AffineTransform, _as_vector, diff --git a/src/nmesh/geometry/transform.py b/src/nmesh/geometry/transform.py index 144cc89..d1734c1 100644 --- a/src/nmesh/geometry/transform.py +++ b/src/nmesh/geometry/transform.py @@ -12,10 +12,9 @@ from dataclasses import dataclass import numpy as np -from numpy.typing import ArrayLike from ..utils.constants import MIN_ABS_SCALE_FACTOR -from ..utils.types import FloatArray +from ..utils.types import ArrayLike, FloatArray def _as_vector(values: ArrayLike, dim: int | None = None) -> FloatArray: diff --git a/src/nmesh/io/meshio_support.py b/src/nmesh/io/meshio_support.py index 13758ff..37d58a0 100644 --- a/src/nmesh/io/meshio_support.py +++ b/src/nmesh/io/meshio_support.py @@ -8,9 +8,10 @@ import meshio import numpy as np -from numpy.typing import ArrayLike, NDArray +from numpy.typing import NDArray from ..backend import RawMesh +from ..utils.types import ArrayLike from .legacy_nmesh_hdf5 import ( is_legacy_nmesh_hdf5, load_raw_mesh_from_legacy_nmesh_hdf5, diff --git a/src/nmesh/utils/types.py b/src/nmesh/utils/types.py index ee16773..6bcc03f 100644 --- a/src/nmesh/utils/types.py +++ b/src/nmesh/utils/types.py @@ -1,12 +1,13 @@ """Common type aliases used throughout the nmesh package.""" -from typing import TypeAlias +from typing import Any, TypeAlias import numpy as np from numpy.typing import NDArray -__all__ = ["FloatArray", "BoolArray", "IntArray"] +__all__ = ["ArrayLike", "FloatArray", "BoolArray", "IntArray"] +ArrayLike: TypeAlias = Any FloatArray: TypeAlias = NDArray[np.float64] BoolArray: TypeAlias = NDArray[np.bool_] IntArray: TypeAlias = NDArray[np.int_]