From eb55cf7aca43135a52425a778c13540ff4884c3b Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 2 Mar 2025 05:14:07 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20stub=20`numpy.=5Fcore.o?= =?UTF-8?q?verrides`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .mypyignore | 1 + .mypyignore-todo | 2 -- src/numpy-stubs/_core/overrides.pyi | 56 +++++++++++++++++++++++++++++ src/numpy-stubs/core/__init__.pyi | 12 +++---- src/numpy-stubs/core/overrides.pyi | 7 ++++ 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/numpy-stubs/_core/overrides.pyi create mode 100644 src/numpy-stubs/core/overrides.pyi diff --git a/.mypyignore b/.mypyignore index 2d462b87..08f399a6 100644 --- a/.mypyignore +++ b/.mypyignore @@ -18,6 +18,7 @@ numpy\._core\.numerictypes\.__all__ numpy\.matlib\.__all__ # workaround mypy's lack of Python 3.13 support for `NamedTuple` types (mypy <= 1.15.0) +numpy\._core\.overrides\.ArgSpec\.__replace__ numpy\._utils\._pep440\._Version\.__replace__ numpy\.lib\._arraysetops_impl\.Unique(All|Counts|Inverse)Result\.__replace__ numpy\.linalg\._linalg\.(Eig|Eigh|QR|SVD|Slogdet)Result\.__replace__ diff --git a/.mypyignore-todo b/.mypyignore-todo index d3cdd7aa..a5bacbc9 100644 --- a/.mypyignore-todo +++ b/.mypyignore-todo @@ -88,7 +88,6 @@ numpy.ndenumerate.iter numpy.poly1d.integ numpy._core.cversions -numpy._core.overrides numpy._core.printoptions numpy._core._internal.IS_PYPY @@ -157,7 +156,6 @@ numpy.core.getlimits numpy.core.multiarray numpy.core.numeric numpy.core.numerictypes -numpy.core.overrides numpy.core.records numpy.core.shape_base numpy.core.umath diff --git a/src/numpy-stubs/_core/overrides.pyi b/src/numpy-stubs/_core/overrides.pyi new file mode 100644 index 00000000..73836a68 --- /dev/null +++ b/src/numpy-stubs/_core/overrides.pyi @@ -0,0 +1,56 @@ +from collections.abc import Callable, Iterable +from typing import Any, Final, NamedTuple, TypeAlias +from typing_extensions import ParamSpec, TypeVar + +from numpy._typing import _SupportsArrayFunc + +_T = TypeVar("_T") +_Tss = ParamSpec("_Tss") +_FuncT = TypeVar("_FuncT", bound=Callable[..., object]) + +_AnyFunc: TypeAlias = Callable[..., Any] + +### + +ARRAY_FUNCTIONS: set[Callable[..., Any]] = ... +array_function_like_doc: Final[str] = ... + +class ArgSpec(NamedTuple): + args: list[str] + varargs: str | None + keywords: str | None + defaults: tuple[Any, ...] + +def get_array_function_like_doc(public_api: Callable[..., Any], docstring_template: str = "") -> str: ... +def finalize_array_function_like(public_api: _FuncT) -> _FuncT: ... + +# +def verify_matching_signatures( + implementation: Callable[_Tss, object], + dispatcher: Callable[_Tss, Iterable[_SupportsArrayFunc]], +) -> None: ... + +# NOTE: This actually returns a `_ArrayFunctionDispatcher` callable wrapper object, with +# the original wrapped callable stored in the `._implementation` attribute. It checks +# for any `__array_function__` of the values of specific arguments that the dispatcher +# specifies. Since the dispatcher only returns an iterable of passed array-like argument, +# this overridable behaviour is impossible to annotate. This behaviour should therefore +# be annotated for each function that's wrapped with this decorator (also, currently +# it's not allowed to use custom decorators in stubs, all because *a particular +# type-checker maintainer* keeps whining about how difficult it would be for him to +# implement, ignoring the many hours that I and other stub maintainers are now losing +# because of this completely unreasonable restriction). +def array_function_dispatch( + dispatcher: Callable[_Tss, Iterable[_SupportsArrayFunc]] | None = None, + module: str | None = None, + verify: bool = True, + docs_from_dispatcher: bool = False, +) -> Callable[[_FuncT], _FuncT]: ... + +# +def array_function_from_dispatcher( + implementation: Callable[_Tss, _T], + module: str | None = None, + verify: bool = True, + docs_from_dispatcher: bool = True, +) -> Callable[[Callable[_Tss, Iterable[_SupportsArrayFunc]]], Callable[_Tss, _T]]: ... diff --git a/src/numpy-stubs/core/__init__.pyi b/src/numpy-stubs/core/__init__.pyi index 2399f660..d21e5e85 100644 --- a/src/numpy-stubs/core/__init__.pyi +++ b/src/numpy-stubs/core/__init__.pyi @@ -1,11 +1,12 @@ +# TODO(jorenham): +# add mirror modules of `_core` in the `core` namespace + from numpy import _core as _core # noqa: ICN003 from numpy._core import ( # _dtype, # _dtype_ctypes, _internal, _multiarray_umath, - # TODO(jorenham): - # add mirror modules of `_core` in the `core` namespace arrayprint, defchararray, einsumfunc, @@ -15,7 +16,7 @@ from numpy._core import ( multiarray, numeric, numerictypes, - # overrides, + overrides, records, shape_base, umath, @@ -35,15 +36,12 @@ __all__ = [ "multiarray", "numeric", "numerictypes", - # "overrides", + "overrides", "records", "shape_base", "umath", ] -# TODO(jorenham): stub `_core.overrides` -# https://github.com/numpy/numtype/issues/48 - # TODO(jorenham): stub `_core._dtype_ctypes` # https://github.com/numpy/numtype/issues/54 diff --git a/src/numpy-stubs/core/overrides.pyi b/src/numpy-stubs/core/overrides.pyi new file mode 100644 index 00000000..fab35126 --- /dev/null +++ b/src/numpy-stubs/core/overrides.pyi @@ -0,0 +1,7 @@ +# NOTE: At runtime, this submodule dynamically re-exports any `numpy._core.overrides` +# member, and issues a `DeprecationWarning` when accessed. But since there is no +# `__dir__` or `__all__` present, these annotations would be unverifiable. Because +# this module is also deprecated in favor of `numpy._core`, and therefore not part of +# the public API, we omit the "re-exports", which in practice would require literal +# duplication of the stubs in order for the `@deprecated` decorator to be understood +# by type-checkers.