diff options
Diffstat (limited to 'numpy')
101 files changed, 4211 insertions, 1443 deletions
diff --git a/numpy/__init__.py b/numpy/__init__.py index baff5e141..8546238ec 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -383,10 +383,10 @@ else: error_message = "{}: {}".format(w[-1].category.__name__, str(w[-1].message)) msg = ( "Polyfit sanity test emitted a warning, most likely due " - "to using a buggy Accelerate backend. If you compiled " - "yourself, more information is available at " - "https://numpy.org/doc/stable/user/building.html#accelerated-blas-lapack-libraries " - "Otherwise report this to the vendor " + "to using a buggy Accelerate backend." + "\nIf you compiled yourself, more information is available at:" + "\nhttps://numpy.org/doc/stable/user/building.html#accelerated-blas-lapack-libraries" + "\nOtherwise report this to the vendor " "that provided NumPy.\n{}\n".format(error_message)) raise RuntimeError(msg) del _mac_os_check diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 0245ffd9a..826bbcb3c 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1,9 +1,11 @@ import builtins import os import sys +import mmap +import array as _array import datetime as dt from abc import abstractmethod -from types import TracebackType +from types import TracebackType, MappingProxyType from contextlib import ContextDecorator from numpy.core._internal import _ctypes @@ -252,10 +254,6 @@ from numpy.core.fromnumeric import ( ) from numpy.core._asarray import ( - asarray as asarray, - asanyarray as asanyarray, - ascontiguousarray as ascontiguousarray, - asfortranarray as asfortranarray, require as require, ) @@ -294,11 +292,61 @@ from numpy.core.einsumfunc import ( einsum_path as einsum_path, ) +from numpy.core.multiarray import ( + ALLOW_THREADS as ALLOW_THREADS, + BUFSIZE as BUFSIZE, + CLIP as CLIP, + MAXDIMS as MAXDIMS, + MAY_SHARE_BOUNDS as MAY_SHARE_BOUNDS, + MAY_SHARE_EXACT as MAY_SHARE_EXACT, + RAISE as RAISE, + WRAP as WRAP, + tracemalloc_domain as tracemalloc_domain, + array as array, + empty_like as empty_like, + empty as empty, + zeros as zeros, + concatenate as concatenate, + inner as inner, + where as where, + lexsort as lexsort, + can_cast as can_cast, + min_scalar_type as min_scalar_type, + result_type as result_type, + dot as dot, + vdot as vdot, + bincount as bincount, + copyto as copyto, + putmask as putmask, + packbits as packbits, + unpackbits as unpackbits, + shares_memory as shares_memory, + may_share_memory as may_share_memory, + asarray as asarray, + asanyarray as asanyarray, + ascontiguousarray as ascontiguousarray, + asfortranarray as asfortranarray, + arange as arange, + busday_count as busday_count, + busday_offset as busday_offset, + compare_chararrays as compare_chararrays, + datetime_as_string as datetime_as_string, + datetime_data as datetime_data, + frombuffer as frombuffer, + fromfile as fromfile, + fromiter as fromiter, + is_busday as is_busday, + promote_types as promote_types, + seterrobj as seterrobj, + geterrobj as geterrobj, + fromstring as fromstring, + frompyfunc as frompyfunc, +) + from numpy.core.numeric import ( zeros_like as zeros_like, ones as ones, ones_like as ones_like, - empty_like as empty_like, full as full, full_like as full_like, count_nonzero as count_nonzero, @@ -578,10 +626,6 @@ class MachAr: ) -> None: ... def __getattr__(self, key: str) -> Any: ... -class busdaycalendar: - def __new__(cls, weekmask: Any = ..., holidays: Any = ...) -> Any: ... - def __getattr__(self, key: str) -> Any: ... - class chararray(ndarray[_ShapeType, _DType_co]): def __new__( subtype, @@ -845,69 +889,15 @@ def round(a, decimals=..., out=...): ... def round_(a, decimals=..., out=...): ... def show_config(): ... -# Placeholders for C-based functions # TODO: Sort out which parameters are positional-only -@overload -def arange(stop, dtype=..., *, like=...): ... -@overload -def arange(start, stop, step=..., dtype=..., *, like=...): ... -def busday_count( - begindates, - enddates, - weekmask=..., - holidays=..., - busdaycal=..., - out=..., -): ... -def busday_offset( - dates, - offsets, - roll=..., - weekmask=..., - holidays=..., - busdaycal=..., - out=..., -): ... -def can_cast(from_, to, casting=...): ... -def compare_chararrays(a, b, cmp_op, rstrip): ... -def concatenate(__a, axis=..., out=..., dtype=..., casting=...): ... -def copyto(dst, src, casting=..., where=...): ... -def datetime_as_string(arr, unit=..., timezone=..., casting=...): ... -def datetime_data(__dtype): ... -def dot(a, b, out=...): ... -def frombuffer(buffer, dtype=..., count=..., offset=..., *, like=...): ... -def fromfile( - file, dtype=..., count=..., sep=..., offset=..., *, like=... -): ... -def fromiter(iter, dtype, count=..., *, like=...): ... -def frompyfunc(func, nin, nout, * identity): ... -def fromstring(string, dtype=..., count=..., sep=..., *, like=...): ... -def geterrobj(): ... -def inner(a, b): ... -def is_busday( - dates, weekmask=..., holidays=..., busdaycal=..., out=... -): ... -def lexsort(keys, axis=...): ... -def may_share_memory(a, b, max_work=...): ... -def min_scalar_type(a): ... -def nested_iters(*args, **kwargs): ... # TODO: Sort out parameters -def promote_types(type1, type2): ... -def putmask(a, mask, values): ... -def result_type(*arrays_and_dtypes): ... -def seterrobj(errobj): ... -def shares_memory(a, b, max_work=...): ... -def vdot(a, b): ... -@overload -def where(__condition): ... -@overload -def where(__condition, __x, __y): ... +def nested_iters(*args, **kwargs): ... # TODO: Sort out parameters _NdArraySubClass = TypeVar("_NdArraySubClass", bound=ndarray) _DTypeScalar_co = TypeVar("_DTypeScalar_co", covariant=True, bound=generic) _ByteOrder = L["S", "<", ">", "=", "|", "L", "B", "N", "I"] class dtype(Generic[_DTypeScalar_co]): - names: Optional[Tuple[str, ...]] + names: None | Tuple[str, ...] # Overload for subclass of generic @overload def __new__( @@ -930,7 +920,7 @@ class dtype(Generic[_DTypeScalar_co]): @overload def __new__(cls, dtype: Type[int], align: bool = ..., copy: bool = ...) -> dtype[int_]: ... @overload - def __new__(cls, dtype: Optional[Type[float]], align: bool = ..., copy: bool = ...) -> dtype[float_]: ... + def __new__(cls, dtype: None | Type[float], align: bool = ..., copy: bool = ...) -> dtype[float_]: ... @overload def __new__(cls, dtype: Type[complex], align: bool = ..., copy: bool = ...) -> dtype[complex_]: ... @overload @@ -1061,22 +1051,24 @@ class dtype(Generic[_DTypeScalar_co]): @overload def __getitem__(self: dtype[void], key: List[str]) -> dtype[void]: ... @overload - def __getitem__(self: dtype[void], key: Union[str, int]) -> dtype[Any]: ... + def __getitem__(self: dtype[void], key: str | SupportsIndex) -> dtype[Any]: ... - # NOTE: In the future 1-based multiplications will also yield `void` dtypes - @overload - def __mul__(self, value: L[0]) -> None: ... # type: ignore[misc] + # NOTE: In the future 1-based multiplications will also yield `flexible` dtypes @overload def __mul__(self: _DType, value: L[1]) -> _DType: ... @overload - def __mul__(self, value: int) -> dtype[void]: ... + def __mul__(self: _FlexDType, value: SupportsIndex) -> _FlexDType: ... + @overload + def __mul__(self, value: SupportsIndex) -> dtype[void]: ... # NOTE: `__rmul__` seems to be broken when used in combination with - # literals as of mypy 0.800. Set the return-type to `Any` for now. - def __rmul__(self, value: int) -> Any: ... + # literals as of mypy 0.902. Set the return-type to `dtype[Any]` for + # now for non-flexible dtypes. + @overload + def __rmul__(self: _FlexDType, value: SupportsIndex) -> _FlexDType: ... + @overload + def __rmul__(self, value: SupportsIndex) -> dtype[Any]: ... - def __eq__(self, other: DTypeLike) -> bool: ... - def __ne__(self, other: DTypeLike) -> bool: ... def __gt__(self, other: DTypeLike) -> bool: ... def __ge__(self, other: DTypeLike) -> bool: ... def __lt__(self, other: DTypeLike) -> bool: ... @@ -1084,17 +1076,17 @@ class dtype(Generic[_DTypeScalar_co]): @property def alignment(self) -> int: ... @property - def base(self: _DType) -> _DType: ... + def base(self) -> dtype[Any]: ... @property def byteorder(self) -> str: ... @property def char(self) -> str: ... @property - def descr(self) -> List[Union[Tuple[str, str], Tuple[str, str, _Shape]]]: ... + def descr(self) -> List[Tuple[str, str] | Tuple[str, str, _Shape]]: ... @property def fields( self, - ) -> Optional[Mapping[str, Union[Tuple[dtype[Any], int], Tuple[dtype[Any], int, Any]]]]: ... + ) -> None | MappingProxyType[str, Tuple[dtype[Any], int] | Tuple[dtype[Any], int, Any]]: ... @property def flags(self) -> int: ... @property @@ -1110,19 +1102,17 @@ class dtype(Generic[_DTypeScalar_co]): @property def kind(self) -> str: ... @property - def metadata(self) -> Optional[Mapping[str, Any]]: ... + def metadata(self) -> None | MappingProxyType[str, Any]: ... @property def name(self) -> str: ... @property - def names(self) -> Optional[Tuple[str, ...]]: ... - @property def num(self) -> int: ... @property def shape(self) -> _Shape: ... @property def ndim(self) -> int: ... @property - def subdtype(self: _DType) -> Optional[Tuple[_DType, _Shape]]: ... + def subdtype(self) -> None | Tuple[dtype[Any], _Shape]: ... def newbyteorder(self: _DType, __new_order: _ByteOrder = ...) -> _DType: ... # Leave str and type for end to avoid having to use `builtins.str` # everywhere. See https://github.com/python/mypy/issues/3775 @@ -1226,20 +1216,9 @@ class _ArrayOrScalarCommon: def __deepcopy__(self: _ArraySelf, __memo: Optional[dict] = ...) -> _ArraySelf: ... def __eq__(self, other): ... def __ne__(self, other): ... - def astype( - self: _ArraySelf, - dtype: DTypeLike, - order: _OrderKACF = ..., - casting: _Casting = ..., - subok: bool = ..., - copy: bool = ..., - ) -> _ArraySelf: ... def copy(self: _ArraySelf, order: _OrderKACF = ...) -> _ArraySelf: ... def dump(self, file: str) -> None: ... def dumps(self) -> bytes: ... - def getfield( - self: _ArraySelf, dtype: DTypeLike, offset: int = ... - ) -> _ArraySelf: ... def tobytes(self, order: _OrderKACF = ...) -> bytes: ... # NOTE: `tostring()` is deprecated and therefore excluded # def tostring(self, order=...): ... @@ -1248,14 +1227,6 @@ class _ArrayOrScalarCommon: ) -> None: ... # generics and 0d arrays return builtin scalars def tolist(self) -> Any: ... - @overload - def view(self, type: Type[_NdArraySubClass]) -> _NdArraySubClass: ... - @overload - def view(self: _ArraySelf, dtype: DTypeLike = ...) -> _ArraySelf: ... - @overload - def view( - self, dtype: DTypeLike, type: Type[_NdArraySubClass] - ) -> _NdArraySubClass: ... # TODO: Add proper signatures def __getitem__(self, key) -> Any: ... @@ -1625,17 +1596,35 @@ class _ArrayOrScalarCommon: _DType = TypeVar("_DType", bound=dtype[Any]) _DType_co = TypeVar("_DType_co", covariant=True, bound=dtype[Any]) +_FlexDType = TypeVar("_FlexDType", bound=dtype[flexible]) # TODO: Set the `bound` to something more suitable once we # have proper shape support _ShapeType = TypeVar("_ShapeType", bound=Any) _NumberType = TypeVar("_NumberType", bound=number[Any]) -_BufferType = Union[ndarray, bytes, bytearray, memoryview] + +# There is currently no exhaustive way to type the buffer protocol, +# as it is implemented exclusivelly in the C API (python/typing#593) +_SupportsBuffer = Union[ + bytes, + bytearray, + memoryview, + _array.array[Any], + mmap.mmap, + NDArray[Any], + generic, +] _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _2Tuple = Tuple[_T, _T] -_Casting = L["no", "equiv", "safe", "same_kind", "unsafe"] +_CastingKind = L["no", "equiv", "safe", "same_kind", "unsafe"] + +_DTypeLike = Union[ + dtype[_ScalarType], + Type[_ScalarType], + _SupportsDType[dtype[_ScalarType]], +] _ArrayUInt_co = NDArray[Union[bool_, unsignedinteger[Any]]] _ArrayInt_co = NDArray[Union[bool_, integer[Any]]] @@ -1647,6 +1636,14 @@ _ArrayTD64_co = NDArray[Union[bool_, integer[Any], timedelta64]] class _SupportsItem(Protocol[_T_co]): def item(self, __args: Any) -> _T_co: ... +class _SupportsReal(Protocol[_T_co]): + @property + def real(self) -> _T_co: ... + +class _SupportsImag(Protocol[_T_co]): + @property + def imag(self) -> _T_co: ... + class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): @property def base(self) -> Optional[ndarray]: ... @@ -1655,18 +1652,22 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): @property def size(self) -> int: ... @property - def real(self: _ArraySelf) -> _ArraySelf: ... + def real( + self: NDArray[_SupportsReal[_ScalarType]], # type: ignore[type-var] + ) -> ndarray[_ShapeType, dtype[_ScalarType]]: ... @real.setter def real(self, value: ArrayLike) -> None: ... @property - def imag(self: _ArraySelf) -> _ArraySelf: ... + def imag( + self: NDArray[_SupportsImag[_ScalarType]], # type: ignore[type-var] + ) -> ndarray[_ShapeType, dtype[_ScalarType]]: ... @imag.setter def imag(self, value: ArrayLike) -> None: ... def __new__( cls: Type[_ArraySelf], shape: _ShapeLike, dtype: DTypeLike = ..., - buffer: _BufferType = ..., + buffer: _SupportsBuffer = ..., offset: int = ..., strides: _ShapeLike = ..., order: _OrderKACF = ..., @@ -1874,6 +1875,53 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): self, *shape: SupportsIndex, order: _OrderACF = ... ) -> ndarray[Any, _DType_co]: ... + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool = ..., + ) -> NDArray[_ScalarType]: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool = ..., + ) -> NDArray[Any]: ... + + @overload + def view(self: _ArraySelf) -> _ArraySelf: ... + @overload + def view(self, type: Type[_NdArraySubClass]) -> _NdArraySubClass: ... + @overload + def view(self, dtype: _DTypeLike[_ScalarType]) -> NDArray[_ScalarType]: ... + @overload + def view(self, dtype: DTypeLike) -> NDArray[Any]: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[_NdArraySubClass], + ) -> _NdArraySubClass: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> NDArray[_ScalarType]: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> NDArray[Any]: ... + # Dispatch to the underlying `generic` via protocols def __int__( self: ndarray[Any, dtype[SupportsInt]], # type: ignore[type-var] @@ -2842,6 +2890,59 @@ class generic(_ArrayOrScalarCommon): def byteswap(self: _ScalarType, inplace: L[False] = ...) -> _ScalarType: ... @property def flat(self: _ScalarType) -> flatiter[ndarray[Any, dtype[_ScalarType]]]: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool = ..., + ) -> _ScalarType: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool = ..., + ) -> Any: ... + + # NOTE: `view` will perform a 0D->scalar cast, + # thus the array `type` is irrelevant to the output type + @overload + def view( + self: _ScalarType, + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: _DTypeLike[_ScalarType], + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[ndarray[Any, Any]] = ..., + ) -> Any: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> _ScalarType: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> Any: ... + def item( self, __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., @@ -3038,11 +3139,24 @@ class datetime64(generic): if sys.version_info >= (3, 8): _IntValue = Union[SupportsInt, _CharLike_co, SupportsIndex] _FloatValue = Union[None, _CharLike_co, SupportsFloat, SupportsIndex] - _ComplexValue = Union[None, _CharLike_co, SupportsFloat, SupportsComplex, SupportsIndex] + _ComplexValue = Union[ + None, + _CharLike_co, + SupportsFloat, + SupportsComplex, + SupportsIndex, + complex, # `complex` is not a subtype of `SupportsComplex` + ] else: _IntValue = Union[SupportsInt, _CharLike_co] _FloatValue = Union[None, _CharLike_co, SupportsFloat] - _ComplexValue = Union[None, _CharLike_co, SupportsFloat, SupportsComplex] + _ComplexValue = Union[ + None, + _CharLike_co, + SupportsFloat, + SupportsComplex, + complex, + ] class integer(number[_NBit1]): # type: ignore # NOTE: `__index__` is technically defined in the bottom-most @@ -3324,31 +3438,6 @@ class str_(character, str): unicode_ = str_ str0 = str_ -def array( - object: object, - dtype: DTypeLike = ..., - *, - copy: bool = ..., - order: _OrderKACF = ..., - subok: bool = ..., - ndmin: int = ..., - like: ArrayLike = ..., -) -> ndarray: ... -def zeros( - shape: _ShapeLike, - dtype: DTypeLike = ..., - order: _OrderCF = ..., - *, - like: ArrayLike = ..., -) -> ndarray: ... -def empty( - shape: _ShapeLike, - dtype: DTypeLike = ..., - order: _OrderCF = ..., - *, - like: ArrayLike = ..., -) -> ndarray: ... - # # Constants # @@ -3367,9 +3456,7 @@ inf: Final[float] infty: Final[float] nan: Final[float] pi: Final[float] -ALLOW_THREADS: Final[int] -BUFSIZE: Final[int] -CLIP: Final[int] + ERR_CALL: Final[int] ERR_DEFAULT: Final[int] ERR_IGNORE: Final[int] @@ -3382,17 +3469,11 @@ FPE_DIVIDEBYZERO: Final[int] FPE_INVALID: Final[int] FPE_OVERFLOW: Final[int] FPE_UNDERFLOW: Final[int] -MAXDIMS: Final[int] -MAY_SHARE_BOUNDS: Final[int] -MAY_SHARE_EXACT: Final[int] -RAISE: Final[int] SHIFT_DIVIDEBYZERO: Final[int] SHIFT_INVALID: Final[int] SHIFT_OVERFLOW: Final[int] SHIFT_UNDERFLOW: Final[int] UFUNC_BUFSIZE_DEFAULT: Final[int] -WRAP: Final[int] -tracemalloc_domain: Final[int] little_endian: Final[bool] True_: Final[bool_] @@ -3648,3 +3729,14 @@ class broadcast: def __next__(self) -> Tuple[Any, ...]: ... def __iter__(self: _T) -> _T: ... def reset(self) -> None: ... + +class busdaycalendar: + def __new__( + cls, + weekmask: ArrayLike = ..., + holidays: ArrayLike = ..., + ) -> busdaycalendar: ... + @property + def weekmask(self) -> NDArray[bool_]: ... + @property + def holidays(self) -> NDArray[datetime64]: ... diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py index 448f6a0c0..a29e2e8a8 100644 --- a/numpy/core/_add_newdocs.py +++ b/numpy/core/_add_newdocs.py @@ -1280,7 +1280,7 @@ add_newdoc('numpy.core.multiarray', 'set_typeDict', add_newdoc('numpy.core.multiarray', 'fromstring', """ - fromstring(string, dtype=float, count=-1, sep='', *, like=None) + fromstring(string, dtype=float, count=-1, *, sep, like=None) A new 1-D array initialized from text data in a string. @@ -1346,16 +1346,16 @@ add_newdoc('numpy.core.multiarray', 'fromstring', add_newdoc('numpy.core.multiarray', 'compare_chararrays', """ - compare_chararrays(a, b, cmp_op, rstrip) + compare_chararrays(a1, a2, cmp, rstrip) Performs element-wise comparison of two string arrays using the comparison operator specified by `cmp_op`. Parameters ---------- - a, b : array_like + a1, a2 : array_like Arrays to be compared. - cmp_op : {"<", "<=", "==", ">=", ">", "!="} + cmp : {"<", "<=", "==", ">=", ">", "!="} Type of comparison. rstrip : Boolean If True, the spaces at the end of Strings are removed before the comparison. @@ -1585,8 +1585,8 @@ add_newdoc('numpy.core.multiarray', 'arange', For integer arguments the function is equivalent to the Python built-in `range` function, but returns an ndarray rather than a list. - When using a non-integer step, such as 0.1, the results will often not - be consistent. It is better to use `numpy.linspace` for these cases. + When using a non-integer step, such as 0.1, it is often better to use + `numpy.linspace`. See the warnings section below for more information. Parameters ---------- @@ -1619,6 +1619,25 @@ add_newdoc('numpy.core.multiarray', 'arange', this rule may result in the last element of `out` being greater than `stop`. + Warnings + -------- + The length of the output might not be numerically stable. + + Another stability issue is due to the internal implementation of + `numpy.arange`. + The actual step value used to populate the array is + ``dtype(start + step) - dtype(start)`` and not `step`. Precision loss + can occur here, due to casting or due to using floating points when + `start` is much larger than `step`. This can lead to unexpected + behaviour. For example:: + + >>> np.arange(0, 5, 0.5, dtype=int) + array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> np.arange(-3, 3, 0.5, dtype=int) + array([-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) + + In such cases, the use of `numpy.linspace` should be preferred. + See Also -------- numpy.linspace : Evenly spaced numbers with careful handling of endpoints. @@ -4475,7 +4494,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('view', add_newdoc('numpy.core.umath', 'frompyfunc', """ - frompyfunc(func, nin, nout, *[, identity]) + frompyfunc(func, /, nin, nout, *[, identity]) Takes an arbitrary Python function and returns a NumPy ufunc. @@ -4589,7 +4608,7 @@ add_newdoc('numpy.core.umath', 'geterrobj', add_newdoc('numpy.core.umath', 'seterrobj', """ - seterrobj(errobj) + seterrobj(errobj, /) Set the object that defines floating-point error handling. diff --git a/numpy/core/_asarray.pyi b/numpy/core/_asarray.pyi index ee21fc0f1..1928cfe12 100644 --- a/numpy/core/_asarray.pyi +++ b/numpy/core/_asarray.pyi @@ -1,7 +1,7 @@ import sys from typing import TypeVar, Union, Iterable, overload -from numpy import ndarray, _OrderKACF +from numpy import ndarray from numpy.typing import ArrayLike, DTypeLike if sys.version_info >= (3, 8): @@ -11,40 +11,6 @@ else: _ArrayType = TypeVar("_ArrayType", bound=ndarray) -# TODO: The following functions are now defined in C, so should be defined -# in a (not yet existing) `multiarray.pyi`. -# (with the exception of `require`) - -def asarray( - a: object, - dtype: DTypeLike = ..., - order: _OrderKACF = ..., - *, - like: ArrayLike = ... -) -> ndarray: ... -@overload -def asanyarray( - a: _ArrayType, - dtype: None = ..., - order: _OrderKACF = ..., - *, - like: ArrayLike = ... -) -> _ArrayType: ... -@overload -def asanyarray( - a: object, - dtype: DTypeLike = ..., - order: _OrderKACF = ..., - *, - like: ArrayLike = ... -) -> ndarray: ... -def ascontiguousarray( - a: object, dtype: DTypeLike = ..., *, like: ArrayLike = ... -) -> ndarray: ... -def asfortranarray( - a: object, dtype: DTypeLike = ..., *, like: ArrayLike = ... -) -> ndarray: ... - _Requirements = Literal[ "C", "C_CONTIGUOUS", "CONTIGUOUS", "F", "F_CONTIGUOUS", "FORTRAN", diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 3b0c46467..3ed22818f 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -324,10 +324,10 @@ class _ctypes: """ (c_intp*self.ndim): A ctypes array of length self.ndim where the basetype is the C-integer corresponding to ``dtype('p')`` on this - platform. This base-type could be `ctypes.c_int`, `ctypes.c_long`, or - `ctypes.c_longlong` depending on the platform. - The c_intp type is defined accordingly in `numpy.ctypeslib`. - The ctypes array contains the shape of the underlying array. + platform (see `~numpy.ctypeslib.c_intp`). This base-type could be + `ctypes.c_int`, `ctypes.c_long`, or `ctypes.c_longlong` depending on + the platform. The ctypes array contains the shape of + the underlying array. """ return self.shape_as(_getintp_ctype()) @@ -907,4 +907,3 @@ class recursive: self.func = func def __call__(self, *args, **kwargs): return self.func(self, *args, **kwargs) - diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 65a42eb1e..820d6831f 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -1318,8 +1318,9 @@ def searchsorted(a, v, side='left', sorter=None): Returns ------- - indices : array of ints - Array of insertion points with the same shape as `v`. + indices : int or array of ints + Array of insertion points with the same shape as `v`, + or an integer if `v` is a scalar. See Also -------- @@ -2088,9 +2089,9 @@ def clip(a, a_min, a_max, out=None, **kwargs): Notes ----- - When `a_min` is greater than `a_max`, `clip` returns an - array in which all values are equal to `a_max`, - as shown in the second example. + When `a_min` is greater than `a_max`, `clip` returns an + array in which all values are equal to `a_max`, + as shown in the second example. Examples -------- @@ -2525,7 +2526,7 @@ def cumsum(a, axis=None, dtype=None, out=None): >>> b = np.array([1, 2e-9, 3e-9] * 1000000) >>> b.cumsum()[-1] 1000000.0050045159 - >>> b.sum() + >>> b.sum() 1000000.0050000029 """ diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index d1acfdf26..3ea66b049 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -1236,6 +1236,8 @@ struct PyArrayIterObject_tag { _PyAIT(it)->dataptr = PyArray_BYTES(_PyAIT(it)->ao); \ for (__npy_i = 0; __npy_i<=_PyAIT(it)->nd_m1; \ __npy_i++) { \ + _PyAIT(it)->coordinates[__npy_i] = \ + (__npy_ind / _PyAIT(it)->factors[__npy_i]); \ _PyAIT(it)->dataptr += \ (__npy_ind / _PyAIT(it)->factors[__npy_i]) \ * _PyAIT(it)->strides[__npy_i]; \ diff --git a/numpy/core/multiarray.py b/numpy/core/multiarray.py index b7a3a8d67..3205f4ecd 100644 --- a/numpy/core/multiarray.py +++ b/numpy/core/multiarray.py @@ -31,7 +31,7 @@ __all__ = [ 'busday_count', 'busday_offset', 'busdaycalendar', 'can_cast', 'compare_chararrays', 'concatenate', 'copyto', 'correlate', 'correlate2', 'count_nonzero', 'c_einsum', 'datetime_as_string', 'datetime_data', - 'digitize', 'dot', 'dragon4_positional', 'dragon4_scientific', 'dtype', + 'dot', 'dragon4_positional', 'dragon4_scientific', 'dtype', 'empty', 'empty_like', 'error', 'flagsobj', 'flatiter', 'format_longfloat', 'frombuffer', 'fromfile', 'fromiter', 'fromstring', 'inner', 'interp', 'interp_complex', 'is_busday', 'lexsort', @@ -249,7 +249,7 @@ def concatenate(arrays, axis=None, out=None, *, dtype=None, casting=None): @array_function_from_c_func_and_dispatcher(_multiarray_umath.inner) def inner(a, b): """ - inner(a, b) + inner(a, b, /) Inner product of two arrays. @@ -341,7 +341,7 @@ def inner(a, b): @array_function_from_c_func_and_dispatcher(_multiarray_umath.where) def where(condition, x=None, y=None): """ - where(condition, [x, y]) + where(condition, [x, y], /) Return elements chosen from `x` or `y` depending on `condition`. @@ -613,7 +613,7 @@ def can_cast(from_, to, casting=None): @array_function_from_c_func_and_dispatcher(_multiarray_umath.min_scalar_type) def min_scalar_type(a): """ - min_scalar_type(a) + min_scalar_type(a, /) For scalar ``a``, returns the data type with the smallest size and smallest scalar kind which can hold its value. For non-scalar @@ -825,7 +825,7 @@ def dot(a, b, out=None): @array_function_from_c_func_and_dispatcher(_multiarray_umath.vdot) def vdot(a, b): """ - vdot(a, b) + vdot(a, b, /) Return the dot product of two vectors. @@ -883,7 +883,7 @@ def vdot(a, b): @array_function_from_c_func_and_dispatcher(_multiarray_umath.bincount) def bincount(x, weights=None, minlength=None): """ - bincount(x, weights=None, minlength=0) + bincount(x, /, weights=None, minlength=0) Count number of occurrences of each value in array of non-negative ints. @@ -1151,7 +1151,7 @@ def putmask(a, mask, values): @array_function_from_c_func_and_dispatcher(_multiarray_umath.packbits) def packbits(a, axis=None, bitorder='big'): """ - packbits(a, axis=None, bitorder='big') + packbits(a, /, axis=None, bitorder='big') Packs the elements of a binary-valued array into bits in a uint8 array. @@ -1209,7 +1209,7 @@ def packbits(a, axis=None, bitorder='big'): @array_function_from_c_func_and_dispatcher(_multiarray_umath.unpackbits) def unpackbits(a, axis=None, count=None, bitorder='big'): """ - unpackbits(a, axis=None, count=None, bitorder='big') + unpackbits(a, /, axis=None, count=None, bitorder='big') Unpacks elements of a uint8 array into a binary-valued output array. @@ -1293,7 +1293,7 @@ def unpackbits(a, axis=None, count=None, bitorder='big'): @array_function_from_c_func_and_dispatcher(_multiarray_umath.shares_memory) def shares_memory(a, b, max_work=None): """ - shares_memory(a, b, max_work=None) + shares_memory(a, b, /, max_work=None) Determine if two arrays share memory. @@ -1368,7 +1368,7 @@ def shares_memory(a, b, max_work=None): @array_function_from_c_func_and_dispatcher(_multiarray_umath.may_share_memory) def may_share_memory(a, b, max_work=None): """ - may_share_memory(a, b, max_work=None) + may_share_memory(a, b, /, max_work=None) Determine if two arrays might share memory diff --git a/numpy/core/multiarray.pyi b/numpy/core/multiarray.pyi new file mode 100644 index 000000000..bc33165be --- /dev/null +++ b/numpy/core/multiarray.pyi @@ -0,0 +1,950 @@ +# TODO: Sort out any and all missing functions in this namespace + +import os +import sys +import datetime as dt +from typing import ( + Any, + Callable, + IO, + Iterable, + Optional, + overload, + TypeVar, + List, + Type, + Union, + Sequence, + Tuple, +) + +from numpy import ( + # Re-exports + busdaycalendar as busdaycalendar, + broadcast as broadcast, + dtype as dtype, + ndarray as ndarray, + nditer as nditer, + + # The rest + nditer, + ufunc, + str_, + bool_, + uint8, + intp, + int_, + float64, + timedelta64, + datetime64, + generic, + unsignedinteger, + signedinteger, + floating, + complexfloating, + _OrderKACF, + _OrderCF, + _CastingKind, + _ModeKind, + _SupportsBuffer, +) + +from numpy.typing import ( + # Shapes + _ShapeLike, + + # DTypes + DTypeLike, + _SupportsDType, + + # Arrays + NDArray, + ArrayLike, + _SupportsArray, + _NestedSequence, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + _ArrayLikeObject_co, + _ArrayLikeStr_co, + _ArrayLikeBytes_co, + _ScalarLike_co, + _IntLike_co, + _FloatLike_co, + _TD64Like_co, +) + +if sys.version_info >= (3, 8): + from typing import SupportsIndex, Final, Literal as L +else: + from typing_extensions import SupportsIndex, Final, Literal as L + +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +# Subscriptable subsets of `npt.DTypeLike` and `npt.ArrayLike` +_DTypeLike = Union[ + dtype[_SCT], + Type[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] + +# Valid time units +_UnitKind = L[ + "Y", + "M", + "D", + "h", + "m", + "s", + "ms", + "us", "μs", + "ns", + "ps", + "fs", + "as", +] +_RollKind = L[ # `raise` is deliberately excluded + "nat", + "forward", + "following", + "backward", + "preceding", + "modifiedfollowing", + "modifiedpreceding", +] + +__all__: List[str] + +ALLOW_THREADS: Final[int] # 0 or 1 (system-specific) +BUFSIZE: L[8192] +CLIP: L[0] +WRAP: L[1] +RAISE: L[2] +MAXDIMS: L[32] +MAY_SHARE_BOUNDS: L[0] +MAY_SHARE_EXACT: L[-1] +tracemalloc_domain: L[389047] + +@overload +def empty_like( + prototype: _ArrayType, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> _ArrayType: ... +@overload +def empty_like( + prototype: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[_SCT]: ... +@overload +def empty_like( + prototype: object, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[Any]: ... +@overload +def empty_like( + prototype: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[_SCT]: ... +@overload +def empty_like( + prototype: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[Any]: ... + +@overload +def array( + object: _ArrayType, + dtype: None = ..., + *, + copy: bool = ..., + order: _OrderKACF = ..., + subok: L[True], + ndmin: int = ..., + like: ArrayLike = ..., +) -> _ArrayType: ... +@overload +def array( + object: _ArrayLike[_SCT], + dtype: None = ..., + *, + copy: bool = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def array( + object: object, + dtype: None = ..., + *, + copy: bool = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def array( + object: Any, + dtype: _DTypeLike[_SCT], + *, + copy: bool = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def array( + object: Any, + dtype: DTypeLike, + *, + copy: bool = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def zeros( + shape: _ShapeLike, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def zeros( + shape: _ShapeLike, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def zeros( + shape: _ShapeLike, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def empty( + shape: _ShapeLike, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def empty( + shape: _ShapeLike, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def empty( + shape: _ShapeLike, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def unravel_index( # type: ignore[misc] + indices: _IntLike_co, + shape: _ShapeLike, + order: _OrderCF = ..., +) -> Tuple[intp, ...]: ... +@overload +def unravel_index( + indices: _ArrayLikeInt_co, + shape: _ShapeLike, + order: _OrderCF = ..., +) -> Tuple[NDArray[intp], ...]: ... + +@overload +def ravel_multi_index( # type: ignore[misc] + multi_index: Sequence[_IntLike_co], + dims: Sequence[SupportsIndex], + mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., + order: _OrderCF = ..., +) -> intp: ... +@overload +def ravel_multi_index( + multi_index: Sequence[_ArrayLikeInt_co], + dims: Sequence[SupportsIndex], + mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., + order: _OrderCF = ..., +) -> NDArray[intp]: ... + +@overload +def concatenate( # type: ignore[misc] + __arrays: _ArrayLike[_SCT], + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: None = ..., + casting: Optional[_CastingKind] = ... +) -> NDArray[_SCT]: ... +@overload +def concatenate( # type: ignore[misc] + __arrays: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: None = ..., + casting: Optional[_CastingKind] = ... +) -> NDArray[Any]: ... +@overload +def concatenate( # type: ignore[misc] + __arrays: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: _DTypeLike[_SCT], + casting: Optional[_CastingKind] = ... +) -> NDArray[_SCT]: ... +@overload +def concatenate( # type: ignore[misc] + __arrays: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: DTypeLike, + casting: Optional[_CastingKind] = ... +) -> NDArray[Any]: ... +@overload +def concatenate( + __arrays: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: _ArrayType = ..., + *, + dtype: DTypeLike = ..., + casting: Optional[_CastingKind] = ... +) -> _ArrayType: ... + +def inner( + __a: ArrayLike, + __b: ArrayLike, +) -> Any: ... + +@overload +def where( + __condition: ArrayLike, +) -> Tuple[NDArray[intp], ...]: ... +@overload +def where( + __condition: ArrayLike, + __x: ArrayLike, + __y: ArrayLike, +) -> NDArray[Any]: ... + +def lexsort( + keys: ArrayLike, + axis: Optional[SupportsIndex] = ..., +) -> Any: ... + +def can_cast( + from_: Union[ArrayLike, DTypeLike], + to: DTypeLike, + casting: Optional[_CastingKind] = ..., +) -> bool: ... + +def min_scalar_type( + __a: ArrayLike, +) -> dtype[Any]: ... + +def result_type( + *arrays_and_dtypes: Union[ArrayLike, DTypeLike], +) -> dtype[Any]: ... + +@overload +def dot(a: ArrayLike, b: ArrayLike, out: None = ...) -> Any: ... +@overload +def dot(a: ArrayLike, b: ArrayLike, out: _ArrayType) -> _ArrayType: ... + +@overload +def vdot(__a: _ArrayLikeBool_co, __b: _ArrayLikeBool_co) -> bool_: ... # type: ignore[misc] +@overload +def vdot(__a: _ArrayLikeUInt_co, __b: _ArrayLikeUInt_co) -> unsignedinteger[Any]: ... # type: ignore[misc] +@overload +def vdot(__a: _ArrayLikeInt_co, __b: _ArrayLikeInt_co) -> signedinteger[Any]: ... # type: ignore[misc] +@overload +def vdot(__a: _ArrayLikeFloat_co, __b: _ArrayLikeFloat_co) -> floating[Any]: ... # type: ignore[misc] +@overload +def vdot(__a: _ArrayLikeComplex_co, __b: _ArrayLikeComplex_co) -> complexfloating[Any, Any]: ... # type: ignore[misc] +@overload +def vdot(__a: _ArrayLikeTD64_co, __b: _ArrayLikeTD64_co) -> timedelta64: ... +@overload +def vdot(__a: _ArrayLikeObject_co, __b: Any) -> Any: ... +@overload +def vdot(__a: Any, __b: _ArrayLikeObject_co) -> Any: ... + +def bincount( + __x: ArrayLike, + weights: Optional[ArrayLike] = ..., + minlength: SupportsIndex = ..., +) -> NDArray[intp]: ... + +def copyto( + dst: NDArray[Any], + src: ArrayLike, + casting: Optional[_CastingKind] = ..., + where: Optional[_ArrayLikeBool_co] = ..., +) -> None: ... + +def putmask( + a: NDArray[Any], + mask: _ArrayLikeBool_co, + values: ArrayLike, +) -> None: ... + +def packbits( + __a: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + bitorder: L["big", "little"] = ..., +) -> NDArray[uint8]: ... + +def unpackbits( + __a: _ArrayLike[uint8], + axis: Optional[SupportsIndex] = ..., + count: Optional[SupportsIndex] = ..., + bitorder: L["big", "little"] = ..., +) -> NDArray[uint8]: ... + +def shares_memory( + __a: object, + __b: object, + max_work: Optional[int] = ..., +) -> bool: ... + +def may_share_memory( + __a: object, + __b: object, + max_work: Optional[int] = ..., +) -> bool: ... + +@overload +def asarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asarray( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def asanyarray( + a: _ArrayType, # Preserve subclass-information + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> _ArrayType: ... +@overload +def asanyarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asanyarray( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asanyarray( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asanyarray( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def ascontiguousarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ascontiguousarray( + a: object, + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def ascontiguousarray( + a: Any, + dtype: _DTypeLike[_SCT], + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ascontiguousarray( + a: Any, + dtype: DTypeLike, + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def asfortranarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asfortranarray( + a: object, + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asfortranarray( + a: Any, + dtype: _DTypeLike[_SCT], + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asfortranarray( + a: Any, + dtype: DTypeLike, + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +# In practice `List[Any]` is list with an int, int and a valid +# `np.seterrcall()` object +def geterrobj() -> List[Any]: ... +def seterrobj(__errobj: List[Any]) -> None: ... + +def promote_types(__type1: DTypeLike, __type2: DTypeLike) -> dtype[Any]: ... + +# `sep` is a de facto mandatory argument, as its default value is deprecated +@overload +def fromstring( + string: str | bytes, + dtype: None = ..., + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def fromstring( + string: str | bytes, + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromstring( + string: str | bytes, + dtype: DTypeLike, + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +def frompyfunc( + __func: Callable[..., Any], + nin: SupportsIndex, + nout: SupportsIndex, + *, + identity: Any = ..., +) -> ufunc: ... + +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | IO[Any], + dtype: None = ..., + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | IO[Any], + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | IO[Any], + dtype: DTypeLike, + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def fromiter( + iter: Iterable[Any], + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromiter( + iter: Iterable[Any], + dtype: DTypeLike, + count: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: None = ..., + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: DTypeLike, + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def arange( # type: ignore[misc] + __stop: _IntLike_co, + *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def arange( # type: ignore[misc] + start: _IntLike_co, + stop: _IntLike_co, + step: _IntLike_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def arange( # type: ignore[misc] + __stop: _FloatLike_co, + *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[floating[Any]]: ... +@overload +def arange( # type: ignore[misc] + start: _FloatLike_co, + stop: _FloatLike_co, + step: _FloatLike_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[floating[Any]]: ... +@overload +def arange( + __stop: _TD64Like_co, + *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[timedelta64]: ... +@overload +def arange( + start: _TD64Like_co, + stop: _TD64Like_co, + step: _TD64Like_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[timedelta64]: ... +@overload +def arange( # both start and stop must always be specified for datetime64 + start: datetime64, + stop: datetime64, + step: datetime64 = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[datetime64]: ... +@overload +def arange( + __stop: Any, + *, + dtype: _DTypeLike[_SCT], + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def arange( + start: Any, + stop: Any, + step: Any = ..., + dtype: _DTypeLike[_SCT] = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def arange( + __stop: Any, + *, + dtype: DTypeLike, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def arange( + start: Any, + stop: Any, + step: Any = ..., + dtype: DTypeLike = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +def datetime_data( + __dtype: str | _DTypeLike[datetime64] | _DTypeLike[timedelta64], +) -> Tuple[str, int]: ... + +# The datetime functions perform unsafe casts to `datetime64[D]`, +# so a lot of different argument types are allowed here + +@overload +def busday_count( # type: ignore[misc] + begindates: _ScalarLike_co, + enddates: _ScalarLike_co, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> int_: ... +@overload +def busday_count( # type: ignore[misc] + begindates: ArrayLike, + enddates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[int_]: ... +@overload +def busday_count( + begindates: ArrayLike, + enddates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +# `roll="raise"` is (more or less?) equivalent to `casting="safe"` +@overload +def busday_offset( # type: ignore[misc] + dates: datetime64, + offsets: _TD64Like_co, + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> datetime64: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ArrayLike[datetime64], + offsets: _ArrayLikeTD64_co, + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[datetime64]: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ArrayLike[datetime64], + offsets: _ArrayLike[timedelta64], + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ScalarLike_co, + offsets: _ScalarLike_co, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> datetime64: ... +@overload +def busday_offset( # type: ignore[misc] + dates: ArrayLike, + offsets: ArrayLike, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[datetime64]: ... +@overload +def busday_offset( + dates: ArrayLike, + offsets: ArrayLike, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +@overload +def is_busday( # type: ignore[misc] + dates: _ScalarLike_co, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> bool_: ... +@overload +def is_busday( # type: ignore[misc] + dates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[bool_]: ... +@overload +def is_busday( + dates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +@overload +def datetime_as_string( # type: ignore[misc] + arr: datetime64, + unit: None | L["auto"] | _UnitKind = ..., + timezone: L["naive", "UTC", "local"] | dt.tzinfo = ..., + casting: _CastingKind = ..., +) -> str_: ... +@overload +def datetime_as_string( + arr: _ArrayLikeDT64_co, + unit: None | L["auto"] | _UnitKind = ..., + timezone: L["naive", "UTC", "local"] | dt.tzinfo = ..., + casting: _CastingKind = ..., +) -> NDArray[str_]: ... + +@overload +def compare_chararrays( + a1: _ArrayLikeStr_co, + a2: _ArrayLikeStr_co, + cmp: L["<", "<=", "==", ">=", ">", "!="], + rstrip: bool, +) -> NDArray[bool_]: ... +@overload +def compare_chararrays( + a1: _ArrayLikeBytes_co, + a2: _ArrayLikeBytes_co, + cmp: L["<", "<=", "==", ">=", ">", "!="], + rstrip: bool, +) -> NDArray[bool_]: ... + +def add_docstring(__obj: Callable[..., Any], __docstring: str) -> None: ... diff --git a/numpy/core/numeric.pyi b/numpy/core/numeric.pyi index f57951434..3c2b553ec 100644 --- a/numpy/core/numeric.pyi +++ b/numpy/core/numeric.pyi @@ -67,23 +67,6 @@ def ones_like( shape: Optional[_ShapeLike] = ..., ) -> ndarray: ... -@overload -def empty_like( - a: _ArrayType, - dtype: None = ..., - order: _OrderKACF = ..., - subok: Literal[True] = ..., - shape: None = ..., -) -> _ArrayType: ... -@overload -def empty_like( - a: ArrayLike, - dtype: DTypeLike = ..., - order: _OrderKACF = ..., - subok: bool = ..., - shape: Optional[_ShapeLike] = ..., -) -> ndarray: ... - def full( shape: _ShapeLike, fill_value: Any, diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py index c2b5fb7fa..70085d896 100644 --- a/numpy/core/overrides.py +++ b/numpy/core/overrides.py @@ -18,11 +18,7 @@ array_function_like_doc = ( NumPy arrays. If an array-like passed in as ``like`` supports the ``__array_function__`` protocol, the result will be defined by it. In this case, it ensures the creation of an array object - compatible with that passed in via this argument. - - .. note:: - The ``like`` keyword is an experimental feature pending on - acceptance of :ref:`NEP 35 <NEP35>`.""" + compatible with that passed in via this argument.""" ) def set_array_function_like_doc(public_api): diff --git a/numpy/core/shape_base.pyi b/numpy/core/shape_base.pyi index ec40a8814..9aaeceed7 100644 --- a/numpy/core/shape_base.pyi +++ b/numpy/core/shape_base.pyi @@ -1,39 +1,72 @@ import sys -from typing import TypeVar, overload, List, Sequence +from typing import TypeVar, overload, List, Sequence, Any -from numpy import ndarray -from numpy.typing import ArrayLike +from numpy import generic, dtype +from numpy.typing import ArrayLike, NDArray, _NestedSequence, _SupportsArray if sys.version_info >= (3, 8): from typing import SupportsIndex else: from typing_extensions import SupportsIndex -_ArrayType = TypeVar("_ArrayType", bound=ndarray) +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +@overload +def atleast_1d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def atleast_1d(__arys: ArrayLike) -> NDArray[Any]: ... +@overload +def atleast_1d(*arys: ArrayLike) -> List[NDArray[Any]]: ... + +@overload +def atleast_2d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def atleast_2d(__arys: ArrayLike) -> NDArray[Any]: ... @overload -def atleast_1d(__arys: ArrayLike) -> ndarray: ... +def atleast_2d(*arys: ArrayLike) -> List[NDArray[Any]]: ... + +@overload +def atleast_3d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... @overload -def atleast_1d(*arys: ArrayLike) -> List[ndarray]: ... +def atleast_3d(__arys: ArrayLike) -> NDArray[Any]: ... +@overload +def atleast_3d(*arys: ArrayLike) -> List[NDArray[Any]]: ... @overload -def atleast_2d(__arys: ArrayLike) -> ndarray: ... +def vstack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... @overload -def atleast_2d(*arys: ArrayLike) -> List[ndarray]: ... +def vstack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... @overload -def atleast_3d(__arys: ArrayLike) -> ndarray: ... +def hstack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... @overload -def atleast_3d(*arys: ArrayLike) -> List[ndarray]: ... +def hstack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... -def vstack(tup: Sequence[ArrayLike]) -> ndarray: ... -def hstack(tup: Sequence[ArrayLike]) -> ndarray: ... @overload def stack( - arrays: Sequence[ArrayLike], axis: SupportsIndex = ..., out: None = ... -) -> ndarray: ... + arrays: Sequence[_ArrayLike[_SCT]], + axis: SupportsIndex = ..., + out: None = ..., +) -> NDArray[_SCT]: ... +@overload +def stack( + arrays: Sequence[ArrayLike], + axis: SupportsIndex = ..., + out: None = ..., +) -> NDArray[Any]: ... @overload def stack( - arrays: Sequence[ArrayLike], axis: SupportsIndex = ..., out: _ArrayType = ... + arrays: Sequence[ArrayLike], + axis: SupportsIndex = ..., + out: _ArrayType = ..., ) -> _ArrayType: ... -def block(arrays: ArrayLike) -> ndarray: ... + +@overload +def block(arrays: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def block(arrays: ArrayLike) -> NDArray[Any]: ... diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src index bfdeae079..79140bdb7 100644 --- a/numpy/core/src/multiarray/_multiarray_tests.c.src +++ b/numpy/core/src/multiarray/_multiarray_tests.c.src @@ -87,7 +87,7 @@ static int copy_@name@(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni * For each point in itx, copy the current neighborhood into an array which * is appended at the output list */ - for (i = 0; i < itx->size; ++i) { + for (i = itx->index; i < itx->size; ++i) { PyArrayNeighborhoodIter_Reset(niterx); for (j = 0; j < PyArray_NDIM(itx->ao); ++j) { @@ -130,7 +130,7 @@ static int copy_object(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni * For each point in itx, copy the current neighborhood into an array which * is appended at the output list */ - for (i = 0; i < itx->size; ++i) { + for (i = itx->index; i < itx->size; ++i) { PyArrayNeighborhoodIter_Reset(niterx); for (j = 0; j < PyArray_NDIM(itx->ao); ++j) { @@ -161,10 +161,11 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args) PyArrayObject *ax, *afill; PyArrayIterObject *itx; int i, typenum, mode, st; + Py_ssize_t idxstart = 0; npy_intp bounds[NPY_MAXDIMS*2]; PyArrayNeighborhoodIterObject *niterx; - if (!PyArg_ParseTuple(args, "OOOi", &x, &b, &fill, &mode)) { + if (!PyArg_ParseTuple(args, "OOOi|n", &x, &b, &fill, &mode, &idxstart)) { return NULL; } @@ -224,12 +225,20 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args) } } + if (idxstart >= itx->size) { + PyErr_SetString(PyExc_ValueError, + "start index not compatible with x input"); + goto clean_itx; + } + niterx = (PyArrayNeighborhoodIterObject*)PyArray_NeighborhoodIterNew( (PyArrayIterObject*)itx, bounds, mode, afill); if (niterx == NULL) { goto clean_afill; } + PyArray_ITER_GOTO1D((PyArrayIterObject*)itx, idxstart); + switch (typenum) { case NPY_OBJECT: st = copy_object(itx, niterx, bounds, &out); diff --git a/numpy/core/src/multiarray/array_method.c b/numpy/core/src/multiarray/array_method.c index e13da12de..ab992a3ae 100644 --- a/numpy/core/src/multiarray/array_method.c +++ b/numpy/core/src/multiarray/array_method.c @@ -36,6 +36,7 @@ #include "dtypemeta.h" #include "common_dtype.h" #include "convert_datatype.h" +#include "common.h" /* @@ -210,10 +211,12 @@ validate_spec(PyArrayMethod_Spec *spec) case NPY_UNSAFE_CASTING: break; default: - PyErr_Format(PyExc_TypeError, - "ArrayMethod has invalid casting `%d`. (method: %s)", - spec->casting, spec->name); - return -1; + if (spec->casting != -1) { + PyErr_Format(PyExc_TypeError, + "ArrayMethod has invalid casting `%d`. (method: %s)", + spec->casting, spec->name); + return -1; + } } for (int i = 0; i < nargs; i++) { @@ -301,6 +304,13 @@ fill_arraymethod_from_slots( /* Check whether the slots are valid: */ if (meth->resolve_descriptors == &default_resolve_descriptors) { + if (spec->casting == -1) { + PyErr_Format(PyExc_TypeError, + "Cannot set casting to -1 (invalid) when not providing " + "the default `resolve_descriptors` function. " + "(method: %s)", spec->name); + return -1; + } for (int i = 0; i < meth->nin + meth->nout; i++) { if (res->dtypes[i] == NULL) { if (i < meth->nin) { @@ -462,14 +472,11 @@ static PyObject * boundarraymethod_repr(PyBoundArrayMethodObject *self) { int nargs = self->method->nin + self->method->nout; - PyObject *dtypes = PyTuple_New(nargs); + PyObject *dtypes = PyArray_TupleFromItems( + nargs, (PyObject **)self->dtypes, 0); if (dtypes == NULL) { return NULL; } - for (int i = 0; i < nargs; i++) { - Py_INCREF(self->dtypes[i]); - PyTuple_SET_ITEM(dtypes, i, (PyObject *)self->dtypes[i]); - } return PyUnicode_FromFormat( "<np._BoundArrayMethod `%s` for dtypes %S>", self->method->name, dtypes); @@ -573,6 +580,8 @@ boundarraymethod__resolve_descripors( /* * The casting flags should be the most generic casting level (except the * cast-is-view flag. If no input is parametric, it must match exactly. + * + * (Note that these checks are only debugging checks.) */ int parametric = 0; for (int i = 0; i < nin + nout; i++) { @@ -581,34 +590,34 @@ boundarraymethod__resolve_descripors( break; } } - if (!parametric) { - /* - * Non-parametric can only mismatch if it switches from no to equiv - * (e.g. due to byteorder changes). - */ - if (self->method->casting != (casting & ~_NPY_CAST_IS_VIEW) && - !(self->method->casting == NPY_NO_CASTING && - casting == NPY_EQUIV_CASTING)) { - PyErr_Format(PyExc_RuntimeError, - "resolve_descriptors cast level did not match stored one " - "(expected %d, got %d) for method %s", - self->method->casting, (casting & ~_NPY_CAST_IS_VIEW), - self->method->name); - Py_DECREF(result_tuple); - return NULL; - } - } - else { + if (self->method->casting != -1) { NPY_CASTING cast = casting & ~_NPY_CAST_IS_VIEW; - if (cast != PyArray_MinCastSafety(cast, self->method->casting)) { + if (self->method->casting != + PyArray_MinCastSafety(cast, self->method->casting)) { PyErr_Format(PyExc_RuntimeError, - "resolve_descriptors cast level did not match stored one " - "(expected %d, got %d) for method %s", - self->method->casting, (casting & ~_NPY_CAST_IS_VIEW), - self->method->name); + "resolve_descriptors cast level did not match stored one. " + "(set level is %d, got %d for method %s)", + self->method->casting, cast, self->method->name); Py_DECREF(result_tuple); return NULL; } + if (!parametric) { + /* + * Non-parametric can only mismatch if it switches from equiv to no + * (e.g. due to byteorder changes). + */ + if (cast != self->method->casting && + self->method->casting != NPY_EQUIV_CASTING) { + PyErr_Format(PyExc_RuntimeError, + "resolve_descriptors cast level changed even though " + "the cast is non-parametric where the only possible " + "change should be from equivalent to no casting. " + "(set level is %d, got %d for method %s)", + self->method->casting, cast, self->method->name); + Py_DECREF(result_tuple); + return NULL; + } + } } return Py_BuildValue("iN", casting, result_tuple); diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index e7fbb88cd..78f547de2 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -971,8 +971,7 @@ _strings_richcompare(PyArrayObject *self, PyArrayObject *other, int cmp_op, PyArrayMultiIterObject *mit; int val; - /* Cast arrays to a common type */ - if (PyArray_TYPE(self) != PyArray_DESCR(other)->type_num) { + if (PyArray_TYPE(self) != PyArray_TYPE(other)) { /* * Comparison between Bytes and Unicode is not defined in Py3K; * we follow. @@ -981,53 +980,22 @@ _strings_richcompare(PyArrayObject *self, PyArrayObject *other, int cmp_op, return Py_NotImplemented; } if (PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(other)) { - PyObject *new; - if (PyArray_TYPE(self) == NPY_STRING && - PyArray_DESCR(other)->type_num == NPY_UNICODE) { - PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(other)); - unicode->elsize = PyArray_DESCR(self)->elsize << 2; - new = PyArray_FromAny((PyObject *)self, unicode, - 0, 0, 0, NULL); - if (new == NULL) { - return NULL; - } - Py_INCREF(other); - self = (PyArrayObject *)new; - } - else if ((PyArray_TYPE(self) == NPY_UNICODE) && - ((PyArray_DESCR(other)->type_num == NPY_STRING) || - (PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(other)))) { - PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(self)); - - if (PyArray_DESCR(other)->type_num == NPY_STRING) { - unicode->elsize = PyArray_DESCR(other)->elsize << 2; - } - else { - unicode->elsize = PyArray_DESCR(other)->elsize; - } - new = PyArray_FromAny((PyObject *)other, unicode, - 0, 0, 0, NULL); - if (new == NULL) { - return NULL; - } - Py_INCREF(self); - other = (PyArrayObject *)new; - } - else { - PyErr_SetString(PyExc_TypeError, - "invalid string data-types " - "in comparison"); + /* Cast `other` to the same byte order as `self` (both unicode here) */ + PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(self)); + unicode->elsize = PyArray_DESCR(other)->elsize; + PyObject *new = PyArray_FromAny((PyObject *)other, + unicode, 0, 0, 0, NULL); + if (new == NULL) { return NULL; } + other = (PyArrayObject *)new; } else { - Py_INCREF(self); Py_INCREF(other); } /* Broad-cast the arrays to a common shape */ mit = (PyArrayMultiIterObject *)PyArray_MultiIterNew(2, self, other); - Py_DECREF(self); Py_DECREF(other); if (mit == NULL) { return NULL; diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index 83209cd38..203decaa0 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -291,6 +291,34 @@ npy_memchr(char * haystack, char needle, return p; } + +/* + * Simple helper to create a tuple from an array of items. The `make_null_none` + * flag means that NULL entries are replaced with None, which is occasionally + * useful. + */ +static NPY_INLINE PyObject * +PyArray_TupleFromItems(int n, PyObject *const *items, int make_null_none) +{ + PyObject *tuple = PyTuple_New(n); + if (tuple == NULL) { + return NULL; + } + for (int i = 0; i < n; i ++) { + PyObject *tmp; + if (!make_null_none || items[i] != NULL) { + tmp = items[i]; + } + else { + tmp = Py_None; + } + Py_INCREF(tmp); + PyTuple_SET_ITEM(tuple, i, tmp); + } + return tuple; +} + + #include "ucsnarrow.h" /* diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 3c4c21ded..adfff1129 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -1222,11 +1222,7 @@ PyArray_IntTupleFromIntp(int len, npy_intp const *vals) goto fail; } for (i = 0; i < len; i++) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - PyObject *o = PyLong_FromLong((long) vals[i]); -#else - PyObject *o = PyLong_FromLongLong((npy_longlong) vals[i]); -#endif + PyObject *o = PyArray_PyIntFromIntp(vals[i]); if (!o) { Py_DECREF(intTuple); intTuple = NULL; diff --git a/numpy/core/src/multiarray/conversion_utils.h b/numpy/core/src/multiarray/conversion_utils.h index 7d1871c43..55c0cdd35 100644 --- a/numpy/core/src/multiarray/conversion_utils.h +++ b/numpy/core/src/multiarray/conversion_utils.h @@ -39,6 +39,17 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals); NPY_NO_EXPORT int PyArray_TypestrConvert(int itemsize, int gentype); + +static NPY_INLINE PyObject * +PyArray_PyIntFromIntp(npy_intp const value) +{ +#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG + return PyLong_FromLong((long)value); +#else + return PyLong_FromLongLong((npy_longlong)value); +#endif +} + NPY_NO_EXPORT PyObject * PyArray_IntTupleFromIntp(int len, npy_intp const *vals); diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index 01ee56d16..d197a4bea 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -358,6 +358,45 @@ PyArray_CastAnyTo(PyArrayObject *out, PyArrayObject *mp) } +static NPY_CASTING +_get_cast_safety_from_castingimpl(PyArrayMethodObject *castingimpl, + PyArray_DTypeMeta *dtypes[2], PyArray_Descr *from, PyArray_Descr *to) +{ + PyArray_Descr *descrs[2] = {from, to}; + PyArray_Descr *out_descrs[2]; + + NPY_CASTING casting = castingimpl->resolve_descriptors( + castingimpl, dtypes, descrs, out_descrs); + if (casting < 0) { + return -1; + } + /* The returned descriptors may not match, requiring a second check */ + if (out_descrs[0] != descrs[0]) { + NPY_CASTING from_casting = PyArray_GetCastSafety( + descrs[0], out_descrs[0], NULL); + casting = PyArray_MinCastSafety(casting, from_casting); + if (casting < 0) { + goto finish; + } + } + if (descrs[1] != NULL && out_descrs[1] != descrs[1]) { + NPY_CASTING from_casting = PyArray_GetCastSafety( + descrs[1], out_descrs[1], NULL); + casting = PyArray_MinCastSafety(casting, from_casting); + if (casting < 0) { + goto finish; + } + } + + finish: + Py_DECREF(out_descrs[0]); + Py_DECREF(out_descrs[1]); + /* NPY_NO_CASTING has to be used for (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW) */ + assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW)); + return casting; +} + + /** * Given two dtype instances, find the correct casting safety. * @@ -375,7 +414,6 @@ NPY_NO_EXPORT NPY_CASTING PyArray_GetCastSafety( PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype) { - NPY_CASTING casting; if (to != NULL) { to_dtype = NPY_DTYPE(to); } @@ -389,41 +427,68 @@ PyArray_GetCastSafety( } PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth; - PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype}; - PyArray_Descr *descrs[2] = {from, to}; - PyArray_Descr *out_descrs[2]; - - casting = castingimpl->resolve_descriptors( - castingimpl, dtypes, descrs, out_descrs); + NPY_CASTING casting = _get_cast_safety_from_castingimpl(castingimpl, + dtypes, from, to); Py_DECREF(meth); - if (casting < 0) { + + return casting; +} + + +/** + * Check whether a cast is safe, see also `PyArray_GetCastSafety` for + * a similiar function. Unlike GetCastSafety, this function checks the + * `castingimpl->casting` when available. This allows for two things: + * + * 1. It avoids calling `resolve_descriptors` in some cases. + * 2. Strings need to discover the length, but in some cases we know that the + * cast is valid (assuming the string length is discovered first). + * + * The latter means that a `can_cast` could return True, but the cast fail + * because the parametric type cannot guess the correct output descriptor. + * (I.e. if `object_arr.astype("S")` did _not_ inspect the objects, and the + * user would have to guess the string length.) + * + * @param casting the requested casting safety. + * @param from + * @param to The descriptor to cast to (may be NULL) + * @param to_dtype If `to` is NULL, must pass the to_dtype (otherwise this + * is ignored). + * @return 0 for an invalid cast, 1 for a valid and -1 for an error. + */ +static int +PyArray_CheckCastSafety(NPY_CASTING casting, + PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype) +{ + if (to != NULL) { + to_dtype = NPY_DTYPE(to); + } + PyObject *meth = PyArray_GetCastingImpl(NPY_DTYPE(from), to_dtype); + if (meth == NULL) { return -1; } - /* The returned descriptors may not match, requiring a second check */ - if (out_descrs[0] != descrs[0]) { - NPY_CASTING from_casting = PyArray_GetCastSafety( - descrs[0], out_descrs[0], NULL); - casting = PyArray_MinCastSafety(casting, from_casting); - if (casting < 0) { - goto finish; - } + if (meth == Py_None) { + Py_DECREF(Py_None); + return -1; } - if (descrs[1] != NULL && out_descrs[1] != descrs[1]) { - NPY_CASTING from_casting = PyArray_GetCastSafety( - descrs[1], out_descrs[1], NULL); - casting = PyArray_MinCastSafety(casting, from_casting); - if (casting < 0) { - goto finish; - } + PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth; + + if (PyArray_MinCastSafety(castingimpl->casting, casting) == casting) { + /* No need to check using `castingimpl.resolve_descriptors()` */ + Py_DECREF(meth); + return 1; } - finish: - Py_DECREF(out_descrs[0]); - Py_DECREF(out_descrs[1]); - /* NPY_NO_CASTING has to be used for (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW) */ - assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW)); - return casting; + PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype}; + NPY_CASTING safety = _get_cast_safety_from_castingimpl(castingimpl, + dtypes, from, to); + Py_DECREF(meth); + /* If casting is the smaller (or equal) safety we match */ + if (safety < 0) { + return -1; + } + return PyArray_MinCastSafety(safety, casting) == casting; } @@ -565,6 +630,8 @@ NPY_NO_EXPORT npy_bool PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, NPY_CASTING casting) { + PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to); + /* * NOTE: This code supports U and S, this is identical to the code * in `ctors.c` which does not allow these dtypes to be attached @@ -576,21 +643,21 @@ PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, * TODO: We should grow support for `np.can_cast("d", "S")` being * different from `np.can_cast("d", "S0")` here, at least for * the python side API. + * The `to = NULL` branch, which considers "S0" to be "flexible" + * should probably be deprecated. + * (This logic is duplicated in `PyArray_CanCastArrayTo`) */ - NPY_CASTING safety; if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) { - safety = PyArray_GetCastSafety(from, NULL, NPY_DTYPE(to)); - } - else { - safety = PyArray_GetCastSafety(from, to, NPY_DTYPE(to)); + to = NULL; /* consider mainly S0 and U0 as S and U */ } - if (safety < 0) { + int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype); + /* Clear any errors and consider this unsafe (should likely be changed) */ + if (is_valid < 0) { PyErr_Clear(); return 0; } - /* If casting is the smaller (or equal) safety we match */ - return PyArray_MinCastSafety(safety, casting) == casting; + return is_valid; } @@ -610,28 +677,22 @@ can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data, /* * If the two dtypes are actually references to the same object * or if casting type is forced unsafe then always OK. + * + * TODO: Assuming that unsafe casting always works is not actually correct */ if (scal_type == to || casting == NPY_UNSAFE_CASTING ) { return 1; } - /* NOTE: This is roughly the same code as `PyArray_CanCastTypeTo`: */ - NPY_CASTING safety; - if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) { - safety = PyArray_GetCastSafety(scal_type, NULL, NPY_DTYPE(to)); - } - else { - safety = PyArray_GetCastSafety(scal_type, to, NPY_DTYPE(to)); - } - if (safety < 0) { - PyErr_Clear(); - return 0; - } - safety = PyArray_MinCastSafety(safety, casting); - if (safety == casting) { + int valid = PyArray_CheckCastSafety(casting, scal_type, to, NPY_DTYPE(to)); + if (valid == 1) { /* This is definitely a valid cast. */ return 1; } + if (valid < 0) { + /* Probably must return 0, but just keep trying for now. */ + PyErr_Clear(); + } /* * If the scalar isn't a number, value-based casting cannot kick in and @@ -692,14 +753,29 @@ PyArray_CanCastArrayTo(PyArrayObject *arr, PyArray_Descr *to, NPY_CASTING casting) { PyArray_Descr *from = PyArray_DESCR(arr); + PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to); - /* If it's a scalar, check the value */ - if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr)) { + /* NOTE, TODO: The same logic as `PyArray_CanCastTypeTo`: */ + if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) { + to = NULL; + } + + /* + * If it's a scalar, check the value. (This only currently matters for + * numeric types and for `to == NULL` it can't be numeric.) + */ + if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr) && to != NULL) { return can_cast_scalar_to(from, PyArray_DATA(arr), to, casting); } - /* Otherwise, use the standard rules */ - return PyArray_CanCastTypeTo(from, to, casting); + /* Otherwise, use the standard rules (same as `PyArray_CanCastTypeTo`) */ + int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype); + /* Clear any errors and consider this unsafe (should likely be changed) */ + if (is_valid < 0) { + PyErr_Clear(); + return 0; + } + return is_valid; } @@ -1573,14 +1649,14 @@ PyArray_ResultType( Py_DECREF(all_DTypes[i]); } if (common_dtype == NULL) { - goto finish; + goto error; } if (common_dtype->abstract) { /* (ab)use default descriptor to define a default */ PyArray_Descr *tmp_descr = common_dtype->default_descr(common_dtype); if (tmp_descr == NULL) { - goto finish; + goto error; } Py_INCREF(NPY_DTYPE(tmp_descr)); Py_SETREF(common_dtype, NPY_DTYPE(tmp_descr)); @@ -1613,20 +1689,18 @@ PyArray_ResultType( PyObject *tmp = PyArray_GETITEM( arrs[i-ndtypes], PyArray_BYTES(arrs[i-ndtypes])); if (tmp == NULL) { - Py_SETREF(result, NULL); - goto finish; + goto error; } curr = common_dtype->discover_descr_from_pyobject(common_dtype, tmp); Py_DECREF(tmp); } if (curr == NULL) { - Py_SETREF(result, NULL); - goto finish; + goto error; } Py_SETREF(result, common_dtype->common_instance(result, curr)); Py_DECREF(curr); if (result == NULL) { - goto finish; + goto error; } } } @@ -1647,16 +1721,21 @@ PyArray_ResultType( * Going from error to success should not really happen, but is * probably OK if it does. */ - Py_SETREF(result, NULL); - goto finish; + goto error; } /* Return the old "legacy" result (could warn here if different) */ Py_SETREF(result, legacy_result); } - finish: + Py_DECREF(common_dtype); PyMem_Free(info_on_heap); return result; + + error: + Py_XDECREF(result); + Py_XDECREF(common_dtype); + PyMem_Free(info_on_heap); + return NULL; } @@ -2122,13 +2201,6 @@ PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth) meth->method->name); return -1; } - if ((meth->method->casting & ~_NPY_CAST_IS_VIEW) != NPY_NO_CASTING) { - PyErr_Format(PyExc_TypeError, - "A cast where input and output DType (class) are identical " - "must signal `no-casting`. (method: %s)", - meth->method->name); - return -1; - } if (meth->dtypes[0]->within_dtype_castingimpl != NULL) { PyErr_Format(PyExc_RuntimeError, "A cast was already added for %S -> %S. (method: %s)", @@ -2400,7 +2472,7 @@ add_numeric_cast(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to) /* Find the correct casting level, and special case no-cast */ if (dtypes[0]->kind == dtypes[1]->kind && from_itemsize == to_itemsize) { - spec.casting = NPY_NO_CASTING; + spec.casting = NPY_EQUIV_CASTING; /* When there is no casting (equivalent C-types) use byteswap loops */ slots[0].slot = NPY_METH_resolve_descriptors; @@ -2558,7 +2630,6 @@ cast_to_string_resolve_descriptors( dtypes[1]->type_num == NPY_STRING); return NPY_UNSAFE_CASTING; } - assert(self->casting == NPY_SAFE_CASTING); if (loop_descrs[1]->elsize >= size) { return NPY_SAFE_CASTING; @@ -2600,9 +2671,9 @@ add_other_to_and_from_string_cast( .dtypes = dtypes, .slots = slots, }; - /* Almost everything can be safely cast to string (except unicode) */ + /* Almost everything can be same-kind cast to string (except unicode) */ if (other->type_num != NPY_UNICODE) { - spec.casting = NPY_SAFE_CASTING; + spec.casting = NPY_SAME_KIND_CASTING; /* same-kind if too short */ } else { spec.casting = NPY_UNSAFE_CASTING; @@ -2722,7 +2793,7 @@ PyArray_InitializeStringCasts(void) {0, NULL}}; PyArrayMethod_Spec spec = { .name = "string_to_string_cast", - .casting = NPY_NO_CASTING, + .casting = NPY_UNSAFE_CASTING, .nin = 1, .nout = 1, .flags = (NPY_METH_REQUIRES_PYAPI | @@ -2935,7 +3006,7 @@ PyArray_GetGenericToVoidCastingImpl(void) method->name = "any_to_void_cast"; method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; - method->casting = NPY_SAFE_CASTING; + method->casting = -1; method->resolve_descriptors = &nonstructured_to_structured_resolve_descriptors; method->get_strided_loop = &nonstructured_to_structured_get_loop; @@ -3074,7 +3145,7 @@ PyArray_GetVoidToGenericCastingImpl(void) method->name = "void_to_any_cast"; method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; - method->casting = NPY_UNSAFE_CASTING; + method->casting = -1; method->resolve_descriptors = &structured_to_nonstructured_resolve_descriptors; method->get_strided_loop = &structured_to_nonstructured_get_loop; @@ -3306,7 +3377,7 @@ PyArray_InitializeVoidToVoidCast(void) {0, NULL}}; PyArrayMethod_Spec spec = { .name = "void_to_void_cast", - .casting = NPY_NO_CASTING, + .casting = -1, /* may not cast at all */ .nin = 1, .nout = 1, .flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED, diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index fdf4c0839..b9d81e836 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -950,10 +950,6 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, return -1; } - ind = ((int)meta->base - (int)NPY_FR_Y)*2; - totry = _multiples_table[ind]; - baseunit = _multiples_table[ind + 1]; - num = 3; if (meta->base == NPY_FR_W) { num = 4; @@ -962,6 +958,7 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, num = 2; } if (meta->base >= NPY_FR_s) { + /* _multiplies_table only has entries up to NPY_FR_s */ ind = ((int)NPY_FR_s - (int)NPY_FR_Y)*2; totry = _multiples_table[ind]; baseunit = _multiples_table[ind + 1]; @@ -974,6 +971,11 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, num = 0; } } + else { + ind = ((int)meta->base - (int)NPY_FR_Y)*2; + totry = _multiples_table[ind]; + baseunit = _multiples_table[ind + 1]; + } for (i = 0; i < num; i++) { q = totry[i] / den; @@ -3952,7 +3954,6 @@ time_to_string_resolve_descriptors( return -1; } - assert(self->casting == NPY_UNSAFE_CASTING); return NPY_UNSAFE_CASTING; } @@ -4059,7 +4060,7 @@ PyArray_InitializeDatetimeCasts() .name = "datetime_casts", .nin = 1, .nout = 1, - .casting = NPY_NO_CASTING, + .casting = NPY_UNSAFE_CASTING, .flags = NPY_METH_SUPPORTS_UNALIGNED, .slots = slots, .dtypes = dtypes, diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index f0dfac55d..b8b477e5d 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -3228,7 +3228,9 @@ arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op) { PyArray_Descr *new = _convert_from_any(other, 0); if (new == NULL) { - return NULL; + /* Cannot convert `other` to dtype */ + PyErr_Clear(); + Py_RETURN_NOTIMPLEMENTED; } npy_bool ret; diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c index 40ca9ee2a..4ee721964 100644 --- a/numpy/core/src/multiarray/dtypemeta.c +++ b/numpy/core/src/multiarray/dtypemeta.c @@ -415,19 +415,6 @@ string_unicode_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) Py_INCREF(Py_NotImplemented); return (PyArray_DTypeMeta *)Py_NotImplemented; } - if (other->type_num != NPY_STRING && other->type_num != NPY_UNICODE) { - /* Deprecated 2020-12-19, NumPy 1.21. */ - if (DEPRECATE_FUTUREWARNING( - "Promotion of numbers and bools to strings is deprecated. " - "In the future, code such as `np.concatenate((['string'], [0]))` " - "will raise an error, while `np.asarray(['string', 0])` will " - "return an array with `dtype=object`. To avoid the warning " - "while retaining a string result use `dtype='U'` (or 'S'). " - "To get an array of Python objects use `dtype=object`. " - "(Warning added in NumPy 1.21)") < 0) { - return NULL; - } - } /* * The builtin types are ordered by complexity (aside from object) here. * Arguably, we should not consider numbers and strings "common", but diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index 3575d6fad..bccbb7b0c 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -419,33 +419,13 @@ array_itemsize_get(PyArrayObject *self) static PyObject * array_size_get(PyArrayObject *self) { - npy_intp size=PyArray_SIZE(self); -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyLong_FromLong((long) size); -#else - if (size > NPY_MAX_LONG || size < NPY_MIN_LONG) { - return PyLong_FromLongLong(size); - } - else { - return PyLong_FromLong((long) size); - } -#endif + return PyArray_PyIntFromIntp(PyArray_SIZE(self)); } static PyObject * array_nbytes_get(PyArrayObject *self) { - npy_intp nbytes = PyArray_NBYTES(self); -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyLong_FromLong((long) nbytes); -#else - if (nbytes > NPY_MAX_LONG || nbytes < NPY_MIN_LONG) { - return PyLong_FromLongLong(nbytes); - } - else { - return PyLong_FromLong((long) nbytes); - } -#endif + return PyArray_PyIntFromIntp(PyArray_NBYTES(self)); } diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c index 3ebd4c858..576ea89b3 100644 --- a/numpy/core/src/multiarray/iterators.c +++ b/numpy/core/src/multiarray/iterators.c @@ -15,6 +15,7 @@ #include "iterators.h" #include "ctors.h" #include "common.h" +#include "conversion_utils.h" #include "array_coercion.h" #define NEWAXIS_INDEX -1 @@ -1062,14 +1063,16 @@ static PyMemberDef iter_members[] = { T_OBJECT, offsetof(PyArrayIterObject, ao), READONLY, NULL}, - {"index", - T_INT, - offsetof(PyArrayIterObject, index), - READONLY, NULL}, {NULL, 0, 0, 0, NULL}, }; static PyObject * +iter_index_get(PyArrayIterObject *self) +{ + return PyArray_PyIntFromIntp(self->index); +} + +static PyObject * iter_coords_get(PyArrayIterObject *self) { int nd; @@ -1095,10 +1098,12 @@ iter_coords_get(PyArrayIterObject *self) } static PyGetSetDef iter_getsets[] = { + {"index", + (getter)iter_index_get, + NULL, NULL, NULL}, {"coords", (getter)iter_coords_get, - NULL, - NULL, NULL}, + NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -1410,31 +1415,13 @@ arraymultiter_dealloc(PyArrayMultiIterObject *multi) static PyObject * arraymultiter_size_get(PyArrayMultiIterObject *self) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyLong_FromLong((long) self->size); -#else - if (self->size < NPY_MAX_LONG) { - return PyLong_FromLong((long) self->size); - } - else { - return PyLong_FromLongLong((npy_longlong) self->size); - } -#endif + return PyArray_PyIntFromIntp(self->size); } static PyObject * arraymultiter_index_get(PyArrayMultiIterObject *self) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyLong_FromLong((long) self->index); -#else - if (self->size < NPY_MAX_LONG) { - return PyLong_FromLong((long) self->index); - } - else { - return PyLong_FromLongLong((npy_longlong) self->index); - } -#endif + return PyArray_PyIntFromIntp(self->index); } static PyObject * diff --git a/numpy/core/src/umath/reduction.c b/numpy/core/src/umath/reduction.c index f1423d8b9..86cc20eb1 100644 --- a/numpy/core/src/umath/reduction.c +++ b/numpy/core/src/umath/reduction.c @@ -166,7 +166,7 @@ PyArray_CopyInitialReduceValues( * identity : If Py_None, PyArray_CopyInitialReduceValues is used, otherwise * this value is used to initialize the result to * the reduction's unit. - * loop : The loop which does the reduction. + * loop : `reduce_loop` from `ufunc_object.c`. TODO: Refactor * data : Data which is passed to the inner loop. * buffersize : Buffer size for the iterator. For the default, pass in 0. * funcname : The name of the reduction function, for error messages. @@ -182,18 +182,15 @@ PyArray_CopyInitialReduceValues( * generalized ufuncs!) */ NPY_NO_EXPORT PyArrayObject * -PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, - PyArrayObject *wheremask, - PyArray_Descr *operand_dtype, - PyArray_Descr *result_dtype, - NPY_CASTING casting, - npy_bool *axis_flags, int reorderable, - int keepdims, - PyObject *identity, - PyArray_ReduceLoopFunc *loop, - void *data, npy_intp buffersize, const char *funcname, - int errormask) +PyUFunc_ReduceWrapper( + PyArrayObject *operand, PyArrayObject *out, PyArrayObject *wheremask, + PyArray_Descr *operand_dtype, PyArray_Descr *result_dtype, + NPY_CASTING casting, + npy_bool *axis_flags, int reorderable, int keepdims, + PyObject *identity, PyArray_ReduceLoopFunc *loop, + void *data, npy_intp buffersize, const char *funcname, int errormask) { + assert(loop != NULL); PyArrayObject *result = NULL; npy_intp skip_first_count = 0; @@ -201,7 +198,7 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, NpyIter *iter = NULL; PyArrayObject *op[3]; PyArray_Descr *op_dtypes[3]; - npy_uint32 flags, op_flags[3]; + npy_uint32 it_flags, op_flags[3]; /* More than one axis means multiple orders are possible */ if (!reorderable && count_axes(PyArray_NDIM(operand), axis_flags) > 1) { @@ -227,7 +224,7 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, op_dtypes[0] = result_dtype; op_dtypes[1] = operand_dtype; - flags = NPY_ITER_BUFFERED | + it_flags = NPY_ITER_BUFFERED | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_GROWINNER | NPY_ITER_DONT_NEGATE_STRIDES | @@ -293,7 +290,7 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, } } - iter = NpyIter_AdvancedNew(wheremask == NULL ? 2 : 3, op, flags, + iter = NpyIter_AdvancedNew(wheremask == NULL ? 2 : 3, op, it_flags, NPY_KEEPORDER, casting, op_flags, op_dtypes, @@ -304,11 +301,14 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, result = NpyIter_GetOperandArray(iter)[0]; + int needs_api = NpyIter_IterationNeedsAPI(iter); + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); + /* * Initialize the result to the reduction unit if possible, * otherwise copy the initial values and get a view to the rest. */ - if (identity != Py_None) { if (PyArray_FillWithScalar(result, identity) < 0) { goto fail; @@ -331,15 +331,11 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, goto fail; } - /* Start with the floating-point exception flags cleared */ - npy_clear_floatstatus_barrier((char*)&iter); - if (NpyIter_GetIterSize(iter) != 0) { NpyIter_IterNextFunc *iternext; char **dataptr; npy_intp *strideptr; npy_intp *countptr; - int needs_api; iternext = NpyIter_GetIterNext(iter, NULL); if (iternext == NULL) { @@ -349,16 +345,6 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, strideptr = NpyIter_GetInnerStrideArray(iter); countptr = NpyIter_GetInnerLoopSizePtr(iter); - needs_api = NpyIter_IterationNeedsAPI(iter); - - /* Straightforward reduction */ - if (loop == NULL) { - PyErr_Format(PyExc_RuntimeError, - "reduction operation %s did not supply an " - "inner loop function", funcname); - goto fail; - } - if (loop(iter, dataptr, strideptr, countptr, iternext, needs_api, skip_first_count, data) < 0) { goto fail; diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 0644a28c0..cdb5b720d 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -1263,23 +1263,10 @@ iterator_loop(PyUFuncObject *ufunc, void *innerloopdata, npy_uint32 *op_flags) { - npy_intp i, nin = ufunc->nin, nout = ufunc->nout; - npy_intp nop = nin + nout; - NpyIter *iter; - char *baseptrs[NPY_MAXARGS]; - - NpyIter_IterNextFunc *iternext; - char **dataptr; - npy_intp *stride; - npy_intp *count_ptr; - int needs_api; - - PyArrayObject **op_it; - npy_uint32 iter_flags; - - NPY_BEGIN_THREADS_DEF; + int nin = ufunc->nin, nout = ufunc->nout; + int nop = nin + nout; - iter_flags = ufunc->iter_flags | + npy_uint32 iter_flags = ufunc->iter_flags | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_REFS_OK | NPY_ITER_ZEROSIZE_OK | @@ -1288,16 +1275,17 @@ iterator_loop(PyUFuncObject *ufunc, NPY_ITER_DELAY_BUFALLOC | NPY_ITER_COPY_IF_OVERLAP; - /* Call the __array_prepare__ functions for already existing output arrays. + /* + * Call the __array_prepare__ functions for already existing output arrays. * Do this before creating the iterator, as the iterator may UPDATEIFCOPY * some of them. */ - for (i = 0; i < nout; ++i) { + for (int i = 0; i < nout; i++) { if (op[nin+i] == NULL) { continue; } if (prepare_ufunc_output(ufunc, &op[nin+i], - arr_prep[i], full_args, i) < 0) { + arr_prep[i], full_args, i) < 0) { return -1; } } @@ -1307,7 +1295,7 @@ iterator_loop(PyUFuncObject *ufunc, * were already checked, we use the casting rule 'unsafe' which * is faster to calculate. */ - iter = NpyIter_AdvancedNew(nop, op, + NpyIter *iter = NpyIter_AdvancedNew(nop, op, iter_flags, order, NPY_UNSAFE_CASTING, op_flags, dtype, @@ -1316,16 +1304,20 @@ iterator_loop(PyUFuncObject *ufunc, return -1; } - /* Copy any allocated outputs */ - op_it = NpyIter_GetOperandArray(iter); - for (i = 0; i < nout; ++i) { - if (op[nin+i] == NULL) { - op[nin+i] = op_it[nin+i]; - Py_INCREF(op[nin+i]); + NPY_UF_DBG_PRINT("Made iterator\n"); + + /* Call the __array_prepare__ functions for newly allocated arrays */ + PyArrayObject **op_it = NpyIter_GetOperandArray(iter); + char *baseptrs[NPY_MAXARGS]; + + for (int i = 0; i < nout; ++i) { + if (op[nin + i] == NULL) { + op[nin + i] = op_it[nin + i]; + Py_INCREF(op[nin + i]); /* Call the __array_prepare__ functions for the new array */ - if (prepare_ufunc_output(ufunc, &op[nin+i], - arr_prep[i], full_args, i) < 0) { + if (prepare_ufunc_output(ufunc, + &op[nin + i], arr_prep[i], full_args, i) < 0) { NpyIter_Deallocate(iter); return -1; } @@ -1340,45 +1332,59 @@ iterator_loop(PyUFuncObject *ufunc, * with other operands --- the op[nin+i] array passed to it is newly * allocated and doesn't have any overlap. */ - baseptrs[nin+i] = PyArray_BYTES(op[nin+i]); + baseptrs[nin + i] = PyArray_BYTES(op[nin + i]); } else { - baseptrs[nin+i] = PyArray_BYTES(op_it[nin+i]); + baseptrs[nin + i] = PyArray_BYTES(op_it[nin + i]); } } - /* Only do the loop if the iteration size is non-zero */ - if (NpyIter_GetIterSize(iter) != 0) { - /* Reset the iterator with the base pointers from possible __array_prepare__ */ - for (i = 0; i < nin; ++i) { - baseptrs[i] = PyArray_BYTES(op_it[i]); - } - if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { - NpyIter_Deallocate(iter); + npy_intp full_size = NpyIter_GetIterSize(iter); + if (full_size == 0) { + if (!NpyIter_Deallocate(iter)) { return -1; } + return 0; + } - /* Get the variables needed for the loop */ - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { - NpyIter_Deallocate(iter); - return -1; - } - dataptr = NpyIter_GetDataPtrArray(iter); - stride = NpyIter_GetInnerStrideArray(iter); - count_ptr = NpyIter_GetInnerLoopSizePtr(iter); - needs_api = NpyIter_IterationNeedsAPI(iter); + /* + * Reset the iterator with the base pointers possibly modified by + * `__array_prepare__`. + */ + for (int i = 0; i < nin; i++) { + baseptrs[i] = PyArray_BYTES(op_it[i]); + } + if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { + NpyIter_Deallocate(iter); + return -1; + } - NPY_BEGIN_THREADS_NDITER(iter); + /* Get the variables needed for the loop */ + NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL); + if (iternext == NULL) { + NpyIter_Deallocate(iter); + return -1; + } + char **dataptr = NpyIter_GetDataPtrArray(iter); + npy_intp *strides = NpyIter_GetInnerStrideArray(iter); + npy_intp *countptr = NpyIter_GetInnerLoopSizePtr(iter); + int needs_api = NpyIter_IterationNeedsAPI(iter); - /* Execute the loop */ - do { - NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*count_ptr); - innerloop(dataptr, count_ptr, stride, innerloopdata); - } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); + NPY_BEGIN_THREADS_DEF; - NPY_END_THREADS; + if (!needs_api) { + NPY_BEGIN_THREADS_THRESHOLDED(full_size); } + + NPY_UF_DBG_PRINT("Actual inner loop:\n"); + /* Execute the loop */ + do { + NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*count_ptr); + innerloop(dataptr, countptr, strides, innerloopdata); + } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); + + NPY_END_THREADS; + /* * Currently `innerloop` may leave an error set, in this case * NpyIter_Deallocate will always return an error as well. @@ -1517,24 +1523,24 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc, /* Call the __array_prepare__ functions where necessary */ op_it = NpyIter_GetOperandArray(iter); - for (i = nin; i < nop; ++i) { - PyArrayObject *op_tmp, *orig_op_tmp; + for (i = 0; i < nout; ++i) { + PyArrayObject *op_tmp; /* * The array can be allocated by the iterator -- it is placed in op[i] * and returned to the caller, and this needs an extra incref. */ - if (op[i] == NULL) { - op_tmp = op_it[i]; + if (op[i+nin] == NULL) { + op_tmp = op_it[i+nin]; Py_INCREF(op_tmp); } else { - op_tmp = op[i]; + op_tmp = op[i+nin]; + op[i+nin] = NULL; } /* prepare_ufunc_output may decref & replace the pointer */ - orig_op_tmp = op_tmp; - Py_INCREF(op_tmp); + char *original_data = PyArray_BYTES(op_tmp); if (prepare_ufunc_output(ufunc, &op_tmp, arr_prep[i], full_args, i) < 0) { @@ -1543,7 +1549,7 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc, } /* Validate that the prepare_ufunc_output didn't mess with pointers */ - if (PyArray_BYTES(op_tmp) != PyArray_BYTES(orig_op_tmp)) { + if (PyArray_BYTES(op_tmp) != original_data) { PyErr_SetString(PyExc_ValueError, "The __array_prepare__ functions modified the data " "pointer addresses in an invalid fashion"); @@ -1553,12 +1559,11 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc, } /* - * Put the updated operand back and undo the DECREF above. If - * COPY_IF_OVERLAP made a temporary copy, the output will be copied - * by UPDATEIFCOPY even if op[i] was changed by prepare_ufunc_output. + * Put the updated operand back. If COPY_IF_OVERLAP made a temporary + * copy, the output will be copied by WRITEBACKIFCOPY even if op[i] + * was changed by prepare_ufunc_output. */ - op[i] = op_tmp; - Py_DECREF(op_tmp); + op[i+nin] = op_tmp; } /* Only do the loop if the iteration size is non-zero */ @@ -2105,9 +2110,10 @@ _initialize_variable_parts(PyUFuncObject *ufunc, } static int -PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, - ufunc_full_args full_args, PyObject *type_tup, PyObject *extobj, - NPY_CASTING casting, NPY_ORDER order, npy_bool subok, +PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, + PyArray_Descr *operation_descrs[], + PyArrayObject *op[], PyObject *extobj, + NPY_ORDER order, PyObject *axis, PyObject *axes, int keepdims) { int nin, nout; @@ -2116,8 +2122,6 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, int retval; int needs_api = 0; - PyArray_Descr *dtypes[NPY_MAXARGS]; - /* Use remapped axes for generalized ufunc */ int broadcast_ndim, iter_ndim; int op_core_num_dims[NPY_MAXARGS]; @@ -2148,8 +2152,6 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, /* swapping around of axes */ int *remap_axis_memory = NULL; int **remap_axis = NULL; - /* The __array_prepare__ function to call for each output */ - PyObject *arr_prep[NPY_MAXARGS]; nin = ufunc->nin; nout = ufunc->nout; @@ -2159,11 +2161,6 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); - /* Initialize all dtypes and __array_prepare__ call-backs to NULL */ - for (i = 0; i < nop; ++i) { - dtypes[i] = NULL; - arr_prep[i] = NULL; - } /* Initialize possibly variable parts to the values from the ufunc */ retval = _initialize_variable_parts(ufunc, op_core_num_dims, core_dim_sizes, core_dim_flags); @@ -2369,12 +2366,6 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, NPY_UF_DBG_PRINT("Finding inner loop\n"); - - retval = ufunc->type_resolver(ufunc, casting, - op, type_tup, dtypes); - if (retval < 0) { - goto fail; - } /* * We don't write to all elements, and the iterator may make * UPDATEIFCOPY temporary copies. The output arrays (unless they are @@ -2388,34 +2379,12 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, NPY_UFUNC_DEFAULT_OUTPUT_FLAGS, op_flags); /* For the generalized ufunc, we get the loop right away too */ - retval = ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &innerloop, &innerloopdata, &needs_api); + retval = ufunc->legacy_inner_loop_selector(ufunc, + operation_descrs, &innerloop, &innerloopdata, &needs_api); if (retval < 0) { goto fail; } -#if NPY_UF_DBG_TRACING - printf("input types:\n"); - for (i = 0; i < nin; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\noutput types:\n"); - for (i = nin; i < nop; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\n"); -#endif - - if (subok) { - /* - * Get the appropriate __array_prepare__ function to call - * for each output - */ - _find_array_prepare(full_args, arr_prep, nout); - } - /* * Set up the iterator per-op flags. For generalized ufuncs, we * can't do buffering, so must COPY or UPDATEIFCOPY. @@ -2430,7 +2399,7 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, /* Create the iterator */ iter = NpyIter_AdvancedNew(nop, op, iter_flags, order, NPY_UNSAFE_CASTING, op_flags, - dtypes, iter_ndim, + operation_descrs, iter_ndim, op_axes, iter_shape, 0); if (iter == NULL) { retval = -1; @@ -2589,11 +2558,6 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, retval = -1; } - /* The caller takes ownership of all the references in op */ - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); - } PyArray_free(remap_axis_memory); PyArray_free(remap_axis); @@ -2605,10 +2569,6 @@ fail: NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); PyArray_free(inner_strides); NpyIter_Deallocate(iter); - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); - } PyArray_free(remap_axis_memory); PyArray_free(remap_axis); return retval; @@ -2616,56 +2576,33 @@ fail: static int -PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, - ufunc_full_args full_args, PyObject *type_tup, PyObject *extobj, - NPY_CASTING casting, NPY_ORDER order, npy_bool subok, +PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, + PyArray_Descr *operation_descrs[], + PyArrayObject *op[], PyObject *extobj, NPY_ORDER order, + PyObject *output_array_prepare[], ufunc_full_args full_args, PyArrayObject *wheremask) { - int nin, nout; - int i, nop; - const char *ufunc_name; + int nin = ufunc->nin, nout = ufunc->nout, nop = nin + nout; + + const char *ufunc_name = ufunc_name = ufunc_get_name_cstr(ufunc);; int retval = -1; npy_uint32 op_flags[NPY_MAXARGS]; npy_intp default_op_out_flags; - PyArray_Descr *dtypes[NPY_MAXARGS]; - /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; - /* The __array_prepare__ function to call for each output */ - PyObject *arr_prep[NPY_MAXARGS]; - int trivial_loop_ok = 0; - nin = ufunc->nin; - nout = ufunc->nout; - nop = nin + nout; - - ufunc_name = ufunc_get_name_cstr(ufunc); - NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); - /* Initialize all the dtypes and __array_prepare__ callbacks to NULL */ - for (i = 0; i < nop; ++i) { - dtypes[i] = NULL; - arr_prep[i] = NULL; - } - /* Get the buffersize and errormask */ if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { - retval = -1; - goto fail; + return -1; } NPY_UF_DBG_PRINT("Finding inner loop\n"); - retval = ufunc->type_resolver(ufunc, casting, - op, type_tup, dtypes); - if (retval < 0) { - goto fail; - } - if (wheremask != NULL) { /* Set up the flags. */ default_op_out_flags = NPY_ITER_NO_SUBTYPE | @@ -2682,31 +2619,9 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, default_op_out_flags, op_flags); } -#if NPY_UF_DBG_TRACING - printf("input types:\n"); - for (i = 0; i < nin; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\noutput types:\n"); - for (i = nin; i < nop; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\n"); -#endif - - if (subok) { - /* - * Get the appropriate __array_prepare__ function to call - * for each output - */ - _find_array_prepare(full_args, arr_prep, nout); - } - /* Do the ufunc loop */ if (wheremask != NULL) { - NPY_UF_DBG_PRINT("Executing fancy inner loop\n"); + NPY_UF_DBG_PRINT("Executing masked inner loop\n"); if (nop + 1 > NPY_MAXARGS) { PyErr_SetString(PyExc_ValueError, @@ -2714,14 +2629,15 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, return -1; } op[nop] = wheremask; - dtypes[nop] = NULL; + operation_descrs[nop] = NULL; /* Set up the flags */ npy_clear_floatstatus_barrier((char*)&ufunc); retval = execute_fancy_ufunc_loop(ufunc, wheremask, - op, dtypes, order, - buffersize, arr_prep, full_args, op_flags); + op, operation_descrs, order, + buffersize, output_array_prepare, + full_args, op_flags); } else { NPY_UF_DBG_PRINT("Executing legacy inner loop\n"); @@ -2732,20 +2648,22 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, * Since it requires dtypes, it can only be called after * ufunc->type_resolver */ - trivial_loop_ok = check_for_trivial_loop(ufunc, op, dtypes, buffersize); + trivial_loop_ok = check_for_trivial_loop(ufunc, + op, operation_descrs, buffersize); if (trivial_loop_ok < 0) { - goto fail; + return -1; } /* check_for_trivial_loop on half-floats can overflow */ npy_clear_floatstatus_barrier((char*)&ufunc); retval = execute_legacy_ufunc_loop(ufunc, trivial_loop_ok, - op, dtypes, order, - buffersize, arr_prep, full_args, op_flags); + op, operation_descrs, order, + buffersize, output_array_prepare, + full_args, op_flags); } if (retval < 0) { - goto fail; + return -1; } /* @@ -2755,26 +2673,7 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, */ if (PyErr_Occurred() || _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { - retval = -1; - goto fail; - } - - - /* The caller takes ownership of all the references in op */ - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); - } - - NPY_UF_DBG_PRINT("Returning success code 0\n"); - - return 0; - -fail: - NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); + return -1; } return retval; @@ -3186,7 +3085,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, int idim, ndim, otype_final; int needs_api, need_outer_iterator; - NpyIter *iter = NULL, *iter_inner = NULL; + NpyIter *iter = NULL; /* The selected inner loop */ PyUFuncGenericFunction innerloop = NULL; @@ -3512,9 +3411,6 @@ finish: if (!NpyIter_Deallocate(iter)) { res = -1; } - if (!NpyIter_Deallocate(iter_inner)) { - res = -1; - } if (res < 0) { Py_DECREF(out); return NULL; @@ -3527,7 +3423,6 @@ fail: Py_XDECREF(op_dtypes[0]); NpyIter_Deallocate(iter); - NpyIter_Deallocate(iter_inner); return NULL; } @@ -4263,8 +4158,8 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, */ int typenum = PyArray_TYPE(mp); if ((PyTypeNum_ISBOOL(typenum) || PyTypeNum_ISINTEGER(typenum)) - && ((strcmp(ufunc->name,"add") == 0) - || (strcmp(ufunc->name,"multiply") == 0))) { + && ((strcmp(ufunc->name, "add") == 0) + || (strcmp(ufunc->name, "multiply") == 0))) { if (PyTypeNum_ISBOOL(typenum)) { typenum = NPY_LONG; } @@ -4310,9 +4205,9 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, "reduceat does not allow multiple axes"); goto fail; } - ret = (PyArrayObject *)PyUFunc_Reduceat(ufunc, mp, indices, out, - axes[0], otype->type_num); - Py_DECREF(indices); + ret = (PyArrayObject *)PyUFunc_Reduceat(ufunc, + mp, indices, out, axes[0], otype->type_num); + Py_SETREF(indices, NULL); break; } Py_DECREF(mp); @@ -4354,6 +4249,7 @@ fail: Py_XDECREF(otype); Py_XDECREF(mp); Py_XDECREF(wheremask); + Py_XDECREF(indices); Py_XDECREF(full_args.in); Py_XDECREF(full_args.out); return NULL; @@ -4667,6 +4563,81 @@ _get_normalized_typetup(PyUFuncObject *ufunc, } +/** + * Wraps all outputs and returns the result (which may be NULL on error). + * + * Use __array_wrap__ on all outputs + * if present on one of the input arguments. + * If present for multiple inputs: + * use __array_wrap__ of input object with largest + * __array_priority__ (default = 0.0) + * + * Exception: we should not wrap outputs for items already + * passed in as output-arguments. These items should either + * be left unwrapped or wrapped by calling their own __array_wrap__ + * routine. + * + * For each output argument, wrap will be either + * NULL --- call PyArray_Return() -- default if no output arguments given + * None --- array-object passed in don't call PyArray_Return + * method --- the __array_wrap__ method to call. + * + * @param ufunc + * @param full_args Original inputs and outputs + * @param subok Whether subclasses are allowed + * @param result_arrays The ufunc result(s). REFERENCES ARE STOLEN! + */ +static PyObject * +replace_with_wrapped_result_and_return(PyUFuncObject *ufunc, + ufunc_full_args full_args, npy_bool subok, + PyArrayObject *result_arrays[]) +{ + PyObject *retobj[NPY_MAXARGS]; + PyObject *wraparr[NPY_MAXARGS]; + _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout); + + /* wrap outputs */ + for (int i = 0; i < ufunc->nout; i++) { + _ufunc_context context; + + context.ufunc = ufunc; + context.args = full_args; + context.out_i = i; + + retobj[i] = _apply_array_wrap(wraparr[i], result_arrays[i], &context); + result_arrays[i] = NULL; /* Was DECREF'ed and (probably) wrapped */ + if (retobj[i] == NULL) { + goto fail; + } + } + + if (ufunc->nout == 1) { + return retobj[0]; + } + else { + PyObject *result = PyTuple_New(ufunc->nout); + if (result == NULL) { + return NULL; + } + for (int i = 0; i < ufunc->nout; i++) { + PyTuple_SET_ITEM(result, i, retobj[i]); + } + return result; + } + + fail: + for (int i = 0; i < ufunc->nout; i++) { + if (result_arrays[i] != NULL) { + Py_DECREF(result_arrays[i]); + } + else { + Py_XDECREF(retobj[i]); + } + } + return NULL; +} + + /* * Main ufunc call implementation. * @@ -4681,16 +4652,22 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, npy_bool outer) { - PyArrayObject *operands[NPY_MAXARGS] = {NULL}; - PyObject *retobj[NPY_MAXARGS]; - PyObject *wraparr[NPY_MAXARGS]; - PyObject *override = NULL; - ufunc_full_args full_args = {NULL, NULL}; - PyObject *typetup = NULL; - int errval; int nin = ufunc->nin, nout = ufunc->nout, nop = ufunc->nargs; + /* All following variables are cleared in the `fail` error path */ + ufunc_full_args full_args; + PyArrayObject *wheremask = NULL; + PyObject *typetup = NULL; + + PyArrayObject *operands[NPY_MAXARGS]; + PyArray_Descr *operation_descrs[NPY_MAXARGS]; + PyObject *output_array_prepare[NPY_MAXARGS]; + /* Initialize all arrays (we usually only need a small part) */ + memset(operands, 0, nop * sizeof(*operands)); + memset(operation_descrs, 0, nop * sizeof(*operation_descrs)); + memset(output_array_prepare, 0, nout * sizeof(*output_array_prepare)); + /* * Note that the input (and possibly output) arguments are passed in as * positional arguments. We extract these first and check for `out` @@ -4709,15 +4686,10 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, } /* Fetch input arguments. */ - full_args.in = PyTuple_New(ufunc->nin); + full_args.in = PyArray_TupleFromItems(ufunc->nin, args, 0); if (full_args.in == NULL) { return NULL; } - for (int i = 0; i < ufunc->nin; i++) { - PyObject *tmp = args[i]; - Py_INCREF(tmp); - PyTuple_SET_ITEM(full_args.in, i, tmp); - } /* * If there are more arguments, they define the out args. Otherwise @@ -4838,6 +4810,7 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, method = "outer"; } /* We now have all the information required to check for Overrides */ + PyObject *override = NULL; errval = PyUFunc_CheckOverride(ufunc, method, full_args.in, full_args.out, args, len_args, kwnames, &override); @@ -4871,7 +4844,6 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; npy_bool subok = NPY_TRUE; int keepdims = -1; /* We need to know if it was passed */ - PyArrayObject *wheremask = NULL; if (convert_ufunc_arguments(ufunc, full_args, operands, order_obj, &order, casting_obj, &casting, @@ -4881,15 +4853,30 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, goto fail; } + if (ufunc->type_resolver(ufunc, + casting, operands, typetup, operation_descrs) < 0) { + goto fail; + } + + if (subok) { + _find_array_prepare(full_args, output_array_prepare, nout); + } + + /* + * Do the final preparations and call the inner-loop. + */ if (!ufunc->core_enabled) { - errval = PyUFunc_GenericFunctionInternal(ufunc, operands, - full_args, typetup, extobj, casting, order, subok, + errval = PyUFunc_GenericFunctionInternal(ufunc, + operation_descrs, operands, + extobj, order, + output_array_prepare, full_args, /* for __array_prepare__ */ wheremask); - Py_XDECREF(wheremask); } else { - errval = PyUFunc_GeneralizedFunctionInternal(ufunc, operands, - full_args, typetup, extobj, casting, order, subok, + errval = PyUFunc_GeneralizedFunctionInternal(ufunc, + operation_descrs, operands, + extobj, order, + /* GUFuncs never (ever) called __array_prepare__! */ axis_obj, axes_obj, keepdims); } @@ -4897,74 +4884,40 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, goto fail; } - /* Free the input references */ - for (int i = 0; i < ufunc->nin; i++) { - Py_XSETREF(operands[i], NULL); - } - /* - * Use __array_wrap__ on all outputs - * if present on one of the input arguments. - * If present for multiple inputs: - * use __array_wrap__ of input object with largest - * __array_priority__ (default = 0.0) - * - * Exception: we should not wrap outputs for items already - * passed in as output-arguments. These items should either - * be left unwrapped or wrapped by calling their own __array_wrap__ - * routine. - * - * For each output argument, wrap will be either - * NULL --- call PyArray_Return() -- default if no output arguments given - * None --- array-object passed in don't call PyArray_Return - * method --- the __array_wrap__ method to call. + * Clear all variables which are not needed any further. + * (From here on, we cannot `goto fail` any more.) */ - _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout); - - /* wrap outputs */ - for (int i = 0; i < ufunc->nout; i++) { - int j = ufunc->nin+i; - _ufunc_context context; - PyObject *wrapped; - - context.ufunc = ufunc; - context.args = full_args; - context.out_i = i; - - wrapped = _apply_array_wrap(wraparr[i], operands[j], &context); - operands[j] = NULL; /* Prevent fail double-freeing this */ - if (wrapped == NULL) { - for (int j = 0; j < i; j++) { - Py_DECREF(retobj[j]); - } - goto fail; + Py_XDECREF(wheremask); + for (int i = 0; i < nop; i++) { + Py_DECREF(operation_descrs[i]); + if (i < nin) { + Py_DECREF(operands[i]); + } + else { + Py_XDECREF(output_array_prepare[i-nin]); } - - retobj[i] = wrapped; } - Py_XDECREF(typetup); + + /* The following steals the references to the outputs: */ + PyObject *result = replace_with_wrapped_result_and_return(ufunc, + full_args, subok, operands+nin); Py_XDECREF(full_args.in); Py_XDECREF(full_args.out); - if (ufunc->nout == 1) { - return retobj[0]; - } - else { - PyTupleObject *ret; - - ret = (PyTupleObject *)PyTuple_New(ufunc->nout); - for (int i = 0; i < ufunc->nout; i++) { - PyTuple_SET_ITEM(ret, i, retobj[i]); - } - return (PyObject *)ret; - } + return result; fail: Py_XDECREF(typetup); Py_XDECREF(full_args.in); Py_XDECREF(full_args.out); + Py_XDECREF(wheremask); for (int i = 0; i < ufunc->nargs; i++) { Py_XDECREF(operands[i]); + Py_XDECREF(operation_descrs[i]); + if (i < nout) { + Py_XDECREF(output_array_prepare[i]); + } } return NULL; } diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index 2834235e4..211d837dd 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -94,9 +94,6 @@ raise_no_loop_found_error( PyUFuncObject *ufunc, PyArray_Descr **dtypes) { static PyObject *exc_type = NULL; - PyObject *exc_value; - PyObject *dtypes_tup; - npy_intp i; npy_cache_import( "numpy.core._exceptions", "_UFuncNoLoopError", @@ -105,22 +102,13 @@ raise_no_loop_found_error( return -1; } - /* convert dtypes to a tuple */ - dtypes_tup = PyTuple_New(ufunc->nargs); + PyObject *dtypes_tup = PyArray_TupleFromItems( + ufunc->nargs, (PyObject **)dtypes, 1); if (dtypes_tup == NULL) { return -1; } - for (i = 0; i < ufunc->nargs; ++i) { - PyObject *tmp = Py_None; - if (dtypes[i] != NULL) { - tmp = (PyObject *)dtypes[i]; - } - Py_INCREF(tmp); - PyTuple_SET_ITEM(dtypes_tup, i, tmp); - } - /* produce an error object */ - exc_value = PyTuple_Pack(2, ufunc, dtypes_tup); + PyObject *exc_value = PyTuple_Pack(2, ufunc, dtypes_tup); Py_DECREF(dtypes_tup); if (exc_value == NULL) { return -1; diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 9e99e0bc3..291cdae89 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -281,6 +281,19 @@ def test_array_astype(): a = np.array(1000, dtype='i4') assert_raises(TypeError, a.astype, 'U1', casting='safe') +@pytest.mark.parametrize("dt", ["S", "U"]) +def test_array_astype_to_string_discovery_empty(dt): + # See also gh-19085 + arr = np.array([""], dtype=object) + # Note, the itemsize is the `0 -> 1` logic, which should change. + # The important part the test is rather that it does not error. + assert arr.astype(dt).dtype.itemsize == np.dtype(f"{dt}1").itemsize + + # check the same thing for `np.can_cast` (since it accepts arrays) + assert np.can_cast(arr, dt, casting="unsafe") + assert not np.can_cast(arr, dt, casting="same_kind") + # as well as for the object as a descriptor: + assert np.can_cast("O", dt, casting="unsafe") @pytest.mark.parametrize("dt", ["d", "f", "S13", "U32"]) def test_array_astype_to_void(dt): diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index ffe0147b2..42e632e4a 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -1105,41 +1105,6 @@ class TestNoseDecoratorsDeprecated(_DeprecationTestCase): self.assert_deprecated(_test_parametrize) -class TestStringPromotion(_DeprecationTestCase): - # Deprecated 2020-12-19, NumPy 1.21 - warning_cls = FutureWarning - message = "Promotion of numbers and bools to strings is deprecated." - - @pytest.mark.parametrize("dtype", "?bhilqpBHILQPefdgFDG") - @pytest.mark.parametrize("string_dt", ["S", "U"]) - def test_deprecated(self, dtype, string_dt): - self.assert_deprecated(lambda: np.promote_types(dtype, string_dt)) - - # concatenate has to be able to promote to find the result dtype: - arr1 = np.ones(3, dtype=dtype) - arr2 = np.ones(3, dtype=string_dt) - self.assert_deprecated(lambda: np.concatenate((arr1, arr2), axis=0)) - self.assert_deprecated(lambda: np.concatenate((arr1, arr2), axis=None)) - - self.assert_deprecated(lambda: np.array([arr1[0], arr2[0]])) - - @pytest.mark.parametrize("dtype", "?bhilqpBHILQPefdgFDG") - @pytest.mark.parametrize("string_dt", ["S", "U"]) - def test_not_deprecated(self, dtype, string_dt): - # The ufunc type resolvers run into this, but giving a futurewarning - # here is unnecessary (it ends up as an error anyway), so test that - # no warning is given: - arr1 = np.ones(3, dtype=dtype) - arr2 = np.ones(3, dtype=string_dt) - - # Adding two arrays uses result_type normally, which would fail: - with pytest.raises(TypeError): - self.assert_not_deprecated(lambda: arr1 + arr2) - # np.equal uses a different type resolver: - with pytest.raises(TypeError): - self.assert_not_deprecated(lambda: np.equal(arr1, arr2)) - - class TestSingleElementSignature(_DeprecationTestCase): # Deprecated 2021-04-01, NumPy 1.21 message = r"The use of a length 1" diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 8a6b7dcd5..3d15009ea 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -88,6 +88,24 @@ class TestBuiltin: assert_raises(TypeError, np.dtype, 'q8') assert_raises(TypeError, np.dtype, 'Q8') + def test_richcompare_invalid_dtype_equality(self): + # Make sure objects that cannot be converted to valid + # dtypes results in False/True when compared to valid dtypes. + # Here 7 cannot be converted to dtype. No exceptions should be raised + + assert not np.dtype(np.int32) == 7, "dtype richcompare failed for ==" + assert np.dtype(np.int32) != 7, "dtype richcompare failed for !=" + + @pytest.mark.parametrize( + 'operation', + [operator.le, operator.lt, operator.ge, operator.gt]) + def test_richcompare_invalid_dtype_comparison(self, operation): + # Make sure TypeError is raised for comparison operators + # for invalid dtypes. Here 7 is an invalid dtype. + + with pytest.raises(TypeError): + operation(np.dtype(np.int32), 7) + @pytest.mark.parametrize("dtype", ['Bool', 'Complex32', 'Complex64', 'Float16', 'Float32', 'Float64', 'Int8', 'Int16', 'Int32', 'Int64', 'Object0', 'Timedelta64', diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py index 449a01d21..1b6fd21e1 100644 --- a/numpy/core/tests/test_half.py +++ b/numpy/core/tests/test_half.py @@ -71,10 +71,8 @@ class TestHalf: def test_half_conversion_to_string(self, string_dt): # Currently uses S/U32 (which is sufficient for float32) expected_dt = np.dtype(f"{string_dt}32") - with pytest.warns(FutureWarning): - assert np.promote_types(np.float16, string_dt) == expected_dt - with pytest.warns(FutureWarning): - assert np.promote_types(string_dt, np.float16) == expected_dt + assert np.promote_types(np.float16, string_dt) == expected_dt + assert np.promote_types(string_dt, np.float16) == expected_dt arr = np.ones(3, dtype=np.float16).astype(string_dt) assert arr.dtype == expected_dt diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index d567653f5..7ee7253ef 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -5364,6 +5364,17 @@ class TestFlat: assert_(abs(sys.getrefcount(ind) - rc_ind) < 50) assert_(abs(sys.getrefcount(indtype) - rc_indtype) < 50) + def test_index_getset(self): + it = np.arange(10).reshape(2, 1, 5).flat + with pytest.raises(AttributeError): + it.index = 10 + + for _ in it: + pass + # Check the value of `.index` is updated correctly (see also gh-19153) + # If the type was incorrect, this would show up on big-endian machines + assert it.index == it.base.size + class TestResize: @@ -6723,7 +6734,7 @@ class TestMatmulOperator(MatmulCommon): def test_matmul_raises(self): assert_raises(TypeError, self.matmul, np.int8(5), np.int8(5)) assert_raises(TypeError, self.matmul, np.void(b'abc'), np.void(b'abc')) - assert_raises(ValueError, self.matmul, np.arange(10), np.void(b'abc')) + assert_raises(TypeError, self.matmul, np.arange(10), np.void(b'abc')) def test_matmul_inplace(): # It would be nice to support in-place matmul eventually, but for now @@ -6947,6 +6958,13 @@ class TestNeighborhoodIter: x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant']) assert_array_equal(l, r) + # Test with start in the middle + r = [np.array([[4, 0, 1], [4, 2, 3]], dtype=dt), + np.array([[0, 1, 4], [2, 3, 4]], dtype=dt)] + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant'], 2) + assert_array_equal(l, r) + def test_mirror2d(self, dt): x = np.array([[0, 1], [2, 3]], dtype=dt) r = [np.array([[0, 0, 1], [0, 0, 1]], dtype=dt), diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index aba90ece5..f5113150e 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -848,12 +848,10 @@ class TestTypes: assert_equal(np.promote_types('<i8', '<i8'), np.dtype('i8')) assert_equal(np.promote_types('>i8', '>i8'), np.dtype('i8')) - with pytest.warns(FutureWarning, - match="Promotion of numbers and bools to strings"): - assert_equal(np.promote_types('>i8', '>U16'), np.dtype('U21')) - assert_equal(np.promote_types('<i8', '<U16'), np.dtype('U21')) - assert_equal(np.promote_types('>U16', '>i8'), np.dtype('U21')) - assert_equal(np.promote_types('<U16', '<i8'), np.dtype('U21')) + assert_equal(np.promote_types('>i8', '>U16'), np.dtype('U21')) + assert_equal(np.promote_types('<i8', '<U16'), np.dtype('U21')) + assert_equal(np.promote_types('>U16', '>i8'), np.dtype('U21')) + assert_equal(np.promote_types('<U16', '<i8'), np.dtype('U21')) assert_equal(np.promote_types('<S5', '<U8'), np.dtype('U8')) assert_equal(np.promote_types('>S5', '>U8'), np.dtype('U8')) @@ -901,37 +899,32 @@ class TestTypes: S = string_dtype - with pytest.warns(FutureWarning, - match="Promotion of numbers and bools to strings") as record: - # Promote numeric with unsized string: - assert_equal(promote_types('bool', S), np.dtype(S+'5')) - assert_equal(promote_types('b', S), np.dtype(S+'4')) - assert_equal(promote_types('u1', S), np.dtype(S+'3')) - assert_equal(promote_types('u2', S), np.dtype(S+'5')) - assert_equal(promote_types('u4', S), np.dtype(S+'10')) - assert_equal(promote_types('u8', S), np.dtype(S+'20')) - assert_equal(promote_types('i1', S), np.dtype(S+'4')) - assert_equal(promote_types('i2', S), np.dtype(S+'6')) - assert_equal(promote_types('i4', S), np.dtype(S+'11')) - assert_equal(promote_types('i8', S), np.dtype(S+'21')) - # Promote numeric with sized string: - assert_equal(promote_types('bool', S+'1'), np.dtype(S+'5')) - assert_equal(promote_types('bool', S+'30'), np.dtype(S+'30')) - assert_equal(promote_types('b', S+'1'), np.dtype(S+'4')) - assert_equal(promote_types('b', S+'30'), np.dtype(S+'30')) - assert_equal(promote_types('u1', S+'1'), np.dtype(S+'3')) - assert_equal(promote_types('u1', S+'30'), np.dtype(S+'30')) - assert_equal(promote_types('u2', S+'1'), np.dtype(S+'5')) - assert_equal(promote_types('u2', S+'30'), np.dtype(S+'30')) - assert_equal(promote_types('u4', S+'1'), np.dtype(S+'10')) - assert_equal(promote_types('u4', S+'30'), np.dtype(S+'30')) - assert_equal(promote_types('u8', S+'1'), np.dtype(S+'20')) - assert_equal(promote_types('u8', S+'30'), np.dtype(S+'30')) - # Promote with object: - assert_equal(promote_types('O', S+'30'), np.dtype('O')) - - assert len(record) == 22 # each string promotion gave one warning - + # Promote numeric with unsized string: + assert_equal(promote_types('bool', S), np.dtype(S+'5')) + assert_equal(promote_types('b', S), np.dtype(S+'4')) + assert_equal(promote_types('u1', S), np.dtype(S+'3')) + assert_equal(promote_types('u2', S), np.dtype(S+'5')) + assert_equal(promote_types('u4', S), np.dtype(S+'10')) + assert_equal(promote_types('u8', S), np.dtype(S+'20')) + assert_equal(promote_types('i1', S), np.dtype(S+'4')) + assert_equal(promote_types('i2', S), np.dtype(S+'6')) + assert_equal(promote_types('i4', S), np.dtype(S+'11')) + assert_equal(promote_types('i8', S), np.dtype(S+'21')) + # Promote numeric with sized string: + assert_equal(promote_types('bool', S+'1'), np.dtype(S+'5')) + assert_equal(promote_types('bool', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('b', S+'1'), np.dtype(S+'4')) + assert_equal(promote_types('b', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u1', S+'1'), np.dtype(S+'3')) + assert_equal(promote_types('u1', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u2', S+'1'), np.dtype(S+'5')) + assert_equal(promote_types('u2', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u4', S+'1'), np.dtype(S+'10')) + assert_equal(promote_types('u4', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u8', S+'1'), np.dtype(S+'20')) + assert_equal(promote_types('u8', S+'30'), np.dtype(S+'30')) + # Promote with object: + assert_equal(promote_types('O', S+'30'), np.dtype('O')) @pytest.mark.parametrize(["dtype1", "dtype2"], [[np.dtype("V6"), np.dtype("V10")], diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index dbfb75c9a..312d0683d 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -782,9 +782,7 @@ class TestRegression: # Ticket #514 s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" t = [] - with pytest.warns(FutureWarning, - match="Promotion of numbers and bools to strings"): - np.hstack((t, s)) + np.hstack((t, s)) def test_arr_transpose(self): # Ticket #516 diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index f438b622a..e6fcf5bd4 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -4,6 +4,7 @@ import fnmatch import itertools import pytest import sys +import os from fractions import Fraction from functools import reduce @@ -17,6 +18,20 @@ from numpy.testing import ( _gen_alignment_data, assert_array_almost_equal_nulp, assert_warns ) +def get_glibc_version(): + try: + ver = os.confstr('CS_GNU_LIBC_VERSION').rsplit(' ')[1] + except Exception as inst: + ver = '0.0' + + return ver + + +glibcver = get_glibc_version() +glibc_newerthan_2_17 = pytest.mark.xfail( + glibcver != '0.0' and glibcver < '2.17', + reason="Older glibc versions may not raise appropriate FP exceptions") + def on_powerpc(): """ True if we are running on a Power PC platform.""" return platform.processor() == 'powerpc' or \ @@ -984,6 +999,10 @@ class TestSpecialFloats: yf = np.array(y, dtype=dt) assert_equal(np.exp(yf), xf) + # Older version of glibc may not raise the correct FP exceptions + # See: https://github.com/numpy/numpy/issues/19192 + @glibc_newerthan_2_17 + def test_exp_exceptions(self): with np.errstate(over='raise'): assert_raises(FloatingPointError, np.exp, np.float32(100.)) assert_raises(FloatingPointError, np.exp, np.float32(1E19)) @@ -2081,6 +2100,10 @@ class TestSpecialMethods: do_test(lambda a: np.add(0, 0, out=a), lambda a: (0, 0, a)) do_test(lambda a: np.add(0, 0, out=(a,)), lambda a: (0, 0, a)) + # Also check the where mask handling: + do_test(lambda a: np.add(a, 0, where=False), lambda a: (a, 0)) + do_test(lambda a: np.add(0, 0, a, where=False), lambda a: (0, 0, a)) + def test_wrap_with_iterable(self): # test fix for bug #1026: @@ -2230,7 +2253,8 @@ class TestSpecialMethods: assert_equal(x, np.zeros(1)) assert_equal(type(x), np.ndarray) - def test_prepare(self): + @pytest.mark.parametrize("use_where", [True, False]) + def test_prepare(self, use_where): class with_prepare(np.ndarray): __array_priority__ = 10 @@ -2240,11 +2264,18 @@ class TestSpecialMethods: return np.array(arr).view(type=with_prepare) a = np.array(1).view(type=with_prepare) - x = np.add(a, a) + if use_where: + # Currently raises, due to the array being replaced during prepare + with pytest.raises(ValueError): + x = np.add(a, a, where=np.array(True)) + return + else: + x = np.add(a, a) assert_equal(x, np.array(2)) assert_equal(type(x), with_prepare) - def test_prepare_out(self): + @pytest.mark.parametrize("use_where", [True, False]) + def test_prepare_out(self, use_where): class with_prepare(np.ndarray): __array_priority__ = 10 @@ -2253,7 +2284,13 @@ class TestSpecialMethods: return np.array(arr).view(type=with_prepare) a = np.array([1]).view(type=with_prepare) - x = np.add(a, a, a) + if use_where: + # Currently raises, due to the array being replaced during prepare + with pytest.raises(ValueError): + x = np.add(a, a, a, where=[True]) + return + else: + x = np.add(a, a, a) # Returned array is new, because of the strange # __array_prepare__ above assert_(not np.shares_memory(x, a)) @@ -2271,6 +2308,7 @@ class TestSpecialMethods: a = A() assert_raises(RuntimeError, ncu.maximum, a, a) + assert_raises(RuntimeError, ncu.maximum, a, a, where=False) def test_array_too_many_args(self): diff --git a/numpy/distutils/intelccompiler.py b/numpy/distutils/intelccompiler.py index 0388ad577..0fa1c11dd 100644 --- a/numpy/distutils/intelccompiler.py +++ b/numpy/distutils/intelccompiler.py @@ -58,7 +58,7 @@ class IntelEM64TCCompiler(UnixCCompiler): v = self.get_version() mpopt = 'openmp' if v and v < '15' else 'qopenmp' - self.cc_exe = ('icc -m64 -fPIC -fp-model strict -O3 ' + self.cc_exe = ('icc -std=c99 -m64 -fPIC -fp-model strict -O3 ' '-fomit-frame-pointer -{}').format(mpopt) compiler = self.cc_exe diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py index 60685be8a..714f9a932 100644 --- a/numpy/f2py/cfuncs.py +++ b/numpy/f2py/cfuncs.py @@ -479,6 +479,27 @@ cppmacros['STRINGMALLOC'] = """\ cppmacros['STRINGFREE'] = """\ #define STRINGFREE(str) do {if (!(str == NULL)) free(str);} while (0) """ +needs['STRINGPADN'] = ['string.h'] +cppmacros['STRINGPADN'] = """\ +/* +STRINGPADN replaces null values with padding values from the right. + +`to` must have size of at least N bytes. + +If the `to[N-1]` has null value, then replace it and all the +preceeding nulls with the given padding. + +STRINGPADN(to, N, PADDING, NULLVALUE) is an inverse operation. +*/ +#define STRINGPADN(to, N, NULLVALUE, PADDING) \\ + do { \\ + int _m = (N); \\ + char *_to = (to); \\ + for (_m -= 1; _m >= 0 && _to[_m] == NULLVALUE; _m--) { \\ + _to[_m] = PADDING; \\ + } \\ + } while (0) +""" needs['STRINGCOPYN'] = ['string.h', 'FAILNULL'] cppmacros['STRINGCOPYN'] = """\ /* @@ -665,7 +686,8 @@ cfuncs['string_from_pyobj'] = """\ The string buffer has given size (len) or the size of inistr when len==-1. - The string buffer is null-terminated. + The string buffer is padded with blanks: in Fortran, trailing blanks + are insignificant contrary to C nulls. */ static int string_from_pyobj(string *str, int *len, const string inistr, PyObject *obj, @@ -691,6 +713,7 @@ fprintf(stderr,\"string_from_pyobj(str='%s',len=%d,inistr='%s',obj=%p)\\n\", } n = PyArray_NBYTES(arr); buf = PyArray_DATA(arr); + n = strnlen(buf, n); } else { if (PyBytes_Check(obj)) { @@ -726,14 +749,18 @@ fprintf(stderr,\"string_from_pyobj(str='%s',len=%d,inistr='%s',obj=%p)\\n\", } else if (*len < n) { /* discard the last (len-n) bytes of input buf */ - n = *len; + n = *len; } if (n < 0 || *len < 0 || buf == NULL) { goto capi_fail; } STRINGMALLOC(*str, *len); // *str is allocated with size (*len + 1) if (n < *len) { - /* Pad fixed-width string with nulls */ + /* + Pad fixed-width string with nulls. The caller will replace + nulls with blanks when the corresponding argument is not + intent(c). + */ memset(*str + n, '\\0', *len - n); } STRINGCOPYN(*str, buf, n); diff --git a/numpy/f2py/rules.py b/numpy/f2py/rules.py index f01b2bcfa..587ae2e5f 100755 --- a/numpy/f2py/rules.py +++ b/numpy/f2py/rules.py @@ -943,19 +943,35 @@ if (#varname#_cb.capi==Py_None) { '\tPyObject *#varname#_capi = Py_None;'], 'callfortran':'#varname#,', 'callfortranappend':'slen(#varname#),', - 'pyobjfrom':{debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, + 'pyobjfrom':[ + {debugcapi: + '\tfprintf(stderr,' + '"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, + # The trailing null value for Fortran is blank. + {l_and(isintent_out, l_not(isintent_c)): + "\t\tSTRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"}, + ], 'return': {isintent_out: ',#varname#'}, - 'need': ['len..'], # 'STRINGFREE'], + 'need': ['len..', + {l_and(isintent_out, l_not(isintent_c)): 'STRINGPADN'}], '_check':isstring }, { # Common - 'frompyobj': """\ + 'frompyobj': [ + """\ \tslen(#varname#) = #length#; -\tf2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth# `#varname#\' of #pyname# to C #ctype#\"); +\tf2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,""" +"""#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth#""" +"""`#varname#\' of #pyname# to C #ctype#\"); \tif (f2py_success) {""", + # The trailing null value for Fortran is blank. + {l_not(isintent_c): + "\t\tSTRINGPADN(#varname#, slen(#varname#), '\\0', ' ');"}, + ], 'cleanupfrompyobj': """\ \t\tSTRINGFREE(#varname#); \t} /*if (f2py_success) of #varname#*/""", - 'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE'], + 'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE', + {l_not(isintent_c): 'STRINGPADN'}], '_check':isstring, '_depend':'' }, { # Not hidden @@ -963,12 +979,16 @@ if (#varname#_cb.capi==Py_None) { 'keyformat': {isoptional: 'O'}, 'args_capi': {isrequired: ',&#varname#_capi'}, 'keys_capi': {isoptional: ',&#varname#_capi'}, - 'pyobjfrom': {isintent_inout: '''\ + 'pyobjfrom': [ + {l_and(isintent_inout, l_not(isintent_c)): + "\t\tSTRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"}, + {isintent_inout: '''\ \tf2py_success = try_pyarr_from_#ctype#(#varname#_capi, #varname#, \t slen(#varname#)); -\tif (f2py_success) {'''}, +\tif (f2py_success) {'''}], 'closepyobjfrom': {isintent_inout: '\t} /*if (f2py_success) of #varname# pyobjfrom*/'}, - 'need': {isintent_inout: 'try_pyarr_from_#ctype#'}, + 'need': {isintent_inout: 'try_pyarr_from_#ctype#', + l_and(isintent_inout, l_not(isintent_c)): 'STRINGPADN'}, '_check': l_and(isstring, isintent_nothide) }, { # Hidden '_check': l_and(isstring, isintent_hide) diff --git a/numpy/f2py/tests/test_return_character.py b/numpy/f2py/tests/test_return_character.py index dc524e60c..7d4ced914 100644 --- a/numpy/f2py/tests/test_return_character.py +++ b/numpy/f2py/tests/test_return_character.py @@ -21,7 +21,7 @@ class TestReturnCharacter(util.F2PyTest): #assert_(_raises(ValueError, t, array([77,87]))) #assert_(_raises(ValueError, t, array(77))) elif tname in ['ts', 'ss']: - assert_(t(23) == b'23 ', repr(t(23))) + assert_(t(23) == b'23', repr(t(23))) assert_(t('123456789abcdef') == b'123456789a') elif tname in ['t5', 's5']: assert_(t(23) == b'23', repr(t(23))) diff --git a/numpy/f2py/tests/test_string.py b/numpy/f2py/tests/test_string.py index 1530c58f6..7b27f8786 100644 --- a/numpy/f2py/tests/test_string.py +++ b/numpy/f2py/tests/test_string.py @@ -74,8 +74,10 @@ class TestFixedString(util.F2PyTest): character(len=*) :: s integer :: j, i i = 0 - do j=1, len(s) - i = i + ichar(s(j:j)) * 10 ** (j - 1) + do j=len(s), 1, -1 + if (.not.((i.eq.0).and.(s(j:j).eq.' '))) then + i = i + ichar(s(j:j)) * 10 ** (j - 1) + endif end do return end function sint @@ -95,7 +97,7 @@ class TestFixedString(util.F2PyTest): integer :: sint character(len=4), intent(inout) :: a integer :: i - if (ichar(a(1:1)).ne.0) then + if (a(1:1).ne.' ') then a(1:1) = 'E' endif i = sint(a) diff --git a/numpy/lib/_version.py b/numpy/lib/_version.py index 00e00e9a7..bfac5f814 100644 --- a/numpy/lib/_version.py +++ b/numpy/lib/_version.py @@ -15,7 +15,7 @@ class NumpyVersion(): """Parse and compare numpy version strings. NumPy has the following versioning scheme (numbers given are examples; they - can be > 9) in principle): + can be > 9 in principle): - Released version: '1.8.0', '1.8.1', etc. - Alpha: '1.8.0a1', '1.8.0a2', etc. @@ -54,7 +54,7 @@ class NumpyVersion(): def __init__(self, vstring): self.vstring = vstring - ver_main = re.match(r'\d\.\d+\.\d+', vstring) + ver_main = re.match(r'\d+\.\d+\.\d+', vstring) if not ver_main: raise ValueError("Not a valid numpy version string") diff --git a/numpy/lib/arraypad.pyi b/numpy/lib/arraypad.pyi index 64e3e1331..df9538dd7 100644 --- a/numpy/lib/arraypad.pyi +++ b/numpy/lib/arraypad.pyi @@ -1,5 +1,94 @@ -from typing import List +import sys +from typing import ( + Any, + Dict, + List, + overload, + Tuple, + TypeVar, +) + +from numpy import ndarray, dtype, generic + +from numpy.typing import ( + ArrayLike, + NDArray, + _ArrayLikeInt, + _NestedSequence, + _SupportsArray, +) + +if sys.version_info >= (3, 8): + from typing import Literal as L, Protocol +else: + from typing_extensions import Literal as L, Protocol + +_SCT = TypeVar("_SCT", bound=generic) + +class _ModeFunc(Protocol): + def __call__( + self, + __vector: NDArray[Any], + __iaxis_pad_width: Tuple[int, int], + __iaxis: int, + __kwargs: Dict[str, Any], + ) -> None: ... + +_ModeKind = L[ + "constant", + "edge", + "linear_ramp", + "maximum", + "mean", + "median", + "minimum", + "reflect", + "symmetric", + "wrap", + "empty", +] + +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] __all__: List[str] -def pad(array, pad_width, mode=..., **kwargs): ... +# TODO: In practice each keyword argument is exclusive to one or more +# specific modes. Consider adding more overloads to express this in the future. + +# Expand `**kwargs` into explicit keyword-only arguments +@overload +def pad( + array: _ArrayLike[_SCT], + pad_width: _ArrayLikeInt, + mode: _ModeKind = ..., + *, + stat_length: None | _ArrayLikeInt = ..., + constant_values: ArrayLike = ..., + end_values: ArrayLike = ..., + reflect_type: L["odd", "even"] = ..., +) -> NDArray[_SCT]: ... +@overload +def pad( + array: ArrayLike, + pad_width: _ArrayLikeInt, + mode: _ModeKind = ..., + *, + stat_length: None | _ArrayLikeInt = ..., + constant_values: ArrayLike = ..., + end_values: ArrayLike = ..., + reflect_type: L["odd", "even"] = ..., +) -> NDArray[Any]: ... +@overload +def pad( + array: _ArrayLike[_SCT], + pad_width: _ArrayLikeInt, + mode: _ModeFunc, + **kwargs: Any, +) -> NDArray[_SCT]: ... +@overload +def pad( + array: ArrayLike, + pad_width: _ArrayLikeInt, + mode: _ModeFunc, + **kwargs: Any, +) -> NDArray[Any]: ... diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 783d45c2f..2e9ae6644 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -88,8 +88,11 @@ def rot90(m, k=1, axes=(0, 1)): Notes ----- - rot90(m, k=1, axes=(1,0)) is the reverse of rot90(m, k=1, axes=(0,1)) - rot90(m, k=1, axes=(1,0)) is equivalent to rot90(m, k=-1, axes=(0,1)) + ``rot90(m, k=1, axes=(1,0))`` is the reverse of + ``rot90(m, k=1, axes=(0,1))`` + + ``rot90(m, k=1, axes=(1,0))`` is equivalent to + ``rot90(m, k=-1, axes=(0,1))`` Examples -------- diff --git a/numpy/lib/function_base.pyi b/numpy/lib/function_base.pyi index da24ab21d..69c615c9c 100644 --- a/numpy/lib/function_base.pyi +++ b/numpy/lib/function_base.pyi @@ -30,7 +30,7 @@ def gradient(f, *varargs, axis=..., edge_order=...): ... def diff(a, n=..., axis=..., prepend = ..., append = ...): ... def interp(x, xp, fp, left=..., right=..., period=...): ... def angle(z, deg=...): ... -def unwrap(p, discont = ..., axis=...): ... +def unwrap(p, discont = ..., axis=..., *, period=...): ... def sort_complex(a): ... def trim_zeros(filt, trim=...): ... def extract(condition, arr): ... diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index 72d8e9de4..5140ffa61 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -631,7 +631,8 @@ class ndindex: Examples -------- - # dimensions as individual arguments + Dimensions as individual arguments + >>> for index in np.ndindex(3, 2, 1): ... print(index) (0, 0, 0) @@ -641,7 +642,8 @@ class ndindex: (2, 0, 0) (2, 1, 0) - # same dimensions - but in a tuple (3, 2, 1) + Same dimensions - but in a tuple ``(3, 2, 1)`` + >>> for index in np.ndindex((3, 2, 1)): ... print(index) (0, 0, 0) diff --git a/numpy/lib/index_tricks.pyi b/numpy/lib/index_tricks.pyi index a3bfef6b6..0f9ae94a9 100644 --- a/numpy/lib/index_tricks.pyi +++ b/numpy/lib/index_tricks.pyi @@ -44,6 +44,11 @@ from numpy.typing import ( _ShapeLike, ) +from numpy.core.multiarray import ( + unravel_index as unravel_index, + ravel_multi_index as ravel_multi_index, +) + if sys.version_info >= (3, 8): from typing import Literal, SupportsIndex else: @@ -58,34 +63,6 @@ _ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) __all__: List[str] @overload -def unravel_index( # type: ignore[misc] - indices: Union[int, integer[Any]], - shape: _ShapeLike, - order: _OrderCF = ... -) -> Tuple[intp, ...]: ... -@overload -def unravel_index( - indices: _ArrayLikeInt, - shape: _ShapeLike, - order: _OrderCF = ... -) -> Tuple[NDArray[intp], ...]: ... - -@overload -def ravel_multi_index( # type: ignore[misc] - multi_index: Sequence[Union[int, integer[Any]]], - dims: _ShapeLike, - mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., - order: _OrderCF = ... -) -> intp: ... -@overload -def ravel_multi_index( - multi_index: Sequence[_ArrayLikeInt], - dims: _ShapeLike, - mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., - order: _OrderCF = ... -) -> NDArray[intp]: ... - -@overload def ix_(*args: _NestedSequence[_SupportsDType[_DType]]) -> Tuple[ndarray[Any, _DType], ...]: ... @overload def ix_(*args: _NestedSequence[str]) -> Tuple[NDArray[str_], ...]: ... diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 56fcce621..23021cafa 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -489,8 +489,11 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (M,), optional - Weights to apply to the y-coordinates of the sample points. For - gaussian uncertainties, use 1/sigma (not 1/sigma**2). + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. cov : bool or str, optional If given and not `False`, return not just the estimate but also its covariance matrix. By default, the covariance are scaled by @@ -498,7 +501,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): to be unreliable except in a relative sense and everything is scaled such that the reduced chi2 is unity. This scaling is omitted if ``cov='unscaled'``, as is relevant for the case that the weights are - 1/sigma**2, with sigma known to be a reliable estimate of the + w = 1/sigma, with sigma known to be a reliable estimate of the uncertainty. Returns diff --git a/numpy/lib/tests/test__version.py b/numpy/lib/tests/test__version.py index 182504631..e6d41ad93 100644 --- a/numpy/lib/tests/test__version.py +++ b/numpy/lib/tests/test__version.py @@ -7,7 +7,7 @@ from numpy.lib import NumpyVersion def test_main_versions(): assert_(NumpyVersion('1.8.0') == '1.8.0') - for ver in ['1.9.0', '2.0.0', '1.8.1']: + for ver in ['1.9.0', '2.0.0', '1.8.1', '10.0.1']: assert_(NumpyVersion('1.8.0') < ver) for ver in ['1.7.0', '1.7.1', '0.9.9']: diff --git a/numpy/lib/tests/test_regression.py b/numpy/lib/tests/test_regression.py index 94fac7ef0..373226277 100644 --- a/numpy/lib/tests/test_regression.py +++ b/numpy/lib/tests/test_regression.py @@ -64,8 +64,7 @@ class TestRegression: def test_mem_string_concat(self): # Ticket #469 x = np.array([]) - with pytest.warns(FutureWarning): - np.append(x, 'asdasd\tasdasd') + np.append(x, 'asdasd\tasdasd') def test_poly_div(self): # Ticket #553 diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py index ac5fe1c6b..83c028061 100644 --- a/numpy/lib/twodim_base.py +++ b/numpy/lib/twodim_base.py @@ -439,10 +439,12 @@ def tril(m, k=0): Lower triangle of an array. Return a copy of an array with elements above the `k`-th diagonal zeroed. + For arrays with ``ndim`` exceeding 2, `tril` will apply to the final two + axes. Parameters ---------- - m : array_like, shape (M, N) + m : array_like, shape (..., M, N) Input array. k : int, optional Diagonal above which to zero elements. `k = 0` (the default) is the @@ -450,7 +452,7 @@ def tril(m, k=0): Returns ------- - tril : ndarray, shape (M, N) + tril : ndarray, shape (..., M, N) Lower triangle of `m`, of same shape and data-type as `m`. See Also @@ -465,6 +467,20 @@ def tril(m, k=0): [ 7, 8, 0], [10, 11, 12]]) + >>> np.tril(np.arange(3*4*5).reshape(3, 4, 5)) + array([[[ 0, 0, 0, 0, 0], + [ 5, 6, 0, 0, 0], + [10, 11, 12, 0, 0], + [15, 16, 17, 18, 0]], + [[20, 0, 0, 0, 0], + [25, 26, 0, 0, 0], + [30, 31, 32, 0, 0], + [35, 36, 37, 38, 0]], + [[40, 0, 0, 0, 0], + [45, 46, 0, 0, 0], + [50, 51, 52, 0, 0], + [55, 56, 57, 58, 0]]]) + """ m = asanyarray(m) mask = tri(*m.shape[-2:], k=k, dtype=bool) @@ -478,7 +494,8 @@ def triu(m, k=0): Upper triangle of an array. Return a copy of an array with the elements below the `k`-th diagonal - zeroed. + zeroed. For arrays with ``ndim`` exceeding 2, `triu` will apply to the final + two axes. Please refer to the documentation for `tril` for further details. @@ -494,6 +511,20 @@ def triu(m, k=0): [ 0, 8, 9], [ 0, 0, 12]]) + >>> np.triu(np.arange(3*4*5).reshape(3, 4, 5)) + array([[[ 0, 1, 2, 3, 4], + [ 0, 6, 7, 8, 9], + [ 0, 0, 12, 13, 14], + [ 0, 0, 0, 18, 19]], + [[20, 21, 22, 23, 24], + [ 0, 26, 27, 28, 29], + [ 0, 0, 32, 33, 34], + [ 0, 0, 0, 38, 39]], + [[40, 41, 42, 43, 44], + [ 0, 46, 47, 48, 49], + [ 0, 0, 52, 53, 54], + [ 0, 0, 0, 58, 59]]]) + """ m = asanyarray(m) mask = tri(*m.shape[-2:], k=k-1, dtype=bool) diff --git a/numpy/lib/twodim_base.pyi b/numpy/lib/twodim_base.pyi index 79b9511b8..007338d77 100644 --- a/numpy/lib/twodim_base.pyi +++ b/numpy/lib/twodim_base.pyi @@ -1,32 +1,255 @@ -from typing import List, Optional, Any +from typing import ( + Any, + Callable, + List, + Sequence, + overload, + Tuple, + Type, + TypeVar, + Union, +) -from numpy import ndarray, _OrderCF -from numpy.typing import ArrayLike, DTypeLike +from numpy import ( + ndarray, + dtype, + generic, + number, + bool_, + timedelta64, + datetime64, + int_, + intp, + float64, + signedinteger, + floating, + complexfloating, + object_, + _OrderCF, +) + +from numpy.typing import ( + DTypeLike, + _SupportsDType, + ArrayLike, + NDArray, + _NestedSequence, + _SupportsArray, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeObject_co, +) + +_T = TypeVar("_T") +_SCT = TypeVar("_SCT", bound=generic) + +# The returned arrays dtype must be compatible with `np.equal` +_MaskFunc = Callable[ + [NDArray[int_], _T], + NDArray[Union[number[Any], bool_, timedelta64, datetime64, object_]], +] + +_DTypeLike = Union[ + Type[_SCT], + dtype[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] __all__: List[str] -def fliplr(m): ... -def flipud(m): ... +@overload +def fliplr(m: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def fliplr(m: ArrayLike) -> NDArray[Any]: ... + +@overload +def flipud(m: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def flipud(m: ArrayLike) -> NDArray[Any]: ... +@overload def eye( N: int, - M: Optional[int] = ..., + M: None | int = ..., + k: int = ..., + dtype: None = ..., + order: _OrderCF = ..., + *, + like: None | ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def eye( + N: int, + M: None | int = ..., + k: int = ..., + dtype: _DTypeLike[_SCT] = ..., + order: _OrderCF = ..., + *, + like: None | ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def eye( + N: int, + M: None | int = ..., k: int = ..., dtype: DTypeLike = ..., order: _OrderCF = ..., *, - like: Optional[ArrayLike] = ... -) -> ndarray[Any, Any]: ... - -def diag(v, k=...): ... -def diagflat(v, k=...): ... -def tri(N, M=..., k=..., dtype = ..., *, like=...): ... -def tril(m, k=...): ... -def triu(m, k=...): ... -def vander(x, N=..., increasing=...): ... -def histogram2d(x, y, bins=..., range=..., normed=..., weights=..., density=...): ... -def mask_indices(n, mask_func, k=...): ... -def tril_indices(n, k=..., m=...): ... -def tril_indices_from(arr, k=...): ... -def triu_indices(n, k=..., m=...): ... -def triu_indices_from(arr, k=...): ... + like: None | ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def diag(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def diag(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def diagflat(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def diagflat(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: None = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[float64]: ... +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: _DTypeLike[_SCT] = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[_SCT]: ... +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: DTypeLike = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[Any]: ... + +@overload +def tril(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def tril(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def triu(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def triu(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def vander( # type: ignore[misc] + x: _ArrayLikeInt_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def vander( # type: ignore[misc] + x: _ArrayLikeFloat_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[floating[Any]]: ... +@overload +def vander( + x: _ArrayLikeComplex_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def vander( + x: _ArrayLikeObject_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[object_]: ... + +@overload +def histogram2d( # type: ignore[misc] + x: _ArrayLikeFloat_co, + y: _ArrayLikeFloat_co, + bins: int | Sequence[int] = ..., + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[floating[Any]], + NDArray[floating[Any]], +]: ... +@overload +def histogram2d( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + bins: int | Sequence[int] = ..., + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[complexfloating[Any, Any]], + NDArray[complexfloating[Any, Any]], +]: ... +@overload # TODO: Sort out `bins` +def histogram2d( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + bins: Sequence[_ArrayLikeInt_co], + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[Any], + NDArray[Any], +]: ... + +# NOTE: we're assuming/demanding here the `mask_func` returns +# an ndarray of shape `(n, n)`; otherwise there is the possibility +# of the output tuple having more or less than 2 elements +@overload +def mask_indices( + n: int, + mask_func: _MaskFunc[int], + k: int = ..., +) -> Tuple[NDArray[intp], NDArray[intp]]: ... +@overload +def mask_indices( + n: int, + mask_func: _MaskFunc[_T], + k: _T, +) -> Tuple[NDArray[intp], NDArray[intp]]: ... + +def tril_indices( + n: int, + k: int = ..., + m: None | int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def tril_indices_from( + arr: NDArray[Any], + k: int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def triu_indices( + n: int, + k: int = ..., + m: None | int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def triu_indices_from( + arr: NDArray[Any], + k: int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... diff --git a/numpy/lib/type_check.pyi b/numpy/lib/type_check.pyi index 7da02bb9f..fbe325858 100644 --- a/numpy/lib/type_check.pyi +++ b/numpy/lib/type_check.pyi @@ -1,19 +1,235 @@ -from typing import List +import sys +from typing import ( + Any, + Container, + Iterable, + List, + overload, + Type, + TypeVar, +) + +from numpy import ( + dtype, + generic, + bool_, + floating, + float64, + complexfloating, + integer, +) + +from numpy.typing import ( + ArrayLike, + DTypeLike, + NBitBase, + NDArray, + _64Bit, + _SupportsDType, + _ScalarLike_co, + _NestedSequence, + _SupportsArray, + _DTypeLikeComplex, +) + +if sys.version_info >= (3, 8): + from typing import Protocol, Literal as L +else: + from typing_extensions import Protocol, Literal as L + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_SCT = TypeVar("_SCT", bound=generic) +_NBit1 = TypeVar("_NBit1", bound=NBitBase) +_NBit2 = TypeVar("_NBit2", bound=NBitBase) + +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] + +class _SupportsReal(Protocol[_T_co]): + @property + def real(self) -> _T_co: ... + +class _SupportsImag(Protocol[_T_co]): + @property + def imag(self) -> _T_co: ... __all__: List[str] -def mintypecode(typechars, typeset=..., default=...): ... -def asfarray(a, dtype = ...): ... -def real(val): ... -def imag(val): ... -def iscomplex(x): ... -def isreal(x): ... -def iscomplexobj(x): ... -def isrealobj(x): ... -def nan_to_num(x, copy=..., nan=..., posinf=..., neginf=...): ... -def real_if_close(a, tol=...): ... -def typename(char): ... -def common_type(*arrays): ... - -# NOTE: Deprecated +def mintypecode( + typechars: Iterable[str | ArrayLike], + typeset: Container[str] = ..., + default: str = ..., +) -> str: ... + +# `asfarray` ignores dtypes if they're not inexact + +@overload +def asfarray( + a: object, + dtype: None | Type[float] = ..., +) -> NDArray[float64]: ... +@overload +def asfarray( # type: ignore[misc] + a: Any, + dtype: _DTypeLikeComplex, +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def asfarray( + a: Any, + dtype: DTypeLike, +) -> NDArray[floating[Any]]: ... + +@overload +def real(val: _SupportsReal[_T]) -> _T: ... +@overload +def real(val: ArrayLike) -> NDArray[Any]: ... + +@overload +def imag(val: _SupportsImag[_T]) -> _T: ... +@overload +def imag(val: ArrayLike) -> NDArray[Any]: ... + +@overload +def iscomplex(x: _ScalarLike_co) -> bool_: ... # type: ignore[misc] +@overload +def iscomplex(x: ArrayLike) -> NDArray[bool_]: ... + +@overload +def isreal(x: _ScalarLike_co) -> bool_: ... # type: ignore[misc] +@overload +def isreal(x: ArrayLike) -> NDArray[bool_]: ... + +def iscomplexobj(x: _SupportsDType[dtype[Any]] | ArrayLike) -> bool: ... + +def isrealobj(x: _SupportsDType[dtype[Any]] | ArrayLike) -> bool: ... + +@overload +def nan_to_num( # type: ignore[misc] + x: _SCT, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> _SCT: ... +@overload +def nan_to_num( + x: _ScalarLike_co, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> Any: ... +@overload +def nan_to_num( + x: _ArrayLike[_SCT], + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> NDArray[_SCT]: ... +@overload +def nan_to_num( + x: ArrayLike, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> NDArray[Any]: ... + +# If one passes a complex array to `real_if_close`, then one is reasonably +# expected to verify the output dtype (so we can return an unsafe union here) + +@overload +def real_if_close( # type: ignore[misc] + a: _ArrayLike[complexfloating[_NBit1, _NBit1]], + tol: float = ..., +) -> NDArray[floating[_NBit1]] | NDArray[complexfloating[_NBit1, _NBit1]]: ... +@overload +def real_if_close( + a: _ArrayLike[_SCT], + tol: float = ..., +) -> NDArray[_SCT]: ... +@overload +def real_if_close( + a: ArrayLike, + tol: float = ..., +) -> NDArray[Any]: ... + +# NOTE: deprecated # def asscalar(a): ... + +@overload +def typename(char: L['S1']) -> L['character']: ... +@overload +def typename(char: L['?']) -> L['bool']: ... +@overload +def typename(char: L['b']) -> L['signed char']: ... +@overload +def typename(char: L['B']) -> L['unsigned char']: ... +@overload +def typename(char: L['h']) -> L['short']: ... +@overload +def typename(char: L['H']) -> L['unsigned short']: ... +@overload +def typename(char: L['i']) -> L['integer']: ... +@overload +def typename(char: L['I']) -> L['unsigned integer']: ... +@overload +def typename(char: L['l']) -> L['long integer']: ... +@overload +def typename(char: L['L']) -> L['unsigned long integer']: ... +@overload +def typename(char: L['q']) -> L['long long integer']: ... +@overload +def typename(char: L['Q']) -> L['unsigned long long integer']: ... +@overload +def typename(char: L['f']) -> L['single precision']: ... +@overload +def typename(char: L['d']) -> L['double precision']: ... +@overload +def typename(char: L['g']) -> L['long precision']: ... +@overload +def typename(char: L['F']) -> L['complex single precision']: ... +@overload +def typename(char: L['D']) -> L['complex double precision']: ... +@overload +def typename(char: L['G']) -> L['complex long double precision']: ... +@overload +def typename(char: L['S']) -> L['string']: ... +@overload +def typename(char: L['U']) -> L['unicode']: ... +@overload +def typename(char: L['V']) -> L['void']: ... +@overload +def typename(char: L['O']) -> L['object']: ... + +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + integer[Any] + ]] +) -> Type[floating[_64Bit]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + floating[_NBit1] + ]] +) -> Type[floating[_NBit1]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + integer[Any] | floating[_NBit1] + ]] +) -> Type[floating[_NBit1 | _64Bit]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + floating[_NBit1] | complexfloating[_NBit2, _NBit2] + ]] +) -> Type[complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]]: ... +@overload +def common_type( + *arrays: _SupportsDType[dtype[ + integer[Any] | floating[_NBit1] | complexfloating[_NBit2, _NBit2] + ]] +) -> Type[complexfloating[_64Bit | _NBit1 | _NBit2, _64Bit | _NBit1 | _NBit2]]: ... diff --git a/numpy/linalg/__init__.pyi b/numpy/linalg/__init__.pyi index 5080019f4..00db05079 100644 --- a/numpy/linalg/__init__.pyi +++ b/numpy/linalg/__init__.pyi @@ -17,7 +17,7 @@ def eig(a): ... def eigh(a, UPLO=...): ... def svd(a, full_matrices=..., compute_uv=..., hermitian=...): ... def cond(x, p=...): ... -def matrix_rank(M, tol=..., hermitian=...): ... +def matrix_rank(A, tol=..., hermitian=...): ... def pinv(a, rcond=..., hermitian=...): ... def slogdet(a): ... def det(a): ... diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 46fb2502e..e150952e3 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -1797,12 +1797,12 @@ def cond(x, p=None): return r -def _matrix_rank_dispatcher(M, tol=None, hermitian=None): - return (M,) +def _matrix_rank_dispatcher(A, tol=None, hermitian=None): + return (A,) @array_function_dispatch(_matrix_rank_dispatcher) -def matrix_rank(M, tol=None, hermitian=False): +def matrix_rank(A, tol=None, hermitian=False): """ Return matrix rank of array using SVD method @@ -1814,18 +1814,18 @@ def matrix_rank(M, tol=None, hermitian=False): Parameters ---------- - M : {(M,), (..., M, N)} array_like + A : {(M,), (..., M, N)} array_like Input vector or stack of matrices. tol : (...) array_like, float, optional Threshold below which SVD values are considered zero. If `tol` is None, and ``S`` is an array with singular values for `M`, and ``eps`` is the epsilon value for datatype of ``S``, then `tol` is - set to ``S.max() * max(M.shape) * eps``. + set to ``S.max() * max(M, N) * eps``. .. versionchanged:: 1.14 Broadcasted against the stack of matrices hermitian : bool, optional - If True, `M` is assumed to be Hermitian (symmetric if real-valued), + If True, `A` is assumed to be Hermitian (symmetric if real-valued), enabling a more efficient method for finding singular values. Defaults to False. @@ -1834,39 +1834,39 @@ def matrix_rank(M, tol=None, hermitian=False): Returns ------- rank : (...) array_like - Rank of M. + Rank of A. Notes ----- The default threshold to detect rank deficiency is a test on the magnitude - of the singular values of `M`. By default, we identify singular values less - than ``S.max() * max(M.shape) * eps`` as indicating rank deficiency (with + of the singular values of `A`. By default, we identify singular values less + than ``S.max() * max(M, N) * eps`` as indicating rank deficiency (with the symbols defined above). This is the algorithm MATLAB uses [1]. It also appears in *Numerical recipes* in the discussion of SVD solutions for linear least squares [2]. This default threshold is designed to detect rank deficiency accounting for the numerical errors of the SVD computation. Imagine that there is a column - in `M` that is an exact (in floating point) linear combination of other - columns in `M`. Computing the SVD on `M` will not produce a singular value + in `A` that is an exact (in floating point) linear combination of other + columns in `A`. Computing the SVD on `A` will not produce a singular value exactly equal to 0 in general: any difference of the smallest SVD value from 0 will be caused by numerical imprecision in the calculation of the SVD. Our threshold for small SVD values takes this numerical imprecision into account, and the default threshold will detect such numerical rank - deficiency. The threshold may declare a matrix `M` rank deficient even if - the linear combination of some columns of `M` is not exactly equal to - another column of `M` but only numerically very close to another column of - `M`. + deficiency. The threshold may declare a matrix `A` rank deficient even if + the linear combination of some columns of `A` is not exactly equal to + another column of `A` but only numerically very close to another column of + `A`. We chose our default threshold because it is in wide use. Other thresholds are possible. For example, elsewhere in the 2007 edition of *Numerical recipes* there is an alternative threshold of ``S.max() * - np.finfo(M.dtype).eps / 2. * np.sqrt(m + n + 1.)``. The authors describe + np.finfo(A.dtype).eps / 2. * np.sqrt(m + n + 1.)``. The authors describe this threshold as being based on "expected roundoff error" (p 71). The thresholds above deal with floating point roundoff error in the calculation of the SVD. However, you may have more information about the - sources of error in `M` that would make you consider other tolerance values + sources of error in `A` that would make you consider other tolerance values to detect *effective* rank deficiency. The most useful measure of the tolerance depends on the operations you intend to use on your matrix. For example, if your data come from uncertain measurements with uncertainties @@ -1895,12 +1895,12 @@ def matrix_rank(M, tol=None, hermitian=False): >>> matrix_rank(np.zeros((4,))) 0 """ - M = asarray(M) - if M.ndim < 2: - return int(not all(M==0)) - S = svd(M, compute_uv=False, hermitian=hermitian) + A = asarray(A) + if A.ndim < 2: + return int(not all(A==0)) + S = svd(A, compute_uv=False, hermitian=hermitian) if tol is None: - tol = S.max(axis=-1, keepdims=True) * max(M.shape[-2:]) * finfo(S.dtype).eps + tol = S.max(axis=-1, keepdims=True) * max(A.shape[-2:]) * finfo(S.dtype).eps else: tol = asarray(tol)[..., newaxis] return count_nonzero(S > tol, axis=-1) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 4c204cac2..82e5e7155 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -8094,22 +8094,51 @@ class _convert2ma: """ __doc__ = None - def __init__(self, funcname, params=None): + def __init__(self, funcname, np_ret, np_ma_ret, params=None): self._func = getattr(np, funcname) - self.__doc__ = self.getdoc() + self.__doc__ = self.getdoc(np_ret, np_ma_ret) self._extras = params or {} - def getdoc(self): + def getdoc(self, np_ret, np_ma_ret): "Return the doc of the function (from the doc of the method)." doc = getattr(self._func, '__doc__', None) sig = get_object_signature(self._func) if doc: + doc = self._replace_return_type(doc, np_ret, np_ma_ret) # Add the signature of the function at the beginning of the doc if sig: sig = "%s%s\n" % (self._func.__name__, sig) doc = sig + doc return doc + def _replace_return_type(self, doc, np_ret, np_ma_ret): + """ + Replace documentation of ``np`` function's return type. + + Replaces it with the proper type for the ``np.ma`` function. + + Parameters + ---------- + doc : str + The documentation of the ``np`` method. + np_ret : str + The return type string of the ``np`` method that we want to + replace. (e.g. "out : ndarray") + np_ma_ret : str + The return type string of the ``np.ma`` method. + (e.g. "out : MaskedArray") + """ + if np_ret not in doc: + raise RuntimeError( + f"Failed to replace `{np_ret}` with `{np_ma_ret}`. " + f"The documentation string for return type, {np_ret}, is not " + f"found in the docstring for `np.{self._func.__name__}`. " + f"Fix the docstring for `np.{self._func.__name__}` or " + "update the expected string for return type." + ) + + return doc.replace(np_ret, np_ma_ret) + def __call__(self, *args, **params): # Find the common parameters to the call and the definition _extras = self._extras @@ -8125,20 +8154,57 @@ class _convert2ma: result._hardmask = bool(_extras.get("hard_mask", False)) return result -arange = _convert2ma('arange', params=dict(fill_value=None, hardmask=False)) + +arange = _convert2ma( + 'arange', + params=dict(fill_value=None, hardmask=False), + np_ret='arange : ndarray', + np_ma_ret='arange : MaskedArray', +) clip = np.clip diff = np.diff -empty = _convert2ma('empty', params=dict(fill_value=None, hardmask=False)) -empty_like = _convert2ma('empty_like') -frombuffer = _convert2ma('frombuffer') -fromfunction = _convert2ma('fromfunction') +empty = _convert2ma( + 'empty', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +empty_like = _convert2ma( + 'empty_like', + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +frombuffer = _convert2ma( + 'frombuffer', + np_ret='out : ndarray', + np_ma_ret='out: MaskedArray', +) +fromfunction = _convert2ma( + 'fromfunction', + np_ret='fromfunction : any', + np_ma_ret='fromfunction: MaskedArray', +) identity = _convert2ma( - 'identity', params=dict(fill_value=None, hardmask=False)) + 'identity', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) indices = np.indices -ones = _convert2ma('ones', params=dict(fill_value=None, hardmask=False)) +ones = _convert2ma( + 'ones', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) ones_like = np.ones_like squeeze = np.squeeze -zeros = _convert2ma('zeros', params=dict(fill_value=None, hardmask=False)) +zeros = _convert2ma( + 'zeros', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) zeros_like = np.zeros_like diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index b04b8e66b..5525b232b 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -936,11 +936,11 @@ class ABCPolyBase(abc.ABC): diagnostic information from the singular value decomposition is also returned. w : array_like, shape (M,), optional - Weights. If not None the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products - ``w[i]*y[i]`` all have the same variance. The default value is - None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have + the same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 window : {[beg, end]}, optional diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index d24fc738f..210000ec4 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -1582,10 +1582,11 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index eef5c25b2..c1b9f71c0 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -1310,10 +1310,11 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index 05d1337b0..b7095c910 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -1301,10 +1301,11 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index 69d557510..d3b6432dc 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -1307,10 +1307,11 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index cd4da2a79..d4cf4accf 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -1321,10 +1321,11 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index 940eed5e3..d8a032068 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -1252,10 +1252,11 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): diagnostic information from the singular value decomposition (used to solve the fit's matrix equation) is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by ``w[i]``. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 diff --git a/numpy/testing/__init__.py b/numpy/testing/__init__.py index e1f87621f..064a74a16 100644 --- a/numpy/testing/__init__.py +++ b/numpy/testing/__init__.py @@ -8,6 +8,7 @@ away. from unittest import TestCase from ._private.utils import * +from ._private.utils import _assert_valid_refcount, _gen_alignment_data from ._private import decorators as dec from ._private.nosetester import ( run_module_suite, NoseTester as Tester diff --git a/numpy/testing/__init__.pyi b/numpy/testing/__init__.pyi index 395626f6b..955dae862 100644 --- a/numpy/testing/__init__.pyi +++ b/numpy/testing/__init__.pyi @@ -1,113 +1,53 @@ -import sys -import warnings -from typing import Any, List, ClassVar, Tuple, Set - -if sys.version_info >= (3, 8): - from typing import Final -else: - from typing_extensions import Final +from typing import List from unittest import ( TestCase as TestCase, ) -from unittest.case import ( +from numpy.testing._private.utils import ( + assert_equal as assert_equal, + assert_almost_equal as assert_almost_equal, + assert_approx_equal as assert_approx_equal, + assert_array_equal as assert_array_equal, + assert_array_less as assert_array_less, + assert_string_equal as assert_string_equal, + assert_array_almost_equal as assert_array_almost_equal, + assert_raises as assert_raises, + build_err_msg as build_err_msg, + decorate_methods as decorate_methods, + jiffies as jiffies, + memusage as memusage, + print_assert_equal as print_assert_equal, + raises as raises, + rundocs as rundocs, + runstring as runstring, + verbose as verbose, + measure as measure, + assert_ as assert_, + assert_array_almost_equal_nulp as assert_array_almost_equal_nulp, + assert_raises_regex as assert_raises_regex, + assert_array_max_ulp as assert_array_max_ulp, + assert_warns as assert_warns, + assert_no_warnings as assert_no_warnings, + assert_allclose as assert_allclose, + IgnoreException as IgnoreException, + clear_and_catch_warnings as clear_and_catch_warnings, SkipTest as SkipTest, + KnownFailureException as KnownFailureException, + temppath as temppath, + tempdir as tempdir, + IS_PYPY as IS_PYPY, + HAS_REFCOUNT as HAS_REFCOUNT, + suppress_warnings as suppress_warnings, + assert_array_compare as assert_array_compare, + assert_no_gc_cycles as assert_no_gc_cycles, + break_cycles as break_cycles, + HAS_LAPACK64 as HAS_LAPACK64, ) __all__: List[str] -def run_module_suite(file_to_run=..., argv=...): ... - -class KnownFailureException(Exception): ... -class IgnoreException(Exception): ... - -class clear_and_catch_warnings(warnings.catch_warnings): - class_modules: ClassVar[Tuple[str, ...]] - modules: Set[str] - def __init__(self, record=..., modules=...): ... - def __enter__(self): ... - def __exit__(self, *exc_info): ... - -class suppress_warnings: - log: List[warnings.WarningMessage] - def __init__(self, forwarding_rule=...): ... - def filter(self, category=..., message=..., module=...): ... - def record(self, category=..., message=..., module=...): ... - def __enter__(self): ... - def __exit__(self, *exc_info): ... - def __call__(self, func): ... - -verbose: int -IS_PYPY: Final[bool] -HAS_REFCOUNT: Final[bool] -HAS_LAPACK64: Final[bool] - -def assert_(val, msg=...): ... -def memusage(processName=..., instance=...): ... -def jiffies(_proc_pid_stat=..., _load_time=...): ... -def build_err_msg( - arrays, - err_msg, - header=..., - verbose=..., - names=..., - precision=..., -): ... -def assert_equal(actual, desired, err_msg=..., verbose=...): ... -def print_assert_equal(test_string, actual, desired): ... -def assert_almost_equal( - actual, - desired, - decimal=..., - err_msg=..., - verbose=..., -): ... -def assert_approx_equal( - actual, - desired, - significant=..., - err_msg=..., - verbose=..., -): ... -def assert_array_compare( - comparison, - x, - y, - err_msg=..., - verbose=..., - header=..., - precision=..., - equal_nan=..., - equal_inf=..., -): ... -def assert_array_equal(x, y, err_msg=..., verbose=...): ... -def assert_array_almost_equal(x, y, decimal=..., err_msg=..., verbose=...): ... -def assert_array_less(x, y, err_msg=..., verbose=...): ... -def runstring(astr, dict): ... -def assert_string_equal(actual, desired): ... -def rundocs(filename=..., raise_on_error=...): ... -def raises(*args): ... -def assert_raises(*args, **kwargs): ... -def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs): ... -def decorate_methods(cls, decorator, testmatch=...): ... -def measure(code_str, times=..., label=...): ... -def assert_allclose( - actual, - desired, - rtol=..., - atol=..., - equal_nan=..., - err_msg=..., - verbose=..., -): ... -def assert_array_almost_equal_nulp(x, y, nulp=...): ... -def assert_array_max_ulp(a, b, maxulp=..., dtype=...): ... -def assert_warns(warning_class, *args, **kwargs): ... -def assert_no_warnings(*args, **kwargs): ... -def tempdir(*args, **kwargs): ... -def temppath(*args, **kwargs): ... -def assert_no_gc_cycles(*args, **kwargs): ... -def break_cycles(): ... -def _assert_valid_refcount(op): ... -def _gen_alignment_data(dtype=..., type=..., max_size=...): ... +def run_module_suite( + file_to_run: None | str = ..., + argv: None | List[str] = ..., +) -> None: ... diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py index 393fedc27..487aa0b4c 100644 --- a/numpy/testing/_private/utils.py +++ b/numpy/testing/_private/utils.py @@ -35,8 +35,7 @@ __all__ = [ 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings', 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', 'HAS_REFCOUNT', 'suppress_warnings', 'assert_array_compare', - '_assert_valid_refcount', '_gen_alignment_data', 'assert_no_gc_cycles', - 'break_cycles', 'HAS_LAPACK64' + 'assert_no_gc_cycles', 'break_cycles', 'HAS_LAPACK64' ] @@ -2518,4 +2517,3 @@ def _no_tracing(func): finally: sys.settrace(original_trace) return wrapper - diff --git a/numpy/testing/_private/utils.pyi b/numpy/testing/_private/utils.pyi new file mode 100644 index 000000000..29915309f --- /dev/null +++ b/numpy/testing/_private/utils.pyi @@ -0,0 +1,396 @@ +import os +import sys +import ast +import types +import warnings +import unittest +import contextlib +from typing import ( + Any, + AnyStr, + Callable, + ClassVar, + Dict, + Iterable, + List, + NoReturn, + overload, + Pattern, + Sequence, + Set, + Tuple, + Type, + type_check_only, + TypeVar, + Union, +) + +from numpy import generic, dtype, number, object_, bool_, _FloatValue +from numpy.typing import ( + NDArray, + ArrayLike, + DTypeLike, + _ArrayLikeNumber_co, + _ArrayLikeObject_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, +) + +from unittest.case import ( + SkipTest as SkipTest, +) + +if sys.version_info >= (3, 8): + from typing import Final, SupportsIndex, Literal as L +else: + from typing_extensions import Final, SupportsIndex, Literal as L + +_T = TypeVar("_T") +_ET = TypeVar("_ET", bound=BaseException) +_FT = TypeVar("_FT", bound=Callable[..., Any]) + +# Must return a bool or an ndarray/generic type +# that is supported by `np.logical_and.reduce` +_ComparisonFunc = Callable[ + [NDArray[Any], NDArray[Any]], + Union[ + bool, + bool_, + number[Any], + NDArray[Union[bool_, number[Any], object_]], + ], +] + +__all__: List[str] + +class KnownFailureException(Exception): ... +class IgnoreException(Exception): ... + +class clear_and_catch_warnings(warnings.catch_warnings): + class_modules: ClassVar[Tuple[types.ModuleType, ...]] + modules: Set[types.ModuleType] + @overload + def __new__( + cls, + record: L[False] = ..., + modules: Iterable[types.ModuleType] = ..., + ) -> _clear_and_catch_warnings_without_records: ... + @overload + def __new__( + cls, + record: L[True], + modules: Iterable[types.ModuleType] = ..., + ) -> _clear_and_catch_warnings_with_records: ... + @overload + def __new__( + cls, + record: bool, + modules: Iterable[types.ModuleType] = ..., + ) -> clear_and_catch_warnings: ... + def __enter__(self) -> None | List[warnings.WarningMessage]: ... + def __exit__( + self, + __exc_type: None | Type[BaseException] = ..., + __exc_val: None | BaseException = ..., + __exc_tb: None | types.TracebackType = ..., + ) -> None: ... + +# Type-check only `clear_and_catch_warnings` subclasses for both values of the +# `record` parameter. Copied from the stdlib `warnings` stubs. + +@type_check_only +class _clear_and_catch_warnings_with_records(clear_and_catch_warnings): + def __enter__(self) -> List[warnings.WarningMessage]: ... + +@type_check_only +class _clear_and_catch_warnings_without_records(clear_and_catch_warnings): + def __enter__(self) -> None: ... + +class suppress_warnings: + log: List[warnings.WarningMessage] + def __init__( + self, + forwarding_rule: L["always", "module", "once", "location"] = ..., + ) -> None: ... + def filter( + self, + category: Type[Warning] = ..., + message: str = ..., + module: None | types.ModuleType = ..., + ) -> None: ... + def record( + self, + category: Type[Warning] = ..., + message: str = ..., + module: None | types.ModuleType = ..., + ) -> List[warnings.WarningMessage]: ... + def __enter__(self: _T) -> _T: ... + def __exit__( + self, + __exc_type: None | Type[BaseException] = ..., + __exc_val: None | BaseException = ..., + __exc_tb: None | types.TracebackType = ..., + ) -> None: ... + def __call__(self, func: _FT) -> _FT: ... + +verbose: int +IS_PYPY: Final[bool] +HAS_REFCOUNT: Final[bool] +HAS_LAPACK64: Final[bool] + +def assert_(val: object, msg: str | Callable[[], str] = ...) -> None: ... + +# Contrary to runtime we can't do `os.name` checks while type checking, +# only `sys.platform` checks +if sys.platform == "win32" or sys.platform == "cygwin": + def memusage(processName: str = ..., instance: int = ...) -> int: ... +elif sys.platform == "linux": + def memusage(_proc_pid_stat: str | bytes | os.PathLike[Any] = ...) -> None | int: ... +else: + def memusage() -> NoReturn: ... + +if sys.platform == "linux": + def jiffies( + _proc_pid_stat: str | bytes | os.PathLike[Any] = ..., + _load_time: List[float] = ..., + ) -> int: ... +else: + def jiffies(_load_time: List[float] = ...) -> int: ... + +def build_err_msg( + arrays: Iterable[object], + err_msg: str, + header: str = ..., + verbose: bool = ..., + names: Sequence[str] = ..., + precision: None | SupportsIndex = ..., +) -> str: ... + +def assert_equal( + actual: object, + desired: object, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def print_assert_equal( + test_string: str, + actual: object, + desired: object, +) -> None: ... + +def assert_almost_equal( + actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, + desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, + decimal: int = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +# Anything that can be coerced into `builtins.float` +def assert_approx_equal( + actual: _FloatValue, + desired: _FloatValue, + significant: int = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_compare( + comparison: _ComparisonFunc, + x: ArrayLike, + y: ArrayLike, + err_msg: str = ..., + verbose: bool = ..., + header: str = ..., + precision: SupportsIndex = ..., + equal_nan: bool = ..., + equal_inf: bool = ..., +) -> None: ... + +def assert_array_equal( + x: ArrayLike, + y: ArrayLike, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_almost_equal( + x: _ArrayLikeNumber_co | _ArrayLikeObject_co, + y: _ArrayLikeNumber_co | _ArrayLikeObject_co, + decimal: float = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +@overload +def assert_array_less( + x: _ArrayLikeNumber_co | _ArrayLikeObject_co, + y: _ArrayLikeNumber_co | _ArrayLikeObject_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_array_less( + x: _ArrayLikeTD64_co, + y: _ArrayLikeTD64_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_array_less( + x: _ArrayLikeDT64_co, + y: _ArrayLikeDT64_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def runstring( + astr: str | bytes | types.CodeType, + dict: None | Dict[str, Any], +) -> Any: ... + +def assert_string_equal(actual: str, desired: str) -> None: ... + +def rundocs( + filename: None | str | os.PathLike[str] = ..., + raise_on_error: bool = ..., +) -> None: ... + +def raises(*args: Type[BaseException]) -> Callable[[_FT], _FT]: ... + +@overload +def assert_raises( # type: ignore + __expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + __callable: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> None: ... +@overload +def assert_raises( + expected_exception: Type[_ET] | Tuple[Type[_ET], ...], + *, + msg: None | str = ..., +) -> unittest.case._AssertRaisesContext[_ET]: ... + +@overload +def assert_raises_regex( + __expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + __expected_regex: str | bytes | Pattern[Any], + __callable: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> None: ... +@overload +def assert_raises_regex( + expected_exception: Type[_ET] | Tuple[Type[_ET], ...], + expected_regex: str | bytes | Pattern[Any], + *, + msg: None | str = ..., +) -> unittest.case._AssertRaisesContext[_ET]: ... + +def decorate_methods( + cls: Type[Any], + decorator: Callable[[Callable[..., Any]], Any], + testmatch: None | str | bytes | Pattern[Any] = ..., +) -> None: ... + +def measure( + code_str: str | bytes | ast.mod | ast.AST, + times: int = ..., + label: None | str = ..., +) -> float: ... + +@overload +def assert_allclose( + actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, + desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_allclose( + actual: _ArrayLikeTD64_co, + desired: _ArrayLikeTD64_co, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_almost_equal_nulp( + x: _ArrayLikeNumber_co, + y: _ArrayLikeNumber_co, + nulp: float = ..., +) -> None: ... + +def assert_array_max_ulp( + a: _ArrayLikeNumber_co, + b: _ArrayLikeNumber_co, + maxulp: float = ..., + dtype: DTypeLike = ..., +) -> NDArray[Any]: ... + +@overload +def assert_warns( + warning_class: Type[Warning], +) -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_warns( + __warning_class: Type[Warning], + __func: Callable[..., _T], + *args: Any, + **kwargs: Any, +) -> _T: ... + +@overload +def assert_no_warnings() -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_no_warnings( + __func: Callable[..., _T], + *args: Any, + **kwargs: Any, +) -> _T: ... + +@overload +def tempdir( + suffix: None = ..., + prefix: None = ..., + dir: None = ..., +) -> contextlib._GeneratorContextManager[str]: ... +@overload +def tempdir( + suffix: None | AnyStr = ..., + prefix: None | AnyStr = ..., + dir: None | AnyStr | os.PathLike[AnyStr] = ..., +) -> contextlib._GeneratorContextManager[AnyStr]: ... + +@overload +def temppath( + suffix: None = ..., + prefix: None = ..., + dir: None = ..., + text: bool = ..., +) -> contextlib._GeneratorContextManager[str]: ... +@overload +def temppath( + suffix: None | AnyStr = ..., + prefix: None | AnyStr = ..., + dir: None | AnyStr | os.PathLike[AnyStr] = ..., + text: bool = ..., +) -> contextlib._GeneratorContextManager[AnyStr]: ... + +@overload +def assert_no_gc_cycles() -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_no_gc_cycles( + __func: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> None: ... + +def break_cycles() -> None: ... diff --git a/numpy/testing/setup.py b/numpy/testing/setup.py index 7652a94a2..6f203e872 100755 --- a/numpy/testing/setup.py +++ b/numpy/testing/setup.py @@ -7,6 +7,7 @@ def configuration(parent_package='',top_path=None): config.add_subpackage('_private') config.add_subpackage('tests') config.add_data_files('*.pyi') + config.add_data_files('_private/*.pyi') return config if __name__ == '__main__': diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index 753258c13..20a883304 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -12,6 +12,7 @@ warnings.warn("Importing from numpy.testing.utils is deprecated " DeprecationWarning, stacklevel=2) from ._private.utils import * +from ._private.utils import _assert_valid_refcount, _gen_alignment_data __all__ = [ 'assert_equal', 'assert_almost_equal', 'assert_approx_equal', @@ -24,5 +25,5 @@ __all__ = [ 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings', 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', 'HAS_REFCOUNT', 'suppress_warnings', 'assert_array_compare', - '_assert_valid_refcount', '_gen_alignment_data', 'assert_no_gc_cycles' + 'assert_no_gc_cycles' ] diff --git a/numpy/tests/test_numpy_version.py b/numpy/tests/test_numpy_version.py index 28595026e..bccbcb8e9 100644 --- a/numpy/tests/test_numpy_version.py +++ b/numpy/tests/test_numpy_version.py @@ -32,3 +32,13 @@ def test_valid_numpy_version(): res = re.match(version_pattern + dev_suffix + '$', np.__version__) assert_(res is not None, np.__version__) + + +def test_short_version(): + # Check numpy.short_version actually exists + if np.version.release: + assert_(np.__version__ == np.version.short_version, + "short_version mismatch in release version") + else: + assert_(np.__version__.split("+")[0] == np.version.short_version, + "short_version mismatch in development version") diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py index 252123a19..19424169a 100644 --- a/numpy/typing/__init__.py +++ b/numpy/typing/__init__.py @@ -164,7 +164,7 @@ API # NOTE: The API section will be appended with additional entries # further down in this file -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING, List, Any if TYPE_CHECKING: # typing_extensions is always available when type-checking @@ -376,14 +376,14 @@ if TYPE_CHECKING: _GUFunc_Nin2_Nout1, ) else: - _UFunc_Nin1_Nout1 = NotImplemented - _UFunc_Nin2_Nout1 = NotImplemented - _UFunc_Nin1_Nout2 = NotImplemented - _UFunc_Nin2_Nout2 = NotImplemented - _GUFunc_Nin2_Nout1 = NotImplemented + _UFunc_Nin1_Nout1 = Any + _UFunc_Nin2_Nout1 = Any + _UFunc_Nin1_Nout2 = Any + _UFunc_Nin2_Nout2 = Any + _GUFunc_Nin2_Nout1 = Any # Clean up the namespace -del TYPE_CHECKING, final, List +del TYPE_CHECKING, final, List, Any if __doc__ is not None: from ._add_docstring import _docstrings diff --git a/numpy/typing/_array_like.py b/numpy/typing/_array_like.py index 2b823ecc0..3bdbed8f8 100644 --- a/numpy/typing/_array_like.py +++ b/numpy/typing/_array_like.py @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from typing import Any, Sequence, TYPE_CHECKING, Union, TypeVar +from typing import Any, Sequence, TYPE_CHECKING, Union, TypeVar, Generic from numpy import ( ndarray, @@ -34,7 +34,7 @@ _ScalarType = TypeVar("_ScalarType", bound=generic) _DType = TypeVar("_DType", bound="dtype[Any]") _DType_co = TypeVar("_DType_co", covariant=True, bound="dtype[Any]") -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: +if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): # The `_SupportsArray` protocol only cares about the default dtype # (i.e. `dtype=None` or no `dtype` parameter at all) of the to-be returned # array. @@ -43,7 +43,7 @@ if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: class _SupportsArray(Protocol[_DType_co]): def __array__(self) -> ndarray[Any, _DType_co]: ... else: - _SupportsArray = Any + class _SupportsArray(Generic[_DType_co]): ... # TODO: Wait for support for recursive types _NestedSequence = Union[ diff --git a/numpy/typing/_callable.py b/numpy/typing/_callable.py index 54f9b1425..8f911da3b 100644 --- a/numpy/typing/_callable.py +++ b/numpy/typing/_callable.py @@ -53,7 +53,7 @@ if sys.version_info >= (3, 8): elif _HAS_TYPING_EXTENSIONS: from typing_extensions import Protocol -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: +if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") _2Tuple = Tuple[_T1, _T1] @@ -332,25 +332,25 @@ if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: def __call__(self, __other: _T2) -> NDArray[bool_]: ... else: - _BoolOp = NotImplemented - _BoolBitOp = NotImplemented - _BoolSub = NotImplemented - _BoolTrueDiv = NotImplemented - _BoolMod = NotImplemented - _BoolDivMod = NotImplemented - _TD64Div = NotImplemented - _IntTrueDiv = NotImplemented - _UnsignedIntOp = NotImplemented - _UnsignedIntBitOp = NotImplemented - _UnsignedIntMod = NotImplemented - _UnsignedIntDivMod = NotImplemented - _SignedIntOp = NotImplemented - _SignedIntBitOp = NotImplemented - _SignedIntMod = NotImplemented - _SignedIntDivMod = NotImplemented - _FloatOp = NotImplemented - _FloatMod = NotImplemented - _FloatDivMod = NotImplemented - _ComplexOp = NotImplemented - _NumberOp = NotImplemented - _ComparisonOp = NotImplemented + _BoolOp = Any + _BoolBitOp = Any + _BoolSub = Any + _BoolTrueDiv = Any + _BoolMod = Any + _BoolDivMod = Any + _TD64Div = Any + _IntTrueDiv = Any + _UnsignedIntOp = Any + _UnsignedIntBitOp = Any + _UnsignedIntMod = Any + _UnsignedIntDivMod = Any + _SignedIntOp = Any + _SignedIntBitOp = Any + _SignedIntMod = Any + _SignedIntDivMod = Any + _FloatOp = Any + _FloatMod = Any + _FloatDivMod = Any + _ComplexOp = Any + _NumberOp = Any + _ComparisonOp = Any diff --git a/numpy/typing/_char_codes.py b/numpy/typing/_char_codes.py index 6b33f995d..22ee168e9 100644 --- a/numpy/typing/_char_codes.py +++ b/numpy/typing/_char_codes.py @@ -8,7 +8,7 @@ if sys.version_info >= (3, 8): elif _HAS_TYPING_EXTENSIONS: from typing_extensions import Literal -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: +if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): _BoolCodes = Literal["?", "=?", "<?", ">?", "bool", "bool_", "bool8"] _UInt8Codes = Literal["uint8", "u1", "=u1", "<u1", ">u1"] @@ -120,52 +120,52 @@ if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: ] else: - _BoolCodes = NotImplemented - - _UInt8Codes = NotImplemented - _UInt16Codes = NotImplemented - _UInt32Codes = NotImplemented - _UInt64Codes = NotImplemented - - _Int8Codes = NotImplemented - _Int16Codes = NotImplemented - _Int32Codes = NotImplemented - _Int64Codes = NotImplemented - - _Float16Codes = NotImplemented - _Float32Codes = NotImplemented - _Float64Codes = NotImplemented - - _Complex64Codes = NotImplemented - _Complex128Codes = NotImplemented - - _ByteCodes = NotImplemented - _ShortCodes = NotImplemented - _IntCCodes = NotImplemented - _IntPCodes = NotImplemented - _IntCodes = NotImplemented - _LongLongCodes = NotImplemented - - _UByteCodes = NotImplemented - _UShortCodes = NotImplemented - _UIntCCodes = NotImplemented - _UIntPCodes = NotImplemented - _UIntCodes = NotImplemented - _ULongLongCodes = NotImplemented - - _HalfCodes = NotImplemented - _SingleCodes = NotImplemented - _DoubleCodes = NotImplemented - _LongDoubleCodes = NotImplemented - - _CSingleCodes = NotImplemented - _CDoubleCodes = NotImplemented - _CLongDoubleCodes = NotImplemented - - _StrCodes = NotImplemented - _BytesCodes = NotImplemented - _VoidCodes = NotImplemented - _ObjectCodes = NotImplemented - - _DT64Codes = NotImplemented - _TD64Codes = NotImplemented + _BoolCodes = Any + + _UInt8Codes = Any + _UInt16Codes = Any + _UInt32Codes = Any + _UInt64Codes = Any + + _Int8Codes = Any + _Int16Codes = Any + _Int32Codes = Any + _Int64Codes = Any + + _Float16Codes = Any + _Float32Codes = Any + _Float64Codes = Any + + _Complex64Codes = Any + _Complex128Codes = Any + + _ByteCodes = Any + _ShortCodes = Any + _IntCCodes = Any + _IntPCodes = Any + _IntCodes = Any + _LongLongCodes = Any + + _UByteCodes = Any + _UShortCodes = Any + _UIntCCodes = Any + _UIntPCodes = Any + _UIntCodes = Any + _ULongLongCodes = Any + + _HalfCodes = Any + _SingleCodes = Any + _DoubleCodes = Any + _LongDoubleCodes = Any + + _CSingleCodes = Any + _CDoubleCodes = Any + _CLongDoubleCodes = Any + + _StrCodes = Any + _BytesCodes = Any + _VoidCodes = Any + _ObjectCodes = Any + + _DT64Codes = Any + _TD64Codes = Any diff --git a/numpy/typing/_dtype_like.py b/numpy/typing/_dtype_like.py index 405cc4a3c..b2ce3adb4 100644 --- a/numpy/typing/_dtype_like.py +++ b/numpy/typing/_dtype_like.py @@ -11,9 +11,6 @@ if sys.version_info >= (3, 8): from typing import Protocol, TypedDict elif _HAS_TYPING_EXTENSIONS: from typing_extensions import Protocol, TypedDict - -if sys.version_info >= (3, 9): - from types import GenericAlias else: from ._generic_alias import _GenericAlias as GenericAlias @@ -62,7 +59,7 @@ from ._char_codes import ( _DTypeLikeNested = Any # TODO: wait for support for recursive types _DType_co = TypeVar("_DType_co", covariant=True, bound=DType[Any]) -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: +if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): # Mandatory keys class _DTypeDictBase(TypedDict): names: Sequence[str] @@ -81,7 +78,7 @@ if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS: def dtype(self) -> _DType_co: ... else: - _DTypeDict = NotImplemented + _DTypeDict = Any class _SupportsDType: ... _SupportsDType = GenericAlias(_SupportsDType, _DType_co) diff --git a/numpy/typing/_extended_precision.py b/numpy/typing/_extended_precision.py index 3f1ce2038..0900bc659 100644 --- a/numpy/typing/_extended_precision.py +++ b/numpy/typing/_extended_precision.py @@ -28,15 +28,15 @@ if TYPE_CHECKING: complex256 = np.complexfloating[_128Bit, _128Bit] complex512 = np.complexfloating[_256Bit, _256Bit] else: - uint128 = NotImplemented - uint256 = NotImplemented - int128 = NotImplemented - int256 = NotImplemented - float80 = NotImplemented - float96 = NotImplemented - float128 = NotImplemented - float256 = NotImplemented - complex160 = NotImplemented - complex192 = NotImplemented - complex256 = NotImplemented - complex512 = NotImplemented + uint128 = Any + uint256 = Any + int128 = Any + int256 = Any + float80 = Any + float96 = Any + float128 = Any + float256 = Any + complex160 = Any + complex192 = Any + complex256 = Any + complex512 = Any diff --git a/numpy/typing/_generic_alias.py b/numpy/typing/_generic_alias.py index 0d30f54ca..8d65ef855 100644 --- a/numpy/typing/_generic_alias.py +++ b/numpy/typing/_generic_alias.py @@ -93,7 +93,7 @@ class _GenericAlias: return super().__getattribute__("_origin") @property - def __args__(self) -> Tuple[Any, ...]: + def __args__(self) -> Tuple[object, ...]: return super().__getattribute__("_args") @property @@ -101,16 +101,23 @@ class _GenericAlias: """Type variables in the ``GenericAlias``.""" return super().__getattribute__("_parameters") - def __init__(self, origin: type, args: Any) -> None: + def __init__( + self, + origin: type, + args: object | Tuple[object, ...], + ) -> None: self._origin = origin self._args = args if isinstance(args, tuple) else (args,) - self._parameters = tuple(_parse_parameters(args)) + self._parameters = tuple(_parse_parameters(self.__args__)) @property def __call__(self) -> type: return self.__origin__ - def __reduce__(self: _T) -> Tuple[Type[_T], Tuple[type, Tuple[Any, ...]]]: + def __reduce__(self: _T) -> Tuple[ + Type[_T], + Tuple[type, Tuple[object, ...]], + ]: cls = type(self) return cls, (self.__origin__, self.__args__) @@ -148,7 +155,7 @@ class _GenericAlias: origin = _to_str(self.__origin__) return f"{origin}[{args}]" - def __getitem__(self: _T, key: Any) -> _T: + def __getitem__(self: _T, key: object | Tuple[object, ...]) -> _T: """Return ``self[key]``.""" key_tup = key if isinstance(key, tuple) else (key,) diff --git a/numpy/typing/_shape.py b/numpy/typing/_shape.py index 0742be8a9..75698f3d3 100644 --- a/numpy/typing/_shape.py +++ b/numpy/typing/_shape.py @@ -1,5 +1,5 @@ import sys -from typing import Sequence, Tuple, Union +from typing import Sequence, Tuple, Union, Any from . import _HAS_TYPING_EXTENSIONS @@ -8,7 +8,7 @@ if sys.version_info >= (3, 8): elif _HAS_TYPING_EXTENSIONS: from typing_extensions import SupportsIndex else: - SupportsIndex = NotImplemented + SupportsIndex = Any _Shape = Tuple[int, ...] diff --git a/numpy/typing/_ufunc.pyi b/numpy/typing/_ufunc.pyi index f4fead504..be1e654c2 100644 --- a/numpy/typing/_ufunc.pyi +++ b/numpy/typing/_ufunc.pyi @@ -16,7 +16,7 @@ from typing import ( TypeVar, ) -from numpy import ufunc, _Casting, _OrderKACF +from numpy import ufunc, _CastingKind, _OrderKACF from numpy.typing import NDArray from ._shape import _ShapeLike @@ -81,7 +81,7 @@ class _UFunc_Nin1_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): out: None = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -95,7 +95,7 @@ class _UFunc_Nin1_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -133,7 +133,7 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): out: None = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -148,7 +148,7 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -200,7 +200,7 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): *, out: None = ..., where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -215,7 +215,7 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): *, out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -257,7 +257,7 @@ class _UFunc_Nin1_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): __out2: None = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -273,7 +273,7 @@ class _UFunc_Nin1_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): *, out: _2Tuple[NDArray[Any]] = ..., where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -316,7 +316,7 @@ class _UFunc_Nin2_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): __out2: None = ..., *, where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -333,7 +333,7 @@ class _UFunc_Nin2_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): *, out: _2Tuple[NDArray[Any]] = ..., where: None | _ArrayLikeBool_co = ..., - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -378,7 +378,7 @@ class _GUFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): __x2: ArrayLike, out: None = ..., *, - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., @@ -393,7 +393,7 @@ class _GUFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): __x2: ArrayLike, out: NDArray[Any] | Tuple[NDArray[Any]], *, - casting: _Casting = ..., + casting: _CastingKind = ..., order: _OrderKACF = ..., dtype: DTypeLike = ..., subok: bool = ..., diff --git a/numpy/typing/tests/data/fail/array_constructors.py b/numpy/typing/tests/data/fail/array_constructors.py index f13fdacb2..0e2250513 100644 --- a/numpy/typing/tests/data/fail/array_constructors.py +++ b/numpy/typing/tests/data/fail/array_constructors.py @@ -7,12 +7,12 @@ np.require(a, requirements=1) # E: No overload variant np.require(a, requirements="TEST") # E: incompatible type np.zeros("test") # E: incompatible type -np.zeros() # E: Missing positional argument +np.zeros() # E: require at least one argument np.ones("test") # E: incompatible type np.ones() # E: Missing positional argument -np.array(0, float, True) # E: Too many positional +np.array(0, float, True) # E: No overload variant np.linspace(None, 'bob') # E: No overload variant np.linspace(0, 2, num=10.0) # E: No overload variant @@ -27,5 +27,5 @@ np.logspace(0, 2, base=None) # E: Argument "base" np.geomspace(None, 'bob') # E: Argument 1 np.stack(generator) # E: No overload variant -np.hstack({1, 2}) # E: incompatible type -np.vstack(1) # E: incompatible type +np.hstack({1, 2}) # E: No overload variant +np.vstack(1) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/array_pad.py b/numpy/typing/tests/data/fail/array_pad.py new file mode 100644 index 000000000..2be51a871 --- /dev/null +++ b/numpy/typing/tests/data/fail/array_pad.py @@ -0,0 +1,6 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] + +np.pad(AR_i8, 2, mode="bob") # E: No overload variant diff --git a/numpy/typing/tests/data/fail/constants.py b/numpy/typing/tests/data/fail/constants.py index 67ee0e0bc..cf6d760dc 100644 --- a/numpy/typing/tests/data/fail/constants.py +++ b/numpy/typing/tests/data/fail/constants.py @@ -4,3 +4,4 @@ np.Inf = np.Inf # E: Cannot assign to final np.ALLOW_THREADS = np.ALLOW_THREADS # E: Cannot assign to final np.little_endian = np.little_endian # E: Cannot assign to final np.UFUNC_PYVALS_NAME = np.UFUNC_PYVALS_NAME # E: Cannot assign to final +np.CLIP = 2 # E: Incompatible types diff --git a/numpy/typing/tests/data/fail/multiarray.py b/numpy/typing/tests/data/fail/multiarray.py new file mode 100644 index 000000000..50361ec43 --- /dev/null +++ b/numpy/typing/tests/data/fail/multiarray.py @@ -0,0 +1,49 @@ +from typing import List +import numpy as np +import numpy.typing as npt + +i8: np.int64 + +AR_b: npt.NDArray[np.bool_] +AR_u1: npt.NDArray[np.uint8] +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_M: npt.NDArray[np.datetime64] + +M: np.datetime64 + +AR_LIKE_f: List[float] + +def func(a: int) -> None: ... + +np.where(AR_b, 1) # E: No overload variant + +np.can_cast(AR_f8, 1) # E: incompatible type + +np.vdot(AR_M, AR_M) # E: incompatible type + +np.copyto(AR_LIKE_f, AR_f8) # E: incompatible type + +np.putmask(AR_LIKE_f, [True, True, False], 1.5) # E: incompatible type + +np.packbits(AR_f8) # E: incompatible type +np.packbits(AR_u1, bitorder=">") # E: incompatible type + +np.unpackbits(AR_i8) # E: incompatible type +np.unpackbits(AR_u1, bitorder=">") # E: incompatible type + +np.shares_memory(1, 1, max_work=i8) # E: incompatible type +np.may_share_memory(1, 1, max_work=i8) # E: incompatible type + +np.arange(M) # E: No overload variant +np.arange(stop=10) # E: No overload variant + +np.datetime_data(int) # E: incompatible type + +np.busday_offset("2012", 10) # E: incompatible type + +np.datetime_as_string("2012") # E: incompatible type + +np.compare_chararrays("a", b"a", "==", False) # E: No overload variant + +np.add_docstring(func, None) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/testing.py b/numpy/typing/tests/data/fail/testing.py new file mode 100644 index 000000000..e753a9810 --- /dev/null +++ b/numpy/typing/tests/data/fail/testing.py @@ -0,0 +1,26 @@ +import numpy as np +import numpy.typing as npt + +AR_U: npt.NDArray[np.str_] + +def func() -> bool: ... + +np.testing.assert_(True, msg=1) # E: incompatible type +np.testing.build_err_msg(1, "test") # E: incompatible type +np.testing.assert_almost_equal(AR_U, AR_U) # E: incompatible type +np.testing.assert_approx_equal([1, 2, 3], [1, 2, 3]) # E: incompatible type +np.testing.assert_array_almost_equal(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_less(AR_U, AR_U) # E: incompatible type +np.testing.assert_string_equal(b"a", b"a") # E: incompatible type + +np.testing.assert_raises(expected_exception=TypeError, callable=func) # E: No overload variant +np.testing.assert_raises_regex(expected_exception=TypeError, expected_regex="T", callable=func) # E: No overload variant + +np.testing.assert_allclose(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_almost_equal_nulp(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_max_ulp(AR_U, AR_U) # E: incompatible type + +np.testing.assert_warns(warning_class=RuntimeWarning, func=func) # E: No overload variant +np.testing.assert_no_warnings(func=func) # E: No overload variant + +np.testing.assert_no_gc_cycles(func=func) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/twodim_base.py b/numpy/typing/tests/data/fail/twodim_base.py new file mode 100644 index 000000000..ab34a374c --- /dev/null +++ b/numpy/typing/tests/data/fail/twodim_base.py @@ -0,0 +1,37 @@ +from typing import Any, List, TypeVar + +import numpy as np +import numpy.typing as npt + + +def func1(ar: npt.NDArray[Any], a: int) -> npt.NDArray[np.str_]: + pass + + +def func2(ar: npt.NDArray[Any], a: float) -> float: + pass + + +AR_b: npt.NDArray[np.bool_] +AR_m: npt.NDArray[np.timedelta64] + +AR_LIKE_b: List[bool] + +np.eye(10, M=20.0) # E: No overload variant +np.eye(10, k=2.5, dtype=int) # E: No overload variant + +np.diag(AR_b, k=0.5) # E: No overload variant +np.diagflat(AR_b, k=0.5) # E: No overload variant + +np.tri(10, M=20.0) # E: No overload variant +np.tri(10, k=2.5, dtype=int) # E: No overload variant + +np.tril(AR_b, k=0.5) # E: No overload variant +np.triu(AR_b, k=0.5) # E: No overload variant + +np.vander(AR_m) # E: incompatible type + +np.histogram2d(AR_m) # E: No overload variant + +np.mask_indices(10, func1) # E: incompatible type +np.mask_indices(10, func2, 10.5) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/type_check.py b/numpy/typing/tests/data/fail/type_check.py new file mode 100644 index 000000000..95f52bfbd --- /dev/null +++ b/numpy/typing/tests/data/fail/type_check.py @@ -0,0 +1,13 @@ +import numpy as np +import numpy.typing as npt + +DTYPE_i8: np.dtype[np.int64] + +np.mintypecode(DTYPE_i8) # E: incompatible type +np.iscomplexobj(DTYPE_i8) # E: incompatible type +np.isrealobj(DTYPE_i8) # E: incompatible type + +np.typename(DTYPE_i8) # E: No overload variant +np.typename("invalid") # E: No overload variant + +np.common_type(np.timedelta64()) # E: incompatible type diff --git a/numpy/typing/tests/data/pass/lib_utils.py b/numpy/typing/tests/data/pass/lib_utils.py index c602923d9..0a15dad22 100644 --- a/numpy/typing/tests/data/pass/lib_utils.py +++ b/numpy/typing/tests/data/pass/lib_utils.py @@ -6,7 +6,7 @@ from typing import Any import numpy as np FILE = StringIO() -AR: np.ndarray[Any, np.dtype[np.float64]] = np.arange(10).astype(np.float64) +AR = np.arange(10, dtype=np.float64) def func(a: int) -> bool: ... diff --git a/numpy/typing/tests/data/pass/multiarray.py b/numpy/typing/tests/data/pass/multiarray.py index e2b5d16a0..e5d33c673 100644 --- a/numpy/typing/tests/data/pass/multiarray.py +++ b/numpy/typing/tests/data/pass/multiarray.py @@ -1,37 +1,77 @@ -from __future__ import annotations - from typing import Any import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] = np.array([1.0]) +AR_i4 = np.array([1], dtype=np.int32) +AR_u1 = np.array([1], dtype=np.uint8) -AR_f8: np.ndarray[Any, np.dtype[np.float64]] = np.array([1.0]) -AR_i8: np.ndarray[Any, np.dtype[np.int_]] = np.array([1]) +AR_LIKE_f = [1.5] +AR_LIKE_i = [1] b_f8 = np.broadcast(AR_f8) -b_i8_f8_f8 = np.broadcast(AR_i8, AR_f8, AR_f8) +b_i4_f8_f8 = np.broadcast(AR_i4, AR_f8, AR_f8) next(b_f8) -next(b_i8_f8_f8) - b_f8.reset() -b_i8_f8_f8.reset() - b_f8.index -b_i8_f8_f8.index - b_f8.iters -b_i8_f8_f8.iters - b_f8.nd -b_i8_f8_f8.nd - b_f8.ndim -b_i8_f8_f8.ndim - b_f8.numiter -b_i8_f8_f8.numiter - b_f8.shape -b_i8_f8_f8.shape - b_f8.size -b_i8_f8_f8.size + +next(b_i4_f8_f8) +b_i4_f8_f8.reset() +b_i4_f8_f8.ndim +b_i4_f8_f8.index +b_i4_f8_f8.iters +b_i4_f8_f8.nd +b_i4_f8_f8.numiter +b_i4_f8_f8.shape +b_i4_f8_f8.size + +np.inner(AR_f8, AR_i4) + +np.where([True, True, False]) +np.where([True, True, False], 1, 0) + +np.lexsort([0, 1, 2]) + +np.can_cast(np.dtype("i8"), int) +np.can_cast(AR_f8, "f8") +np.can_cast(AR_f8, np.complex128, casting="unsafe") + +np.min_scalar_type([1]) +np.min_scalar_type(AR_f8) + +np.result_type(int, AR_i4) +np.result_type(AR_f8, AR_u1) +np.result_type(AR_f8, np.complex128) + +np.dot(AR_LIKE_f, AR_i4) +np.dot(AR_u1, 1) +np.dot(1.5j, 1) +np.dot(AR_u1, 1, out=AR_f8) + +np.vdot(AR_LIKE_f, AR_i4) +np.vdot(AR_u1, 1) +np.vdot(1.5j, 1) + +np.bincount(AR_i4) + +np.copyto(AR_f8, [1.6]) + +np.putmask(AR_f8, [True], 1.5) + +np.packbits(AR_i4) +np.packbits(AR_u1) + +np.unpackbits(AR_u1) + +np.shares_memory(1, 2) +np.shares_memory(AR_f8, AR_f8, max_work=1) + +np.may_share_memory(1, 2) +np.may_share_memory(AR_f8, AR_f8, max_work=1) diff --git a/numpy/typing/tests/data/reveal/array_constructors.py b/numpy/typing/tests/data/reveal/array_constructors.py index 2e803a365..44c85e988 100644 --- a/numpy/typing/tests/data/reveal/array_constructors.py +++ b/numpy/typing/tests/data/reveal/array_constructors.py @@ -1,43 +1,117 @@ -from typing import List, Any +from typing import List, Any, TypeVar +from pathlib import Path + import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic, covariant=True) -class SubClass(np.ndarray): ... +class SubClass(np.ndarray[Any, np.dtype[_SCT]]): ... i8: np.int64 -A: np.ndarray -B: SubClass +A: npt.NDArray[np.float64] +B: SubClass[np.float64] C: List[int] -def func(i: int, j: int, **kwargs: Any) -> SubClass: ... - -reveal_type(np.asarray(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asarray(B)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asarray(C)) # E: numpy.ndarray[Any, Any] - -reveal_type(np.asanyarray(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asanyarray(B)) # E: SubClass -reveal_type(np.asanyarray(B, dtype=int)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asanyarray(C)) # E: numpy.ndarray[Any, Any] - -reveal_type(np.ascontiguousarray(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.ascontiguousarray(B)) # E: numpy.ndarray[Any, Any] -reveal_type(np.ascontiguousarray(C)) # E: numpy.ndarray[Any, Any] - -reveal_type(np.asfortranarray(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asfortranarray(B)) # E: numpy.ndarray[Any, Any] -reveal_type(np.asfortranarray(C)) # E: numpy.ndarray[Any, Any] - -reveal_type(np.require(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.require(B)) # E: SubClass -reveal_type(np.require(B, requirements=None)) # E: SubClass +def func(i: int, j: int, **kwargs: Any) -> SubClass[np.float64]: ... + +reveal_type(np.empty_like(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.empty_like(B)) # E: SubClass[{float64}] +reveal_type(np.empty_like([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.empty_like(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.empty_like(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.array(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.array(B)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.array(B, subok=True)) # E: SubClass[{float64}] +reveal_type(np.array([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.array(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.array(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.zeros([1, 5, 6])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.zeros([1, 5, 6], dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.zeros([1, 5, 6], dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.empty([1, 5, 6])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.empty([1, 5, 6], dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.empty([1, 5, 6], dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.concatenate(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.concatenate([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.concatenate(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.concatenate(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.concatenate([1, 1.0], out=A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.asarray(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asarray(B)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asarray([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.asarray(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.asarray(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.asanyarray(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asanyarray(B)) # E: SubClass[{float64}] +reveal_type(np.asanyarray([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.asanyarray(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.asanyarray(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.ascontiguousarray(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.ascontiguousarray(B)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.ascontiguousarray([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.ascontiguousarray(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.ascontiguousarray(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.asfortranarray(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asfortranarray(B)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asfortranarray([1, 1.0])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.asfortranarray(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.asfortranarray(A, dtype='c16')) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.fromstring("1 1 1", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromstring(b"1 1 1", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromstring("1 1 1", dtype=np.int64, sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.fromstring(b"1 1 1", dtype=np.int64, sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.fromstring("1 1 1", dtype="c16", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.fromstring(b"1 1 1", dtype="c16", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.fromfile("test.txt", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromfile("test.txt", dtype=np.int64, sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.fromfile("test.txt", dtype="c16", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +with open("test.txt") as f: + reveal_type(np.fromfile(f, sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + reveal_type(np.fromfile(b"test.txt", sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + reveal_type(np.fromfile(Path("test.txt"), sep=" ")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.fromiter("12345", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromiter("12345", float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.frombuffer(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.frombuffer(A, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.frombuffer(A, dtype="c16")) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.arange(False, True)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.arange(10)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.arange(0, 10, step=2)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.arange(10.0)) # E: numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]] +reveal_type(np.arange(start=0, stop=10.0)) # E: numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]] +reveal_type(np.arange(np.timedelta64(0))) # E: numpy.ndarray[Any, numpy.dtype[numpy.timedelta64]] +reveal_type(np.arange(0, np.timedelta64(10))) # E: numpy.ndarray[Any, numpy.dtype[numpy.timedelta64]] +reveal_type(np.arange(np.datetime64("0"), np.datetime64("10"))) # E: numpy.ndarray[Any, numpy.dtype[numpy.datetime64]] +reveal_type(np.arange(10, dtype=np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.arange(0, 10, step=2, dtype=np.int16)) # E: numpy.ndarray[Any, numpy.dtype[{int16}]] +reveal_type(np.arange(10, dtype=int)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.arange(0, 10, dtype="f8")) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.require(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.require(B)) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements=None)) # E: SubClass[{float64}] reveal_type(np.require(B, dtype=int)) # E: numpy.ndarray[Any, Any] reveal_type(np.require(B, requirements="E")) # E: numpy.ndarray[Any, Any] reveal_type(np.require(B, requirements=["ENSUREARRAY"])) # E: numpy.ndarray[Any, Any] reveal_type(np.require(B, requirements={"F", "E"})) # E: numpy.ndarray[Any, Any] -reveal_type(np.require(B, requirements=["C", "OWNDATA"])) # E: SubClass -reveal_type(np.require(B, requirements="W")) # E: SubClass -reveal_type(np.require(B, requirements="A")) # E: SubClass +reveal_type(np.require(B, requirements=["C", "OWNDATA"])) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements="W")) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements="A")) # E: SubClass[{float64}] reveal_type(np.require(C)) # E: numpy.ndarray[Any, Any] reveal_type(np.linspace(0, 10)) # E: numpy.ndarray[Any, Any] @@ -45,24 +119,19 @@ reveal_type(np.linspace(0, 10, retstep=True)) # E: Tuple[numpy.ndarray[Any, Any reveal_type(np.logspace(0, 10)) # E: numpy.ndarray[Any, Any] reveal_type(np.geomspace(1, 10)) # E: numpy.ndarray[Any, Any] -reveal_type(np.zeros_like(A)) # E: numpy.ndarray[Any, Any] +reveal_type(np.zeros_like(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.zeros_like(C)) # E: numpy.ndarray[Any, Any] reveal_type(np.zeros_like(B)) # E: SubClass reveal_type(np.zeros_like(B, dtype=np.int64)) # E: numpy.ndarray[Any, Any] -reveal_type(np.ones_like(A)) # E: numpy.ndarray[Any, Any] +reveal_type(np.ones_like(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.ones_like(C)) # E: numpy.ndarray[Any, Any] reveal_type(np.ones_like(B)) # E: SubClass reveal_type(np.ones_like(B, dtype=np.int64)) # E: numpy.ndarray[Any, Any] -reveal_type(np.empty_like(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.empty_like(C)) # E: numpy.ndarray[Any, Any] -reveal_type(np.empty_like(B)) # E: SubClass -reveal_type(np.empty_like(B, dtype=np.int64)) # E: numpy.ndarray[Any, Any] - -reveal_type(np.full_like(A, i8)) # E: numpy.ndarray[Any, Any] +reveal_type(np.full_like(A, i8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.full_like(C, i8)) # E: numpy.ndarray[Any, Any] -reveal_type(np.full_like(B, i8)) # E: SubClass +reveal_type(np.full_like(B, i8)) # E: SubClass[{float64}] reveal_type(np.full_like(B, i8, dtype=np.int64)) # E: numpy.ndarray[Any, Any] reveal_type(np.ones(1)) # E: numpy.ndarray[Any, Any] @@ -74,29 +143,31 @@ reveal_type(np.full([1, 1, 1], i8)) # E: numpy.ndarray[Any, Any] reveal_type(np.indices([1, 2, 3])) # E: numpy.ndarray[Any, Any] reveal_type(np.indices([1, 2, 3], sparse=True)) # E: tuple[numpy.ndarray[Any, Any]] -reveal_type(np.fromfunction(func, (3, 5))) # E: SubClass +reveal_type(np.fromfunction(func, (3, 5))) # E: SubClass[{float64}] reveal_type(np.identity(10)) # E: numpy.ndarray[Any, Any] -reveal_type(np.atleast_1d(A)) # E: numpy.ndarray[Any, Any] -reveal_type(np.atleast_1d(C)) # E: numpy.ndarray[Any, Any] -reveal_type(np.atleast_1d(A, A)) # E: list[numpy.ndarray[Any, Any]] -reveal_type(np.atleast_1d(A, C)) # E: list[numpy.ndarray[Any, Any]] -reveal_type(np.atleast_1d(C, C)) # E: list[numpy.ndarray[Any, Any]] +reveal_type(np.atleast_1d(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.atleast_1d(C)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.atleast_1d(A, A)) # E: list[numpy.ndarray[Any, numpy.dtype[Any]]] +reveal_type(np.atleast_1d(A, C)) # E: list[numpy.ndarray[Any, numpy.dtype[Any]]] +reveal_type(np.atleast_1d(C, C)) # E: list[numpy.ndarray[Any, numpy.dtype[Any]]] -reveal_type(np.atleast_2d(A)) # E: numpy.ndarray[Any, Any] +reveal_type(np.atleast_2d(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] -reveal_type(np.atleast_3d(A)) # E: numpy.ndarray[Any, Any] +reveal_type(np.atleast_3d(A)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] -reveal_type(np.vstack([A, A])) # E: numpy.ndarray[Any, Any] -reveal_type(np.vstack([A, C])) # E: numpy.ndarray[Any, Any] -reveal_type(np.vstack([C, C])) # E: numpy.ndarray[Any, Any] +reveal_type(np.vstack([A, A])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.vstack([A, C])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.vstack([C, C])) # E: numpy.ndarray[Any, numpy.dtype[Any]] -reveal_type(np.hstack([A, A])) # E: numpy.ndarray[Any, Any] +reveal_type(np.hstack([A, A])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] -reveal_type(np.stack([A, A])) # E: numpy.ndarray[Any, Any] -reveal_type(np.stack([A, A], axis=0)) # E: numpy.ndarray[Any, Any] -reveal_type(np.stack([A, A], out=B)) # E: SubClass +reveal_type(np.stack([A, A])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.stack([A, C])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.stack([C, C])) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.stack([A, A], axis=0)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.stack([A, A], out=B)) # E: SubClass[{float64}] -reveal_type(np.block([[A, A], [A, A]])) # E: numpy.ndarray[Any, Any] -reveal_type(np.block(C)) # E: numpy.ndarray[Any, Any] +reveal_type(np.block([[A, A], [A, A]])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.block(C)) # E: numpy.ndarray[Any, numpy.dtype[Any]] diff --git a/numpy/typing/tests/data/reveal/arraypad.py b/numpy/typing/tests/data/reveal/arraypad.py new file mode 100644 index 000000000..ba5577ee0 --- /dev/null +++ b/numpy/typing/tests/data/reveal/arraypad.py @@ -0,0 +1,22 @@ +from typing import List, Any, Mapping, Tuple +from typing_extensions import SupportsIndex + +import numpy as np +import numpy.typing as npt + +def mode_func( + ar: npt.NDArray[np.number[Any]], + width: Tuple[int, int], + iaxis: SupportsIndex, + kwargs: Mapping[str, Any], +) -> None: ... + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_LIKE: List[int] + +reveal_type(np.pad(AR_i8, (2, 3), "constant")) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.pad(AR_LIKE, (2, 3), "constant")) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.pad(AR_f8, (2, 3), mode_func)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.pad(AR_f8, (2, 3), mode_func, a=1, b=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] diff --git a/numpy/typing/tests/data/reveal/arrayterator.py b/numpy/typing/tests/data/reveal/arrayterator.py index b57861d00..ea4e75612 100644 --- a/numpy/typing/tests/data/reveal/arrayterator.py +++ b/numpy/typing/tests/data/reveal/arrayterator.py @@ -10,7 +10,7 @@ reveal_type(ar_iter.start) # E: builtins.list[builtins.int] reveal_type(ar_iter.stop) # E: builtins.list[builtins.int] reveal_type(ar_iter.step) # E: builtins.list[builtins.int] reveal_type(ar_iter.shape) # E: builtins.tuple[builtins.int] -reveal_type(ar_iter.flat) # E: 'typing.Generator[{int64}, None, None] +reveal_type(ar_iter.flat) # E: typing.Generator[{int64}, None, None] reveal_type(ar_iter.__array__()) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] diff --git a/numpy/typing/tests/data/reveal/constants.py b/numpy/typing/tests/data/reveal/constants.py index b2382e861..2adefc0c6 100644 --- a/numpy/typing/tests/data/reveal/constants.py +++ b/numpy/typing/tests/data/reveal/constants.py @@ -16,8 +16,8 @@ reveal_type(np.nan) # E: float reveal_type(np.pi) # E: float reveal_type(np.ALLOW_THREADS) # E: int -reveal_type(np.BUFSIZE) # E: int -reveal_type(np.CLIP) # E: int +reveal_type(np.BUFSIZE) # E: Literal[8192] +reveal_type(np.CLIP) # E: Literal[0] reveal_type(np.ERR_CALL) # E: int reveal_type(np.ERR_DEFAULT) # E: int reveal_type(np.ERR_IGNORE) # E: int @@ -30,17 +30,17 @@ reveal_type(np.FPE_DIVIDEBYZERO) # E: int reveal_type(np.FPE_INVALID) # E: int reveal_type(np.FPE_OVERFLOW) # E: int reveal_type(np.FPE_UNDERFLOW) # E: int -reveal_type(np.MAXDIMS) # E: int -reveal_type(np.MAY_SHARE_BOUNDS) # E: int -reveal_type(np.MAY_SHARE_EXACT) # E: int -reveal_type(np.RAISE) # E: int +reveal_type(np.MAXDIMS) # E: Literal[32] +reveal_type(np.MAY_SHARE_BOUNDS) # E: Literal[0] +reveal_type(np.MAY_SHARE_EXACT) # E: Literal[-1] +reveal_type(np.RAISE) # E: Literal[2] reveal_type(np.SHIFT_DIVIDEBYZERO) # E: int reveal_type(np.SHIFT_INVALID) # E: int reveal_type(np.SHIFT_OVERFLOW) # E: int reveal_type(np.SHIFT_UNDERFLOW) # E: int reveal_type(np.UFUNC_BUFSIZE_DEFAULT) # E: int -reveal_type(np.WRAP) # E: int -reveal_type(np.tracemalloc_domain) # E: int +reveal_type(np.WRAP) # E: Literal[1] +reveal_type(np.tracemalloc_domain) # E: Literal[389047] reveal_type(np.little_endian) # E: bool reveal_type(np.True_) # E: numpy.bool_ diff --git a/numpy/typing/tests/data/reveal/dtype.py b/numpy/typing/tests/data/reveal/dtype.py index 215d89ead..eb1489bf3 100644 --- a/numpy/typing/tests/data/reveal/dtype.py +++ b/numpy/typing/tests/data/reveal/dtype.py @@ -1,7 +1,8 @@ import numpy as np -dtype_obj: np.dtype[np.str_] -void_dtype_obj: np.dtype[np.void] +dtype_U: np.dtype[np.str_] +dtype_V: np.dtype[np.void] +dtype_i8: np.dtype[np.int64] reveal_type(np.dtype(np.float64)) # E: numpy.dtype[{float64}] reveal_type(np.dtype(np.int64)) # E: numpy.dtype[{int64}] @@ -36,22 +37,30 @@ reveal_type(np.dtype("S8")) # E: numpy.dtype reveal_type(np.dtype(("U", 10))) # E: numpy.dtype[numpy.void] # Methods and attributes -reveal_type(dtype_obj.base) # E: numpy.dtype[numpy.str_] -reveal_type(dtype_obj.subdtype) # E: Union[Tuple[numpy.dtype[numpy.str_], builtins.tuple[builtins.int]], None] -reveal_type(dtype_obj.newbyteorder()) # E: numpy.dtype[numpy.str_] -reveal_type(dtype_obj.type) # E: Type[numpy.str_] -reveal_type(dtype_obj.name) # E: str -reveal_type(dtype_obj.names) # E: Union[builtins.tuple[builtins.str], None] - -reveal_type(dtype_obj * 0) # E: None -reveal_type(dtype_obj * 1) # E: numpy.dtype[numpy.str_] -reveal_type(dtype_obj * 2) # E: numpy.dtype[numpy.void] - -reveal_type(0 * dtype_obj) # E: Any -reveal_type(1 * dtype_obj) # E: Any -reveal_type(2 * dtype_obj) # E: Any - -reveal_type(void_dtype_obj["f0"]) # E: numpy.dtype[Any] -reveal_type(void_dtype_obj[0]) # E: numpy.dtype[Any] -reveal_type(void_dtype_obj[["f0", "f1"]]) # E: numpy.dtype[numpy.void] -reveal_type(void_dtype_obj[["f0"]]) # E: numpy.dtype[numpy.void] +reveal_type(dtype_U.base) # E: numpy.dtype[Any] +reveal_type(dtype_U.subdtype) # E: Union[None, Tuple[numpy.dtype[Any], builtins.tuple[builtins.int]]] +reveal_type(dtype_U.newbyteorder()) # E: numpy.dtype[numpy.str_] +reveal_type(dtype_U.type) # E: Type[numpy.str_] +reveal_type(dtype_U.name) # E: str +reveal_type(dtype_U.names) # E: Union[None, builtins.tuple[builtins.str]] + +reveal_type(dtype_U * 0) # E: numpy.dtype[numpy.str_] +reveal_type(dtype_U * 1) # E: numpy.dtype[numpy.str_] +reveal_type(dtype_U * 2) # E: numpy.dtype[numpy.str_] + +reveal_type(dtype_i8 * 0) # E: numpy.dtype[numpy.void] +reveal_type(dtype_i8 * 1) # E: numpy.dtype[{int64}] +reveal_type(dtype_i8 * 2) # E: numpy.dtype[numpy.void] + +reveal_type(0 * dtype_U) # E: numpy.dtype[numpy.str_] +reveal_type(1 * dtype_U) # E: numpy.dtype[numpy.str_] +reveal_type(2 * dtype_U) # E: numpy.dtype[numpy.str_] + +reveal_type(0 * dtype_i8) # E: numpy.dtype[Any] +reveal_type(1 * dtype_i8) # E: numpy.dtype[Any] +reveal_type(2 * dtype_i8) # E: numpy.dtype[Any] + +reveal_type(dtype_V["f0"]) # E: numpy.dtype[Any] +reveal_type(dtype_V[0]) # E: numpy.dtype[Any] +reveal_type(dtype_V[["f0", "f1"]]) # E: numpy.dtype[numpy.void] +reveal_type(dtype_V[["f0"]]) # E: numpy.dtype[numpy.void] diff --git a/numpy/typing/tests/data/reveal/modules.py b/numpy/typing/tests/data/reveal/modules.py index fa356969a..b045585b2 100644 --- a/numpy/typing/tests/data/reveal/modules.py +++ b/numpy/typing/tests/data/reveal/modules.py @@ -29,9 +29,6 @@ reveal_type(np.polynomial.laguerre) # E: ModuleType reveal_type(np.polynomial.legendre) # E: ModuleType reveal_type(np.polynomial.polynomial) # E: ModuleType -# TODO: Remove when annotations have been added to `np.testing.assert_equal` -reveal_type(np.testing.assert_equal) # E: Any - reveal_type(np.__path__) # E: list[builtins.str] reveal_type(np.__version__) # E: str reveal_type(np.__git_version__) # E: str diff --git a/numpy/typing/tests/data/reveal/multiarray.py b/numpy/typing/tests/data/reveal/multiarray.py index 33e9ede7c..cee51975e 100644 --- a/numpy/typing/tests/data/reveal/multiarray.py +++ b/numpy/typing/tests/data/reveal/multiarray.py @@ -1,35 +1,125 @@ -from typing import Any +from typing import Any, List, TypeVar +from pathlib import Path + import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic, covariant=True) + +class SubClass(np.ndarray[Any, np.dtype[_SCT]]): ... + +subclass: SubClass[np.float64] -AR_f8: np.ndarray[Any, np.dtype[np.float64]] -AR_i8: np.ndarray[Any, np.dtype[np.int64]] +AR_f8: npt.NDArray[np.float64] +AR_i8: npt.NDArray[np.int64] +AR_u1: npt.NDArray[np.uint8] +AR_m: npt.NDArray[np.timedelta64] +AR_M: npt.NDArray[np.datetime64] + +AR_LIKE_f: List[float] +AR_LIKE_i: List[int] + +m: np.timedelta64 +M: np.datetime64 b_f8 = np.broadcast(AR_f8) b_i8_f8_f8 = np.broadcast(AR_i8, AR_f8, AR_f8) -reveal_type(next(b_f8)) # E: tuple[Any] -reveal_type(next(b_i8_f8_f8)) # E: tuple[Any] +def func(a: int) -> bool: ... +reveal_type(next(b_f8)) # E: tuple[Any] reveal_type(b_f8.reset()) # E: None -reveal_type(b_i8_f8_f8.reset()) # E: None - reveal_type(b_f8.index) # E: int -reveal_type(b_i8_f8_f8.index) # E: int - reveal_type(b_f8.iters) # E: tuple[numpy.flatiter[Any]] -reveal_type(b_i8_f8_f8.iters) # E: tuple[numpy.flatiter[Any]] - reveal_type(b_f8.nd) # E: int -reveal_type(b_i8_f8_f8.nd) # E: int - reveal_type(b_f8.ndim) # E: int -reveal_type(b_i8_f8_f8.ndim) # E: int - reveal_type(b_f8.numiter) # E: int -reveal_type(b_i8_f8_f8.numiter) # E: int - reveal_type(b_f8.shape) # E: tuple[builtins.int] -reveal_type(b_i8_f8_f8.shape) # E: tuple[builtins.int] - reveal_type(b_f8.size) # E: int + +reveal_type(next(b_i8_f8_f8)) # E: tuple[Any] +reveal_type(b_i8_f8_f8.reset()) # E: None +reveal_type(b_i8_f8_f8.index) # E: int +reveal_type(b_i8_f8_f8.iters) # E: tuple[numpy.flatiter[Any]] +reveal_type(b_i8_f8_f8.nd) # E: int +reveal_type(b_i8_f8_f8.ndim) # E: int +reveal_type(b_i8_f8_f8.numiter) # E: int +reveal_type(b_i8_f8_f8.shape) # E: tuple[builtins.int] reveal_type(b_i8_f8_f8.size) # E: int + +reveal_type(np.inner(AR_f8, AR_i8)) # E: Any + +reveal_type(np.where([True, True, False])) # E: tuple[numpy.ndarray[Any, numpy.dtype[{intp}]]] +reveal_type(np.where([True, True, False], 1, 0)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.lexsort([0, 1, 2])) # E: Any + +reveal_type(np.can_cast(np.dtype("i8"), int)) # E: bool +reveal_type(np.can_cast(AR_f8, "f8")) # E: bool +reveal_type(np.can_cast(AR_f8, np.complex128, casting="unsafe")) # E: bool + +reveal_type(np.min_scalar_type([1])) # E: numpy.dtype[Any] +reveal_type(np.min_scalar_type(AR_f8)) # E: numpy.dtype[Any] + +reveal_type(np.result_type(int, [1])) # E: numpy.dtype[Any] +reveal_type(np.result_type(AR_f8, AR_u1)) # E: numpy.dtype[Any] +reveal_type(np.result_type(AR_f8, np.complex128)) # E: numpy.dtype[Any] + +reveal_type(np.dot(AR_LIKE_f, AR_i8)) # E: Any +reveal_type(np.dot(AR_u1, 1)) # E: Any +reveal_type(np.dot(1.5j, 1)) # E: Any +reveal_type(np.dot(AR_u1, 1, out=AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.vdot(AR_LIKE_f, AR_i8)) # E: numpy.floating[Any] +reveal_type(np.vdot(AR_u1, 1)) # E: numpy.signedinteger[Any] +reveal_type(np.vdot(1.5j, 1)) # E: numpy.complexfloating[Any, Any] + +reveal_type(np.bincount(AR_i8)) # E: numpy.ndarray[Any, numpy.dtype[{intp}]] + +reveal_type(np.copyto(AR_f8, [1., 1.5, 1.6])) # E: None + +reveal_type(np.putmask(AR_f8, [True, True, False], 1.5)) # E: None + +reveal_type(np.packbits(AR_i8)) # numpy.ndarray[Any, numpy.dtype[{uint8}]] +reveal_type(np.packbits(AR_u1)) # numpy.ndarray[Any, numpy.dtype[{uint8}]] + +reveal_type(np.unpackbits(AR_u1)) # numpy.ndarray[Any, numpy.dtype[{uint8}]] + +reveal_type(np.shares_memory(1, 2)) # E: bool +reveal_type(np.shares_memory(AR_f8, AR_f8, max_work=1)) # E: bool + +reveal_type(np.may_share_memory(1, 2)) # E: bool +reveal_type(np.may_share_memory(AR_f8, AR_f8, max_work=1)) # E: bool + +reveal_type(np.geterrobj()) # E: list[Any] + +reveal_type(np.seterrobj([8192, 521, None])) # E: None + +reveal_type(np.promote_types(np.int32, np.int64)) # E: numpy.dtype[Any] +reveal_type(np.promote_types("f4", float)) # E: numpy.dtype[Any] + +reveal_type(np.frompyfunc(func, 1, 1, identity=None)) # numpy.ufunc + +reveal_type(np.datetime_data("m8[D]")) # E: Tuple[builtins.str, builtins.int] +reveal_type(np.datetime_data(np.datetime64)) # E: Tuple[builtins.str, builtins.int] +reveal_type(np.datetime_data(np.dtype(np.timedelta64))) # E: Tuple[builtins.str, builtins.int] + +reveal_type(np.busday_count("2011-01", "2011-02")) # E: {int_} +reveal_type(np.busday_count(["2011-01"], "2011-02")) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] + +reveal_type(np.busday_offset(M, m)) # E: numpy.datetime64 +reveal_type(np.busday_offset(M, 5)) # E: numpy.datetime64 +reveal_type(np.busday_offset(AR_M, m)) # E: numpy.ndarray[Any, numpy.dtype[numpy.datetime64]] +reveal_type(np.busday_offset("2011-01", "2011-02", roll="forward")) # E: numpy.datetime64 +reveal_type(np.busday_offset(["2011-01"], "2011-02", roll="forward")) # E: numpy.ndarray[Any, numpy.dtype[numpy.datetime64]] + +reveal_type(np.is_busday("2012")) # E: numpy.bool_ +reveal_type(np.is_busday(["2012"])) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] + +reveal_type(np.datetime_as_string(M)) # E: numpy.str_ +reveal_type(np.datetime_as_string(AR_M)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] + +reveal_type(np.compare_chararrays("a", "b", "!=", rstrip=False)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.compare_chararrays(b"a", b"a", "==", True)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] + +reveal_type(np.add_docstring(func, "test")) # E: None diff --git a/numpy/typing/tests/data/reveal/ndarray_conversion.py b/numpy/typing/tests/data/reveal/ndarray_conversion.py index 4ee637b75..03f2faf43 100644 --- a/numpy/typing/tests/data/reveal/ndarray_conversion.py +++ b/numpy/typing/tests/data/reveal/ndarray_conversion.py @@ -1,12 +1,13 @@ import numpy as np +import numpy.typing as npt -nd = np.array([[1, 2], [3, 4]]) +nd: npt.NDArray[np.int_] = np.array([[1, 2], [3, 4]]) # item -reveal_type(nd.item()) # E: Any -reveal_type(nd.item(1)) # E: Any -reveal_type(nd.item(0, 1)) # E: Any -reveal_type(nd.item((0, 1))) # E: Any +reveal_type(nd.item()) # E: int +reveal_type(nd.item(1)) # E: int +reveal_type(nd.item(0, 1)) # E: int +reveal_type(nd.item((0, 1))) # E: int # tolist reveal_type(nd.tolist()) # E: Any @@ -19,36 +20,32 @@ reveal_type(nd.tolist()) # E: Any # dumps is pretty simple # astype -reveal_type(nd.astype("float")) # E: numpy.ndarray -reveal_type(nd.astype(float)) # E: numpy.ndarray -reveal_type(nd.astype(float, "K")) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe")) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe", True)) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe", True, True)) # E: numpy.ndarray +reveal_type(nd.astype("float")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.astype(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.astype(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True, True)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] # byteswap -reveal_type(nd.byteswap()) # E: numpy.ndarray -reveal_type(nd.byteswap(True)) # E: numpy.ndarray +reveal_type(nd.byteswap()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.byteswap(True)) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] # copy -reveal_type(nd.copy()) # E: numpy.ndarray -reveal_type(nd.copy("C")) # E: numpy.ndarray +reveal_type(nd.copy()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.copy("C")) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] -# view -class SubArray(np.ndarray): - pass - - -reveal_type(nd.view()) # E: numpy.ndarray -reveal_type(nd.view(np.int64)) # E: numpy.ndarray -# replace `Any` with `numpy.matrix` when `matrix` will be added to stubs -reveal_type(nd.view(np.int64, np.matrix)) # E: Any -reveal_type(nd.view(np.int64, SubArray)) # E: SubArray +reveal_type(nd.view()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.view(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.view(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.view(np.float64, np.matrix)) # E: numpy.matrix[Any, Any] # getfield -reveal_type(nd.getfield("float")) # E: numpy.ndarray -reveal_type(nd.getfield(float)) # E: numpy.ndarray -reveal_type(nd.getfield(float, 8)) # E: numpy.ndarray +reveal_type(nd.getfield("float")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.getfield(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.getfield(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.getfield(np.float64, 8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] # setflags does not return a value # fill does not return a value diff --git a/numpy/typing/tests/data/reveal/scalars.py b/numpy/typing/tests/data/reveal/scalars.py index d98388422..c081a5c67 100644 --- a/numpy/typing/tests/data/reveal/scalars.py +++ b/numpy/typing/tests/data/reveal/scalars.py @@ -114,3 +114,15 @@ reveal_type(f8.reshape(1)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(c16.reshape(1)) # E: numpy.ndarray[Any, numpy.dtype[{complex128}]] reveal_type(U.reshape(1)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] reveal_type(S.reshape(1)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bytes_]] + +reveal_type(i8.astype(float)) # E: Any +reveal_type(i8.astype(np.float64)) # E: {float64} + +reveal_type(i8.view()) # E: {int64} +reveal_type(i8.view(np.float64)) # E: {float64} +reveal_type(i8.view(float)) # E: Any +reveal_type(i8.view(np.float64, np.ndarray)) # E: {float64} + +reveal_type(i8.getfield(float)) # E: Any +reveal_type(i8.getfield(np.float64)) # E: {float64} +reveal_type(i8.getfield(np.float64, 8)) # E: {float64} diff --git a/numpy/typing/tests/data/reveal/testing.py b/numpy/typing/tests/data/reveal/testing.py new file mode 100644 index 000000000..2b040ff60 --- /dev/null +++ b/numpy/typing/tests/data/reveal/testing.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import re +import sys +from typing import Any, Callable, TypeVar +from pathlib import Path + +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_i8: npt.NDArray[np.int64] + +bool_obj: bool +suppress_obj: np.testing.suppress_warnings +FT = TypeVar("FT", bound=Callable[..., Any]) + +def func() -> int: ... + +def func2( + x: npt.NDArray[np.number[Any]], + y: npt.NDArray[np.number[Any]], +) -> npt.NDArray[np.bool_]: ... + +reveal_type(np.testing.KnownFailureException()) # E: KnownFailureException +reveal_type(np.testing.IgnoreException()) # E: IgnoreException + +reveal_type(np.testing.clear_and_catch_warnings(modules=[np.testing])) # E: _clear_and_catch_warnings_without_records +reveal_type(np.testing.clear_and_catch_warnings(True)) # E: _clear_and_catch_warnings_with_records +reveal_type(np.testing.clear_and_catch_warnings(False)) # E: _clear_and_catch_warnings_without_records +reveal_type(np.testing.clear_and_catch_warnings(bool_obj)) # E: clear_and_catch_warnings +reveal_type(np.testing.clear_and_catch_warnings.class_modules) # E: tuple[types.ModuleType] +reveal_type(np.testing.clear_and_catch_warnings.modules) # E: set[types.ModuleType] + +with np.testing.clear_and_catch_warnings(True) as c1: + reveal_type(c1) # E: builtins.list[warnings.WarningMessage] +with np.testing.clear_and_catch_warnings() as c2: + reveal_type(c2) # E: None + +reveal_type(np.testing.suppress_warnings("once")) # E: suppress_warnings +reveal_type(np.testing.suppress_warnings()(func)) # E: def () -> builtins.int +reveal_type(suppress_obj.filter(RuntimeWarning)) # E: None +reveal_type(suppress_obj.record(RuntimeWarning)) # E: list[warnings.WarningMessage] +with suppress_obj as c3: + reveal_type(c3) # E: suppress_warnings + +reveal_type(np.testing.verbose) # E: int +reveal_type(np.testing.IS_PYPY) # E: bool +reveal_type(np.testing.HAS_REFCOUNT) # E: bool +reveal_type(np.testing.HAS_LAPACK64) # E: bool + +reveal_type(np.testing.assert_(1, msg="test")) # E: None +reveal_type(np.testing.assert_(2, msg=lambda: "test")) # E: None + +if sys.platform == "win32" or sys.platform == "cygwin": + reveal_type(np.testing.memusage()) # E: builtins.int +elif sys.platform == "linux": + reveal_type(np.testing.memusage()) # E: Union[None, builtins.int] +else: + reveal_type(np.testing.memusage()) # E: <nothing> + +reveal_type(np.testing.jiffies()) # E: builtins.int + +reveal_type(np.testing.build_err_msg([0, 1, 2], "test")) # E: str +reveal_type(np.testing.build_err_msg(range(2), "test", header="header")) # E: str +reveal_type(np.testing.build_err_msg(np.arange(9).reshape(3, 3), "test", verbose=False)) # E: str +reveal_type(np.testing.build_err_msg("abc", "test", names=["x", "y"])) # E: str +reveal_type(np.testing.build_err_msg([1.0, 2.0], "test", precision=5)) # E: str + +reveal_type(np.testing.assert_equal({1}, {1})) # E: None +reveal_type(np.testing.assert_equal([1, 2, 3], [1, 2, 3], err_msg="fail")) # E: None +reveal_type(np.testing.assert_equal(1, 1.0, verbose=True)) # E: None + +reveal_type(np.testing.print_assert_equal('Test XYZ of func xyz', [0, 1], [0, 1])) # E: None + +reveal_type(np.testing.assert_almost_equal(1.0, 1.1)) # E: None +reveal_type(np.testing.assert_almost_equal([1, 2, 3], [1, 2, 3], err_msg="fail")) # E: None +reveal_type(np.testing.assert_almost_equal(1, 1.0, verbose=True)) # E: None +reveal_type(np.testing.assert_almost_equal(1, 1.0001, decimal=2)) # E: None + +reveal_type(np.testing.assert_approx_equal(1.0, 1.1)) # E: None +reveal_type(np.testing.assert_approx_equal("1", "2", err_msg="fail")) # E: None +reveal_type(np.testing.assert_approx_equal(1, 1.0, verbose=True)) # E: None +reveal_type(np.testing.assert_approx_equal(1, 1.0001, significant=2)) # E: None + +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, verbose=True)) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, header="header")) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, precision=np.int64())) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, equal_nan=False)) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, equal_inf=True)) # E: None + +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8, verbose=True)) # E: None + +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, verbose=True)) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, decimal=1)) # E: None + +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8, verbose=True)) # E: None + +reveal_type(np.testing.runstring("1 + 1", {})) # E: Any +reveal_type(np.testing.runstring("int64() + 1", {"int64": np.int64})) # E: Any + +reveal_type(np.testing.assert_string_equal("1", "1")) # E: None + +reveal_type(np.testing.rundocs()) # E: None +reveal_type(np.testing.rundocs("test.py")) # E: None +reveal_type(np.testing.rundocs(Path("test.py"), raise_on_error=True)) # E: None + +@np.testing.raises(RuntimeError, RuntimeWarning) +def func3(a: int) -> bool: ... + +reveal_type(func3) # E: def (a: builtins.int) -> builtins.bool + +reveal_type(np.testing.assert_raises(RuntimeWarning)) # E: _AssertRaisesContext[builtins.RuntimeWarning] +reveal_type(np.testing.assert_raises(RuntimeWarning, func3, 5)) # E: None + +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, r"test")) # E: _AssertRaisesContext[builtins.RuntimeWarning] +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, b"test", func3, 5)) # E: None +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, re.compile(b"test"), func3, 5)) # E: None + +class Test: ... + +def decorate(a: FT) -> FT: + return a + +reveal_type(np.testing.decorate_methods(Test, decorate)) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, None)) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, "test")) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, b"test")) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, re.compile("test"))) # E: None + +reveal_type(np.testing.measure("for i in range(1000): np.sqrt(i**2)")) # E: float +reveal_type(np.testing.measure(b"for i in range(1000): np.sqrt(i**2)", times=5)) # E: float + +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, rtol=0.005)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, atol=1)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, equal_nan=True)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, err_msg="err")) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, verbose=False)) # E: None + +reveal_type(np.testing.assert_array_almost_equal_nulp(AR_i8, AR_f8, nulp=2)) # E: None + +reveal_type(np.testing.assert_array_max_ulp(AR_i8, AR_f8, maxulp=2)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.testing.assert_array_max_ulp(AR_i8, AR_f8, dtype=np.float32)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.testing.assert_warns(RuntimeWarning)) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_warns(RuntimeWarning, func3, 5)) # E: bool + +reveal_type(np.testing.assert_no_warnings()) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_no_warnings(func3, 5)) # E: bool + +reveal_type(np.testing.tempdir("test_dir")) # E: _GeneratorContextManager[builtins.str] +reveal_type(np.testing.tempdir(prefix=b"test")) # E: _GeneratorContextManager[builtins.bytes] +reveal_type(np.testing.tempdir("test_dir", dir=Path("here"))) # E: _GeneratorContextManager[builtins.str] + +reveal_type(np.testing.temppath("test_dir", text=True)) # E: _GeneratorContextManager[builtins.str] +reveal_type(np.testing.temppath(prefix=b"test")) # E: _GeneratorContextManager[builtins.bytes] +reveal_type(np.testing.temppath("test_dir", dir=Path("here"))) # E: _GeneratorContextManager[builtins.str] + +reveal_type(np.testing.assert_no_gc_cycles()) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_no_gc_cycles(func3, 5)) # E: None + +reveal_type(np.testing.break_cycles()) # E: None + +reveal_type(np.testing.TestCase()) # E: unittest.case.TestCase +reveal_type(np.testing.run_module_suite(file_to_run="numpy/tests/test_matlib.py")) # E: None diff --git a/numpy/typing/tests/data/reveal/twodim_base.py b/numpy/typing/tests/data/reveal/twodim_base.py new file mode 100644 index 000000000..b95fbc71e --- /dev/null +++ b/numpy/typing/tests/data/reveal/twodim_base.py @@ -0,0 +1,72 @@ +from typing import Any, List, TypeVar + +import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic) + + +def func1(ar: npt.NDArray[_SCT], a: int) -> npt.NDArray[_SCT]: + pass + + +def func2(ar: npt.NDArray[np.number[Any]], a: str) -> npt.NDArray[np.float64]: + pass + + +AR_b: npt.NDArray[np.bool_] +AR_u: npt.NDArray[np.uint64] +AR_i: npt.NDArray[np.int64] +AR_f: npt.NDArray[np.float64] +AR_c: npt.NDArray[np.complex128] +AR_O: npt.NDArray[np.object_] + +AR_LIKE_b: List[bool] + +reveal_type(np.fliplr(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.fliplr(AR_LIKE_b)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.flipud(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.flipud(AR_LIKE_b)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.eye(10)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.eye(10, M=20, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.eye(10, k=2, dtype=int)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.diag(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.diag(AR_LIKE_b, k=0)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.diagflat(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.diagflat(AR_LIKE_b, k=0)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.tri(10)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.tri(10, M=20, dtype=np.int64)) # E: numpy.ndarray[Any, numpy.dtype[{int64}]] +reveal_type(np.tri(10, k=2, dtype=int)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.tril(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.tril(AR_LIKE_b, k=0)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.triu(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.triu(AR_LIKE_b, k=0)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.vander(AR_b)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.vander(AR_u)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.vander(AR_i, N=2)) # E: numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]] +reveal_type(np.vander(AR_f, increasing=True)) # E: numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]] +reveal_type(np.vander(AR_c)) # E: numpy.ndarray[Any, numpy.dtype[numpy.complexfloating[Any, Any]]] +reveal_type(np.vander(AR_O)) # E: numpy.ndarray[Any, numpy.dtype[numpy.object_]] + +reveal_type(np.histogram2d(AR_i, AR_b)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{float64}]], numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]], numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]]] +reveal_type(np.histogram2d(AR_f, AR_f)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{float64}]], numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]], numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]]] +reveal_type(np.histogram2d(AR_f, AR_c, weights=AR_LIKE_b)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{float64}]], numpy.ndarray[Any, numpy.dtype[numpy.complexfloating[Any, Any]]], numpy.ndarray[Any, numpy.dtype[numpy.complexfloating[Any, Any]]]] + +reveal_type(np.mask_indices(10, func1)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{intp}]], numpy.ndarray[Any, numpy.dtype[{intp}]]] +reveal_type(np.mask_indices(8, func2, "0")) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{intp}]], numpy.ndarray[Any, numpy.dtype[{intp}]]] + +reveal_type(np.tril_indices(10)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{int_}]], numpy.ndarray[Any, numpy.dtype[{int_}]]] + +reveal_type(np.tril_indices_from(AR_b)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{int_}]], numpy.ndarray[Any, numpy.dtype[{int_}]]] + +reveal_type(np.triu_indices(10)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{int_}]], numpy.ndarray[Any, numpy.dtype[{int_}]]] + +reveal_type(np.triu_indices_from(AR_b)) # E: Tuple[numpy.ndarray[Any, numpy.dtype[{int_}]], numpy.ndarray[Any, numpy.dtype[{int_}]]] diff --git a/numpy/typing/tests/data/reveal/type_check.py b/numpy/typing/tests/data/reveal/type_check.py new file mode 100644 index 000000000..416dd42a8 --- /dev/null +++ b/numpy/typing/tests/data/reveal/type_check.py @@ -0,0 +1,73 @@ +from typing import List +import numpy as np +import numpy.typing as npt + +f8: np.float64 +f: float + +# NOTE: Avoid importing the platform specific `np.float128` type +AR_i8: npt.NDArray[np.int64] +AR_i4: npt.NDArray[np.int32] +AR_f2: npt.NDArray[np.float16] +AR_f8: npt.NDArray[np.float64] +AR_f16: npt.NDArray[np.floating[npt._128Bit]] +AR_c8: npt.NDArray[np.complex64] +AR_c16: npt.NDArray[np.complex128] + +AR_LIKE_f: List[float] + +class RealObj: + real: slice + +class ImagObj: + imag: slice + +reveal_type(np.mintypecode(["f8"], typeset="qfQF")) + +reveal_type(np.asfarray(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asfarray(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.asfarray(AR_f8, dtype="c16")) # E: numpy.ndarray[Any, numpy.dtype[numpy.complexfloating[Any, Any]]] +reveal_type(np.asfarray(AR_f8, dtype="i8")) # E: numpy.ndarray[Any, numpy.dtype[numpy.floating[Any]]] + +reveal_type(np.real(RealObj())) # E: slice +reveal_type(np.real(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.real(AR_c16)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.real(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.imag(ImagObj())) # E: slice +reveal_type(np.imag(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.imag(AR_c16)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.imag(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.iscomplex(f8)) # E: numpy.bool_ +reveal_type(np.iscomplex(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.iscomplex(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] + +reveal_type(np.isreal(f8)) # E: numpy.bool_ +reveal_type(np.isreal(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] +reveal_type(np.isreal(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] + +reveal_type(np.iscomplexobj(f8)) # E: bool +reveal_type(np.isrealobj(f8)) # E: bool + +reveal_type(np.nan_to_num(f8)) # E: {float64} +reveal_type(np.nan_to_num(f, copy=True)) # E: Any +reveal_type(np.nan_to_num(AR_f8, nan=1.5)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.nan_to_num(AR_LIKE_f, posinf=9999)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.real_if_close(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.real_if_close(AR_c16)) # E: Union[numpy.ndarray[Any, numpy.dtype[{float64}]], numpy.ndarray[Any, numpy.dtype[{complex128}]]] +reveal_type(np.real_if_close(AR_c8)) # E: Union[numpy.ndarray[Any, numpy.dtype[{float32}]], numpy.ndarray[Any, numpy.dtype[{complex64}]]] +reveal_type(np.real_if_close(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[Any]] + +reveal_type(np.typename("h")) # E: Literal['short'] +reveal_type(np.typename("B")) # E: Literal['unsigned char'] +reveal_type(np.typename("V")) # E: Literal['void'] +reveal_type(np.typename("S1")) # E: Literal['character'] + +reveal_type(np.common_type(AR_i4)) # E: Type[{float64}] +reveal_type(np.common_type(AR_f2)) # E: Type[{float16}] +reveal_type(np.common_type(AR_f2, AR_i4)) # E: Type[{float64}] +reveal_type(np.common_type(AR_f16, AR_i4)) # E: Type[{float128}] +reveal_type(np.common_type(AR_c8, AR_f2)) # E: Type[{complex64}] +reveal_type(np.common_type(AR_f2, AR_c8, AR_i4)) # E: Type[{complex128}] diff --git a/numpy/typing/tests/test_generic_alias.py b/numpy/typing/tests/test_generic_alias.py index 0b9917439..538d7eae5 100644 --- a/numpy/typing/tests/test_generic_alias.py +++ b/numpy/typing/tests/test_generic_alias.py @@ -21,8 +21,8 @@ if sys.version_info >= (3, 9): NDArray_ref = types.GenericAlias(np.ndarray, (Any, DType_ref)) FuncType = Callable[[Union[_GenericAlias, types.GenericAlias]], Any] else: - DType_ref = NotImplemented - NDArray_ref = NotImplemented + DType_ref = Any + NDArray_ref = Any FuncType = Callable[[_GenericAlias], Any] GETATTR_NAMES = sorted(set(dir(np.ndarray)) - _GenericAlias._ATTR_EXCEPTIONS) @@ -41,6 +41,12 @@ class TestGenericAlias: @pytest.mark.parametrize("name,func", [ ("__init__", lambda n: n), + ("__init__", lambda n: _GenericAlias(np.ndarray, Any)), + ("__init__", lambda n: _GenericAlias(np.ndarray, (Any,))), + ("__init__", lambda n: _GenericAlias(np.ndarray, (Any, Any))), + ("__init__", lambda n: _GenericAlias(np.ndarray, T1)), + ("__init__", lambda n: _GenericAlias(np.ndarray, (T1,))), + ("__init__", lambda n: _GenericAlias(np.ndarray, (T1, T2))), ("__origin__", lambda n: n.__origin__), ("__args__", lambda n: n.__args__), ("__parameters__", lambda n: n.__parameters__), diff --git a/numpy/version.py b/numpy/version.py index 48bdb32da..4159a1c0e 100644 --- a/numpy/version.py +++ b/numpy/version.py @@ -7,5 +7,6 @@ version: str = vinfo["version"] full_version: str = vinfo['version'] git_revision: str = vinfo['full-revisionid'] release = 'dev0' not in version and '+' not in version +short_version: str = vinfo['version'].split("+")[0] del get_versions, vinfo |