From d68fd011f443418bba9613639e67a5b079f8b1cb Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Tue, 18 Nov 2025 11:33:28 +0100 Subject: [PATCH 1/5] chore: SPEC0 update (Python 3.12+) --- pyproject.toml | 3 +- src/fast_array_utils/stats/_generic_ops.py | 4 +- src/fast_array_utils/stats/_is_constant.py | 5 +-- src/fast_array_utils/stats/_power.py | 11 ++---- src/fast_array_utils/stats/_typing.py | 21 +++++------ src/fast_array_utils/stats/_utils.py | 11 ++---- src/fast_array_utils/types.py | 4 +- src/fast_array_utils/typing.py | 12 ++---- src/testing/fast_array_utils/_array_type.py | 35 +++++++++--------- tests/test_to_dense.py | 6 +-- typings/dask/array/core.pyi | 7 ++-- typings/dask/array/overlap.pyi | 8 ++-- typings/dask/array/wrap.pyi | 4 +- typings/numba/__init__.pyi | 13 +++---- typings/numba/core/datamodel/new_models.pyi | 11 ++---- typings/numba/core/datamodel/registry.pyi | 5 +-- typings/numba/core/extending.pyi | 41 +++++++++------------ typings/numba/core/types.pyi | 4 +- 18 files changed, 85 insertions(+), 120 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index edc5f27..e9139ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,12 +14,11 @@ license = "MPL-2.0" authors = [ { name = "Philipp A.", email = "flying-sheep@web.de" }, ] -requires-python = ">=3.11" +requires-python = ">=3.12" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", diff --git a/src/fast_array_utils/stats/_generic_ops.py b/src/fast_array_utils/stats/_generic_ops.py index ef833ed..79df974 100644 --- a/src/fast_array_utils/stats/_generic_ops.py +++ b/src/fast_array_utils/stats/_generic_ops.py @@ -12,14 +12,14 @@ if TYPE_CHECKING: - from typing import Any, Literal, TypeAlias + from typing import Any, Literal from numpy.typing import DTypeLike, NDArray from ..typing import CpuArray, DiskArray, GpuArray from ._typing import Ops - ComplexAxis: TypeAlias = tuple[Literal[0], Literal[1]] | tuple[Literal[0, 1]] | Literal[0, 1] | None + type ComplexAxis = tuple[Literal[0], Literal[1]] | tuple[Literal[0, 1]] | Literal[0, 1] | None def _run_numpy_op( diff --git a/src/fast_array_utils/stats/_is_constant.py b/src/fast_array_utils/stats/_is_constant.py index 4df5efd..1ac95d3 100644 --- a/src/fast_array_utils/stats/_is_constant.py +++ b/src/fast_array_utils/stats/_is_constant.py @@ -11,13 +11,10 @@ if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any, Literal, TypeVar + from typing import Any, Literal from numpy.typing import NDArray - C = TypeVar("C", bound=Callable[..., Any]) - @singledispatch def is_constant_( diff --git a/src/fast_array_utils/stats/_power.py b/src/fast_array_utils/stats/_power.py index 43dada2..8387836 100644 --- a/src/fast_array_utils/stats/_power.py +++ b/src/fast_array_utils/stats/_power.py @@ -10,20 +10,15 @@ if TYPE_CHECKING: - from typing import TypeAlias, TypeVar - from numpy.typing import DTypeLike from fast_array_utils.typing import CpuArray, GpuArray # All supported array types except for disk ones and CSDataset - Array: TypeAlias = CpuArray | GpuArray | types.DaskArray - - _Arr = TypeVar("_Arr", bound=Array) - _Mat = TypeVar("_Mat", bound=types.CSBase | types.CupyCSMatrix) + type Array = CpuArray | GpuArray | types.DaskArray -def power(x: _Arr, n: int, /, dtype: DTypeLike | None = None) -> _Arr: +def power[Arr: Array](x: Arr, n: int, /, dtype: DTypeLike | None = None) -> Arr: """Take array or matrix to a power.""" # This wrapper is necessary because TypeVars can’t be used in `singledispatch` functions return _power(x, n, dtype=dtype) # type: ignore[return-value] @@ -37,7 +32,7 @@ def _power(x: Array, n: int, /, dtype: DTypeLike | None = None) -> Array: @_power.register(types.CSBase | types.CupyCSMatrix) -def _power_cs(x: _Mat, n: int, /, dtype: DTypeLike | None = None) -> _Mat: +def _power_cs[Mat: types.CSBase | types.CupyCSMatrix](x: Mat, n: int, /, dtype: DTypeLike | None = None) -> Mat: new_data = power(x.data, n, dtype=dtype) return type(x)((new_data, x.indices, x.indptr), shape=x.shape, dtype=new_data.dtype) # type: ignore[call-overload,return-value] diff --git a/src/fast_array_utils/stats/_typing.py b/src/fast_array_utils/stats/_typing.py index 2ddc4c0..be671dd 100644 --- a/src/fast_array_utils/stats/_typing.py +++ b/src/fast_array_utils/stats/_typing.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MPL-2.0 from __future__ import annotations -from typing import TYPE_CHECKING, Generic, Literal, Protocol, TypedDict, TypeVar +from typing import TYPE_CHECKING, Literal, Protocol, TypedDict import numpy as np @@ -11,17 +11,17 @@ if TYPE_CHECKING: - from typing import Any, TypeAlias + from typing import Any from numpy.typing import DTypeLike, NDArray -Array: TypeAlias = CpuArray | GpuArray | DiskArray | types.CSDataset | types.DaskArray +type Array = CpuArray | GpuArray | DiskArray | types.CSDataset | types.DaskArray -DTypeIn: TypeAlias = np.float32 | np.float64 | np.int32 | np.bool_ -DTypeOut: TypeAlias = np.float32 | np.float64 | np.int64 +type DTypeIn = np.float32 | np.float64 | np.int32 | np.bool_ +type DTypeOut = np.float32 | np.float64 | np.int64 -NdAndAx: TypeAlias = tuple[Literal[1], None] | tuple[Literal[2], Literal[0, 1] | None] +type NdAndAx = tuple[Literal[1], None] | tuple[Literal[2], Literal[0, 1] | None] class StatFunNoDtype(Protocol): @@ -48,11 +48,8 @@ def __call__( NoDtypeOps = Literal["max", "min"] DtypeOps = Literal["sum"] -Ops: TypeAlias = NoDtypeOps | DtypeOps +type Ops = NoDtypeOps | DtypeOps -_DT = TypeVar("_DT", bound="DTypeLike") - - -class DTypeKw(TypedDict, Generic[_DT], total=False): - dtype: _DT +class DTypeKw[DT: DTypeLike](TypedDict, total=False): + dtype: DT diff --git a/src/fast_array_utils/stats/_utils.py b/src/fast_array_utils/stats/_utils.py index 1ca4065..d61a04c 100644 --- a/src/fast_array_utils/stats/_utils.py +++ b/src/fast_array_utils/stats/_utils.py @@ -2,7 +2,7 @@ from __future__ import annotations from functools import partial -from typing import TYPE_CHECKING, Literal, TypeVar, cast, get_args +from typing import TYPE_CHECKING, Literal, cast, get_args import numpy as np from numpy.exceptions import AxisError @@ -13,14 +13,14 @@ if TYPE_CHECKING: - from typing import Any, Literal, TypeAlias + from typing import Any, Literal from numpy.typing import DTypeLike, NDArray from ..typing import CpuArray from ._typing import DTypeKw, Ops - ComplexAxis: TypeAlias = tuple[Literal[0], Literal[1]] | tuple[Literal[0, 1]] | Literal[0, 1] | None + type ComplexAxis = tuple[Literal[0], Literal[1]] | tuple[Literal[0, 1]] | Literal[0, 1] | None __all__ = ["_dask_inner"] @@ -114,8 +114,5 @@ def _get_shape(a: NDArray[Any] | np.number[Any] | types.CupyArray, *, axis: Lite raise AssertionError(msg) -DT = TypeVar("DT", bound="DTypeLike") - - -def _dtype_kw(dtype: DT | None, op: Ops) -> DTypeKw[DT]: +def _dtype_kw[DT: DTypeLike](dtype: DT | None, op: Ops) -> DTypeKw[DT]: return {"dtype": dtype} if dtype is not None and op in get_args(DtypeOps) else {} diff --git a/src/fast_array_utils/types.py b/src/fast_array_utils/types.py index 6269c69..41e6c6f 100644 --- a/src/fast_array_utils/types.py +++ b/src/fast_array_utils/types.py @@ -4,7 +4,7 @@ from __future__ import annotations from importlib.util import find_spec -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING __all__ = [ @@ -34,8 +34,6 @@ "spmatrix", ] -T_co = TypeVar("T_co", covariant=True) - # scipy sparse if TYPE_CHECKING: diff --git a/src/fast_array_utils/typing.py b/src/fast_array_utils/typing.py index af7238b..edc90d8 100644 --- a/src/fast_array_utils/typing.py +++ b/src/fast_array_utils/typing.py @@ -3,26 +3,22 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import Any from numpy.typing import NDArray from . import types -if TYPE_CHECKING: - from typing import TypeAlias - - __all__ = ["CpuArray", "DiskArray", "GpuArray"] -CpuArray: TypeAlias = NDArray[Any] | types.CSBase +CpuArray = NDArray[Any] | types.CSBase """Arrays and matrices stored in CPU memory.""" -GpuArray: TypeAlias = types.CupyArray | types.CupyCSMatrix +GpuArray = types.CupyArray | types.CupyCSMatrix """Arrays and matrices stored in GPU memory.""" # TODO(flying-sheep): types.CSDataset # noqa: TD003 -DiskArray: TypeAlias = types.H5Dataset | types.ZarrArray +DiskArray = types.H5Dataset | types.ZarrArray """Arrays and matrices stored on disk.""" diff --git a/src/testing/fast_array_utils/_array_type.py b/src/testing/fast_array_utils/_array_type.py index 48c0280..d3cac83 100644 --- a/src/testing/fast_array_utils/_array_type.py +++ b/src/testing/fast_array_utils/_array_type.py @@ -4,6 +4,7 @@ from __future__ import annotations import enum +import sys from dataclasses import KW_ONLY, dataclass, field from functools import cached_property, partial from importlib.metadata import version @@ -17,23 +18,18 @@ if TYPE_CHECKING: - from typing import Any, Literal, Protocol, TypeAlias + from typing import Any, Literal, Protocol import h5py from numpy.typing import ArrayLike, DTypeLike, NDArray from fast_array_utils.typing import CpuArray, DiskArray, GpuArray - InnerArray = CpuArray | GpuArray | DiskArray - Array: TypeAlias = InnerArray | types.DaskArray | types.CSDataset - ExtendedArray = Array | types.COOBase | types.CupyCOOMatrix + type InnerArray = CpuArray | GpuArray | DiskArray + type Array = InnerArray | types.DaskArray | types.CSDataset + type ExtendedArray = Array | types.COOBase | types.CupyCOOMatrix - Arr = TypeVar("Arr", bound=ExtendedArray, default=Array) - Arr_co = TypeVar("Arr_co", bound=ExtendedArray, covariant=True) - - Inner = TypeVar("Inner", bound="ArrayType[InnerArray, None] | None", default=Any) - - class ToArray(Protocol, Generic[Arr_co]): + class ToArray[Arr_co: ExtendedArray](Protocol): """Convert to a supported array.""" def __call__(self, data: ArrayLike | Array, /, *, dtype: DTypeLike | None = None) -> Arr_co: ... @@ -41,14 +37,11 @@ def __call__(self, data: ArrayLike | Array, /, *, dtype: DTypeLike | None = None class MkArray(Protocol): def __call__(self, shape: tuple[int, int], /, *, dtype: DTypeLike | None = None) -> Array: ... - _DTypeLikeFloat32 = np.dtype[np.float32] | type[np.float32] - _DTypeLikeFloat64 = np.dtype[np.float64] | type[np.float64] - _DTypeLikeInt32 = np.dtype[np.int32] | type[np.int32] - _DTypeLikeIn64 = np.dtype[np.int64] | type[np.int64] - _DTypeLikeNum = _DTypeLikeFloat32 | _DTypeLikeFloat64 | _DTypeLikeInt32 | _DTypeLikeIn64 -else: - Arr = TypeVar("Arr") - Inner = TypeVar("Inner") + type _DTypeLikeFloat32 = np.dtype[np.float32] | type[np.float32] + type _DTypeLikeFloat64 = np.dtype[np.float64] | type[np.float64] + type _DTypeLikeInt32 = np.dtype[np.int32] | type[np.int32] + type _DTypeLikeIn64 = np.dtype[np.int64] | type[np.int64] + type _DTypeLikeNum = _DTypeLikeFloat32 | _DTypeLikeFloat64 | _DTypeLikeInt32 | _DTypeLikeIn64 __all__ = ["ArrayType", "ConversionContext", "ToArray"] @@ -81,6 +74,12 @@ class ConversionContext: hdf5_file: h5py.File # TODO(flying-sheep): ReadOnly +if sys.version_info < (3, 13): + pass # TODO(flying-sheep): move vars into type parameter syntax # noqa: TD003 +Arr = TypeVar("Arr", bound="ExtendedArray", default="Array") +Inner = TypeVar("Inner", bound="ArrayType[InnerArray, None] | None", default="Any") + + @dataclass(frozen=True) class ArrayType(Generic[Arr, Inner]): """Supported array type with methods for conversion and random generation. diff --git a/tests/test_to_dense.py b/tests/test_to_dense.py index a7b5692..1d75969 100644 --- a/tests/test_to_dense.py +++ b/tests/test_to_dense.py @@ -14,13 +14,13 @@ if TYPE_CHECKING: from collections.abc import Iterable - from typing import Literal, TypeAlias + from typing import Literal from fast_array_utils.typing import CpuArray, DiskArray, GpuArray from testing.fast_array_utils import ArrayType - Array: TypeAlias = CpuArray | GpuArray | DiskArray | types.CSDataset | types.DaskArray - ExtendedArray: TypeAlias = Array | types.COOBase | types.CupyCOOMatrix + type Array = CpuArray | GpuArray | DiskArray | types.CSDataset | types.DaskArray + type ExtendedArray = Array | types.COOBase | types.CupyCOOMatrix WARNS_NUMBA = pytest.warns(RuntimeWarning, match="numba is not installed; falling back to slow conversion") diff --git a/typings/dask/array/core.pyi b/typings/dask/array/core.pyi index dba0e6c..7f65dd2 100644 --- a/typings/dask/array/core.pyi +++ b/typings/dask/array/core.pyi @@ -1,19 +1,18 @@ # SPDX-License-Identifier: MPL-2.0 # pyright: reportIncompatibleMethodOverride=false from collections.abc import Callable, Sequence -from typing import Any, Literal, Never, TypeAlias +from typing import Any, Literal, Never, override import cupy import cupyx.scipy.sparse import numpy as np import scipy.sparse from numpy.typing import DTypeLike, NDArray -from typing_extensions import override from ..utils import SerializableLock -_Chunks: TypeAlias = tuple[int, ...] | tuple[tuple[int, ...], ...] -_Array: TypeAlias = ( +type _Chunks = tuple[int, ...] | tuple[tuple[int, ...], ...] +type _Array = ( NDArray[Any] | scipy.sparse.csr_array | scipy.sparse.csc_array diff --git a/typings/dask/array/overlap.pyi b/typings/dask/array/overlap.pyi index 73d00ad..ce1a65a 100644 --- a/typings/dask/array/overlap.pyi +++ b/typings/dask/array/overlap.pyi @@ -1,12 +1,12 @@ # SPDX-License-Identifier: MPL-2.0 from collections.abc import Callable -from typing import Literal, TypeAlias +from typing import Literal from .core import Array, _Array -_Depth: TypeAlias = int | tuple[int, ...] | dict[int, _Depth] -_Boundary: TypeAlias = Literal["reflect", "periodic", "nearest", "none"] | int -_Boundaries: TypeAlias = _Boundary | tuple[_Boundary, ...] | dict[int, _Boundary] +type _Depth = int | tuple[int, ...] | dict[int, _Depth] +type _Boundary = Literal["reflect", "periodic", "nearest", "none"] | int +type _Boundaries = _Boundary | tuple[_Boundary, ...] | dict[int, _Boundary] def map_overlap( func: Callable[[_Array], _Array], diff --git a/typings/dask/array/wrap.pyi b/typings/dask/array/wrap.pyi index 497950d..1559667 100644 --- a/typings/dask/array/wrap.pyi +++ b/typings/dask/array/wrap.pyi @@ -1,13 +1,13 @@ # SPDX-License-Identifier: MPL-2.0 -from typing import Any, Literal, TypeAlias +from typing import Any, Literal import numpy as np from numpy.typing import ArrayLike, DTypeLike from .core import Array, _Chunks -_Order: TypeAlias = Literal["C", "F", "A", "K"] +type _Order = Literal["C", "F", "A", "K"] def full( a: ArrayLike | None = None, diff --git a/typings/numba/__init__.pyi b/typings/numba/__init__.pyi index 016adff..c3a1395 100644 --- a/typings/numba/__init__.pyi +++ b/typings/numba/__init__.pyi @@ -1,18 +1,17 @@ # SPDX-License-Identifier: MPL-2.0 from collections.abc import Callable, Iterable -from typing import Literal, SupportsIndex, TypeAlias, TypeVar, overload +from typing import Literal, SupportsIndex, overload from .core.types import * -_F = TypeVar("_F", bound=Callable[..., object]) - -_Signature: TypeAlias = str | Type | tuple[_Signature, ...] +type __Signature = str | Type +type _Signature = str | Type | tuple[__Signature, ...] # https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#numba.jit @overload -def njit(f: _F) -> _F: ... +def njit[F: Callable[..., object]](f: F) -> F: ... @overload -def njit( +def njit[F: Callable[..., object]]( signature: _Signature | list[_Signature] | None = None, *, nopython: bool = True, @@ -24,7 +23,7 @@ def njit( fastmath: bool = False, locals: dict[str, object] = {}, boundscheck: bool = False, -) -> Callable[[_F], _F]: ... +) -> Callable[[F], F]: ... @overload def prange(stop: SupportsIndex, /) -> Iterable[int]: ... @overload diff --git a/typings/numba/core/datamodel/new_models.pyi b/typings/numba/core/datamodel/new_models.pyi index dd5c0a0..3b79798 100644 --- a/typings/numba/core/datamodel/new_models.pyi +++ b/typings/numba/core/datamodel/new_models.pyi @@ -1,14 +1,11 @@ # SPDX-License-Identifier: MPL-2.0 from collections.abc import Iterable -from typing import Generic, TypeVar from ..types import Type from .manager import DataModelManager -_T = TypeVar("_T", bound=Type) +class DataModel[T: Type]: ... +class CompositeModel[T: Type](DataModel[T]): ... -class DataModel(Generic[_T]): ... -class CompositeModel(DataModel[_T], Generic[_T]): ... - -class StructModel(CompositeModel[_T], Generic[_T]): - def __init__(self, dmm: DataModelManager, fe_type: _T, members: Iterable[tuple[str, Type]]) -> None: ... +class StructModel[T: Type](CompositeModel[T]): + def __init__(self, dmm: DataModelManager, fe_type: T, members: Iterable[tuple[str, Type]]) -> None: ... diff --git a/typings/numba/core/datamodel/registry.pyi b/typings/numba/core/datamodel/registry.pyi index 3ff8ac3..d15ce3b 100644 --- a/typings/numba/core/datamodel/registry.pyi +++ b/typings/numba/core/datamodel/registry.pyi @@ -1,9 +1,6 @@ # SPDX-License-Identifier: MPL-2.0 from collections.abc import Callable -from typing import TypeVar from ..types import Type -_F = TypeVar("_F", bound=Callable[..., object]) - -def register_default(typecls: type[Type]) -> Callable[[_F], _F]: ... +def register_default[F: Callable[..., object]](typecls: type[Type]) -> Callable[[F], F]: ... diff --git a/typings/numba/core/extending.pyi b/typings/numba/core/extending.pyi index bc9a639..5f6119f 100644 --- a/typings/numba/core/extending.pyi +++ b/typings/numba/core/extending.pyi @@ -2,7 +2,7 @@ import typing from collections.abc import Callable from dataclasses import dataclass -from typing import Concatenate, ParamSpec, Protocol, TypeVar +from typing import Concatenate, Protocol from llvmlite.ir import IRBuilder, Value from numba.core.base import BaseContext @@ -28,11 +28,6 @@ __all__ = [ "unbox", ] -_F = TypeVar("_F", bound=Callable[..., object]) -_P = ParamSpec("_P") -_R = TypeVar("_R") -_T = TypeVar("_T", bound=Type) - TypingContext = object class _Context(Protocol): @@ -55,33 +50,33 @@ class NativeValue: is_error: Value = ... cleanup: Value | None = None -def box( - typeclass: type[_T], +def box[T: Type, R]( + typeclass: type[T], ) -> Callable[ - [Callable[[_T, NativeValue, BoxContext], _R]], - Callable[[_T, NativeValue, BoxContext], _R], + [Callable[[T, NativeValue, BoxContext], R]], + Callable[[T, NativeValue, BoxContext], R], ]: ... -def unbox( - typeclass: type[_T], +def unbox[T: Type]( + typeclass: type[T], ) -> Callable[ - [Callable[[_T, Value, UnboxContext], NativeValue]], - Callable[[_T, Value, UnboxContext], NativeValue], + [Callable[[T, Value, UnboxContext], NativeValue]], + Callable[[T, Value, UnboxContext], NativeValue], ]: ... @typing.overload -def intrinsic( - func: Callable[Concatenate[TypingContext, _P], tuple[Signature, Callable[..., NativeValue]]], +def intrinsic[**P]( + func: Callable[Concatenate[TypingContext, P], tuple[Signature, Callable[..., NativeValue]]], /, -) -> Callable[_P, object]: ... +) -> Callable[P, object]: ... @typing.overload -def intrinsic( +def intrinsic[**P]( *, prefer_literal: bool = False, **kwargs: object, ) -> Callable[ - [Callable[Concatenate[TypingContext, _P], tuple[Signature, Callable[..., NativeValue]]]], - Callable[_P, object], + [Callable[Concatenate[TypingContext, P], tuple[Signature, Callable[..., NativeValue]]]], + Callable[P, object], ]: ... def make_attribute_wrapper(typeclass: type[Type], struct_attr: str, python_attr: str) -> None: ... -def overload(f: Callable[..., object]) -> Callable[[_F], _F]: ... -def overload_method(typecls: type[Type], name: str) -> Callable[[_F], _F]: ... -def overload_attribute(typecls: type[Type], name: str) -> Callable[[_F], _F]: ... +def overload[F: Callable[..., object]](f: Callable[..., object]) -> Callable[[F], F]: ... +def overload_method[F: Callable[..., object]](typecls: type[Type], name: str) -> Callable[[F], F]: ... +def overload_attribute[F: Callable[..., object]](typecls: type[Type], name: str) -> Callable[[F], F]: ... diff --git a/typings/numba/core/types.pyi b/typings/numba/core/types.pyi index 2823b70..0660356 100644 --- a/typings/numba/core/types.pyi +++ b/typings/numba/core/types.pyi @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MPL-2.0 # See -from typing import Literal, Self, TypeAlias +from typing import Literal, Self from numba.core.typing.templates import Signature @@ -48,7 +48,7 @@ class ArrayCompatible(Type): dtype: Type class Buffer(IterableType, ArrayCompatible): - Layout: TypeAlias = Literal["C", "F", "CS", "FS", "A"] + type Layout = Literal["C", "F", "CS", "FS", "A"] LAYOUTS: frozenset[Layout] def copy( self, From 01d9e6067dc213c3a5bcbf3450926f445118677f Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 18 Nov 2025 12:12:20 +0100 Subject: [PATCH 2/5] Use type alias in typing docs --- docs/conf.py | 9 +++++++-- pyproject.toml | 4 ++-- src/fast_array_utils/stats/_utils.py | 5 ++--- src/fast_array_utils/typing.py | 13 +++++++++---- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 1c04e72..657de4e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,16 +9,20 @@ from typing import TYPE_CHECKING from docutils.nodes import Text +from sphinx.util import logging if TYPE_CHECKING: + from typing import Final + from docutils.nodes import TextElement, reference from sphinx.addnodes import pending_xref from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment -HERE = Path(__file__).parent +HERE: Final = Path(__file__).parent +LOGGER: Final = logging.getLogger("fast_array_utils") # -- General configuration ------------------------------------------------ @@ -134,7 +138,8 @@ def resolve_type_aliases(app: Sphinx, env: BuildEnvironment, node: pending_xref, ref = resolve_reference_any_inventory(env=env, honor_disabled_refs=False, node=node, contnode=contnode) if ref is None: msg = f"Could not resolve {typ} {target} (from {node['reftarget']})" - raise AssertionError(msg) + LOGGER.warning(msg, type="ref") + return ref if name: ref.children[:] = [Text(name)] return ref diff --git a/pyproject.toml b/pyproject.toml index e9139ee..ae26151 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,11 +106,11 @@ overrides.matrix.resolution.dependencies = [ ] [[tool.hatch.envs.hatch-test.matrix]] -python = [ "3.13", "3.11" ] +python = [ "3.13", "3.12" ] extras = [ "full", "min" ] [[tool.hatch.envs.hatch-test.matrix]] -python = [ "3.11" ] +python = [ "3.12" ] extras = [ "full" ] resolution = [ "lowest" ] diff --git a/src/fast_array_utils/stats/_utils.py b/src/fast_array_utils/stats/_utils.py index d61a04c..bc1f650 100644 --- a/src/fast_array_utils/stats/_utils.py +++ b/src/fast_array_utils/stats/_utils.py @@ -8,7 +8,6 @@ from numpy.exceptions import AxisError from .. import types -from ..typing import GpuArray from ._typing import DtypeOps @@ -17,7 +16,7 @@ from numpy.typing import DTypeLike, NDArray - from ..typing import CpuArray + from ..typing import CpuArray, GpuArray from ._typing import DTypeKw, Ops type ComplexAxis = tuple[Literal[0], Literal[1]] | tuple[Literal[0, 1]] | Literal[0, 1] | None @@ -71,7 +70,7 @@ def _dask_block( from . import max, min, sum if computing_meta: # dask.blockwise doesn’t allow to pass `meta` in, and reductions below don’t handle a 0d matrix - return (types.CupyArray if isinstance(a, GpuArray) else np.ndarray)((), dtype or a.dtype) + return (types.CupyArray if isinstance(a, types.CupyArray | types.CupyCSMatrix) else np.ndarray)((), dtype or a.dtype) fns = {fn.__name__: fn for fn in (min, max, sum)} diff --git a/src/fast_array_utils/typing.py b/src/fast_array_utils/typing.py index edc90d8..aebcdbb 100644 --- a/src/fast_array_utils/typing.py +++ b/src/fast_array_utils/typing.py @@ -3,22 +3,27 @@ from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from numpy.typing import NDArray from . import types +if TYPE_CHECKING: + from typing import TypeAlias + + __all__ = ["CpuArray", "DiskArray", "GpuArray"] +# change to `type` syntax once this is released: https://github.com/sphinx-doc/sphinx/pull/13508 -CpuArray = NDArray[Any] | types.CSBase +CpuArray: TypeAlias = NDArray[Any] | types.CSBase # noqa: UP040 """Arrays and matrices stored in CPU memory.""" -GpuArray = types.CupyArray | types.CupyCSMatrix +GpuArray: TypeAlias = types.CupyArray | types.CupyCSMatrix # noqa: UP040 """Arrays and matrices stored in GPU memory.""" # TODO(flying-sheep): types.CSDataset # noqa: TD003 -DiskArray = types.H5Dataset | types.ZarrArray +DiskArray: TypeAlias = types.H5Dataset | types.ZarrArray # noqa: UP040 """Arrays and matrices stored on disk.""" From 0a3669d772c290b4bf98de4048bfc14fa53efc6b Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 18 Nov 2025 12:15:58 +0100 Subject: [PATCH 3/5] Use type aliases --- src/fast_array_utils/conv/__init__.py | 2 +- src/fast_array_utils/conv/_to_dense.py | 3 ++- src/fast_array_utils/stats/__init__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fast_array_utils/conv/__init__.py b/src/fast_array_utils/conv/__init__.py index 58afd56..6a34ca0 100644 --- a/src/fast_array_utils/conv/__init__.py +++ b/src/fast_array_utils/conv/__init__.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, overload -from ..typing import CpuArray, DiskArray, GpuArray # noqa: TC001 from ._to_dense import to_dense_ @@ -15,6 +14,7 @@ from numpy.typing import NDArray from .. import types + from ..typing import CpuArray, DiskArray, GpuArray __all__ = ["to_dense"] diff --git a/src/fast_array_utils/conv/_to_dense.py b/src/fast_array_utils/conv/_to_dense.py index 249ec07..6b5de88 100644 --- a/src/fast_array_utils/conv/_to_dense.py +++ b/src/fast_array_utils/conv/_to_dense.py @@ -8,7 +8,6 @@ import numpy as np from .. import types -from ..typing import CpuArray, DiskArray, GpuArray # noqa: TC001 if TYPE_CHECKING: @@ -16,6 +15,8 @@ from numpy.typing import NDArray + from ..typing import CpuArray, DiskArray, GpuArray + # fallback’s arg0 type has to include types of registered functions @singledispatch diff --git a/src/fast_array_utils/stats/__init__.py b/src/fast_array_utils/stats/__init__.py index 8010444..24712d8 100644 --- a/src/fast_array_utils/stats/__init__.py +++ b/src/fast_array_utils/stats/__init__.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, cast, get_args, overload from .._validation import validate_axis -from ..typing import CpuArray, DiskArray, GpuArray # noqa: TC001 from ._generic_ops import DtypeOps @@ -22,6 +21,7 @@ from optype.numpy import ToDType from .. import types + from ..typing import CpuArray, DiskArray, GpuArray from ._generic_ops import Ops from ._typing import NoDtypeOps, StatFunDtype, StatFunNoDtype From 115154983ac301b511c743540d6ec9cc3fab742b Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 18 Nov 2025 12:23:16 +0100 Subject: [PATCH 4/5] fix typevar syntax --- .readthedocs.yml | 2 +- src/testing/fast_array_utils/_array_type.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index ca1d026..f32ea6c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -2,7 +2,7 @@ version: 2 build: os: ubuntu-24.04 tools: - python: "3.12" + python: "3.13" python: install: - method: pip diff --git a/src/testing/fast_array_utils/_array_type.py b/src/testing/fast_array_utils/_array_type.py index d3cac83..5ba0855 100644 --- a/src/testing/fast_array_utils/_array_type.py +++ b/src/testing/fast_array_utils/_array_type.py @@ -74,14 +74,17 @@ class ConversionContext: hdf5_file: h5py.File # TODO(flying-sheep): ReadOnly -if sys.version_info < (3, 13): - pass # TODO(flying-sheep): move vars into type parameter syntax # noqa: TD003 -Arr = TypeVar("Arr", bound="ExtendedArray", default="Array") -Inner = TypeVar("Inner", bound="ArrayType[InnerArray, None] | None", default="Any") +if sys.version_info >= (3, 13): + # TODO(flying-sheep): move vars into type parameter syntax # noqa: TD003 + Arr = TypeVar("Arr", bound="ExtendedArray", default="Array") + Inner = TypeVar("Inner", bound="ArrayType[InnerArray, None] | None", default="Any") +else: + Arr = TypeVar("Arr") + Inner = TypeVar("Inner") @dataclass(frozen=True) -class ArrayType(Generic[Arr, Inner]): +class ArrayType(Generic[Arr, Inner]): # noqa: UP046 """Supported array type with methods for conversion and random generation. Examples From 7405cc3fbcfeb05d18d92fe8c5112447e7142c2a Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 18 Nov 2025 12:29:57 +0100 Subject: [PATCH 5/5] fix more harderer --- .github/workflows/ci-gpu.yml | 4 ++-- .github/workflows/ci.yml | 6 +++--- .github/workflows/publish.yml | 4 ++-- src/testing/fast_array_utils/_array_type.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-gpu.yml b/.github/workflows/ci-gpu.yml index d2a2e95..3c3e193 100644 --- a/.github/workflows/ci-gpu.yml +++ b/.github/workflows/ci-gpu.yml @@ -43,9 +43,9 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" - - uses: hynek/setup-cached-uv@v2 + - uses: astral-sh/setup-uv@v6 with: - cache-dependency-path: pyproject.toml + enable-cache: true # "auto" is `false` on non-GitHub runners - name: Install package run: uv pip install --system -e .[test,full] cupy-cuda12x --extra-index-url=https://pypi.nvidia.com --index-strategy=unsafe-best-match - name: List installed packages diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d449dec..a4e24fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: steps: - uses: actions/checkout@v5 with: { fetch-depth: 0, filter: "blob:none" } - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.13' - uses: astral-sh/setup-uv@v7 @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v5 with: { fetch-depth: 0, filter: "blob:none" } - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.13' - uses: astral-sh/setup-uv@v7 @@ -110,7 +110,7 @@ jobs: steps: - uses: actions/checkout@v5 with: { fetch-depth: 0, filter: "blob:none" } - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 74c5322..d559a51 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,8 +13,8 @@ jobs: permissions: id-token: write # to authenticate as Trusted Publisher to pypi.org steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" cache: pip diff --git a/src/testing/fast_array_utils/_array_type.py b/src/testing/fast_array_utils/_array_type.py index 5ba0855..b5649a3 100644 --- a/src/testing/fast_array_utils/_array_type.py +++ b/src/testing/fast_array_utils/_array_type.py @@ -74,7 +74,7 @@ class ConversionContext: hdf5_file: h5py.File # TODO(flying-sheep): ReadOnly -if sys.version_info >= (3, 13): +if TYPE_CHECKING or sys.version_info >= (3, 13): # TODO(flying-sheep): move vars into type parameter syntax # noqa: TD003 Arr = TypeVar("Arr", bound="ExtendedArray", default="Array") Inner = TypeVar("Inner", bound="ArrayType[InnerArray, None] | None", default="Any")