Skip to content

Commit 20d051a

Browse files
authored
Merge pull request numpy#27681 from jorenham/typing/non-existant-scalar-methods
TYP: Fix some inconsistencies in the scalar methods and properties
2 parents 7c0e2e4 + 38814d9 commit 20d051a

File tree

2 files changed

+63
-84
lines changed

2 files changed

+63
-84
lines changed

numpy/__init__.pyi

Lines changed: 63 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ from typing import (
210210
# library include `typing_extensions` stubs:
211211
# https://github.com/python/typeshed/blob/main/stdlib/typing_extensions.pyi
212212
from _typeshed import StrOrBytesPath, SupportsFlush, SupportsLenAndGetItem, SupportsWrite
213-
from typing_extensions import CapsuleType, Generic, LiteralString, Protocol, Self, TypeVar, overload
213+
from typing_extensions import CapsuleType, Generic, LiteralString, Protocol, Self, TypeVar, deprecated, overload
214214

215215
from numpy import (
216216
core,
@@ -1377,6 +1377,10 @@ _SortSide: TypeAlias = L["left", "right"]
13771377

13781378
@type_check_only
13791379
class _ArrayOrScalarCommon:
1380+
@property
1381+
def real(self, /) -> Any: ...
1382+
@property
1383+
def imag(self, /) -> Any: ...
13801384
@property
13811385
def T(self) -> Self: ...
13821386
@property
@@ -1391,17 +1395,18 @@ class _ArrayOrScalarCommon:
13911395
def nbytes(self) -> int: ...
13921396
@property
13931397
def device(self) -> L["cpu"]: ...
1394-
def __bool__(self) -> builtins.bool: ...
1395-
def __bytes__(self) -> bytes: ...
1396-
def __str__(self) -> str: ...
1397-
def __repr__(self) -> str: ...
1398+
1399+
def __bool__(self, /) -> builtins.bool: ...
1400+
def __int__(self, /) -> int: ...
1401+
def __float__(self, /) -> float: ...
13981402
def __copy__(self) -> Self: ...
13991403
def __deepcopy__(self, memo: None | dict[int, Any], /) -> Self: ...
14001404

14011405
# TODO: How to deal with the non-commutative nature of `==` and `!=`?
14021406
# xref numpy/numpy#17368
14031407
def __eq__(self, other: Any, /) -> Any: ...
14041408
def __ne__(self, other: Any, /) -> Any: ...
1409+
14051410
def copy(self, order: _OrderKACF = ...) -> Self: ...
14061411
def dump(self, file: StrOrBytesPath | SupportsWrite[bytes]) -> None: ...
14071412
def dumps(self) -> bytes: ...
@@ -1418,7 +1423,7 @@ class _ArrayOrScalarCommon:
14181423
@property
14191424
def __array_priority__(self) -> float: ...
14201425
@property
1421-
def __array_struct__(self) -> Any: ... # builtins.PyCapsule
1426+
def __array_struct__(self) -> CapsuleType: ... # builtins.PyCapsule
14221427
def __array_namespace__(self, /, *, api_version: _ArrayAPIVersion | None = None) -> ModuleType: ...
14231428
def __setstate__(self, state: tuple[
14241429
SupportsIndex, # version
@@ -2230,8 +2235,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType_co, _DType_co]):
22302235
) -> NDArray[Any]: ...
22312236

22322237
def __index__(self: NDArray[np.integer[Any]], /) -> int: ...
2233-
def __int__(self: NDArray[number[Any] | np.bool | object_], /) -> int: ...
2234-
def __float__(self: NDArray[number[Any] | np.bool | object_], /) -> float: ...
2238+
def __int__(self: NDArray[number[Any] | np.timedelta64 | np.bool | object_], /) -> int: ...
2239+
def __float__(self: NDArray[number[Any] | np.timedelta64 | np.bool | object_], /) -> float: ...
22352240
def __complex__(self: NDArray[number[Any] | np.bool | object_], /) -> complex: ...
22362241

22372242
def __len__(self) -> int: ...
@@ -3254,14 +3259,7 @@ class generic(_ArrayOrScalarCommon):
32543259
def dtype(self) -> _dtype[Self]: ...
32553260

32563261
class number(generic, Generic[_NBit1]): # type: ignore
3257-
@property
3258-
def real(self) -> Self: ...
3259-
@property
3260-
def imag(self) -> Self: ...
32613262
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
3262-
def __int__(self) -> int: ...
3263-
def __float__(self) -> float: ...
3264-
def __complex__(self) -> complex: ...
32653263
def __neg__(self) -> Self: ...
32663264
def __pos__(self) -> Self: ...
32673265
def __abs__(self) -> Self: ...
@@ -3283,19 +3281,19 @@ class number(generic, Generic[_NBit1]): # type: ignore
32833281
__gt__: _ComparisonOpGT[_NumberLike_co, _ArrayLikeNumber_co]
32843282
__ge__: _ComparisonOpGE[_NumberLike_co, _ArrayLikeNumber_co]
32853283

3286-
class bool(generic):
3287-
def __init__(self, value: object = ..., /) -> None: ...
3288-
def item(
3289-
self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /,
3290-
) -> builtins.bool: ...
3291-
def tolist(self) -> builtins.bool: ...
3284+
@type_check_only
3285+
class _RealMixin:
32923286
@property
32933287
def real(self) -> Self: ...
32943288
@property
32953289
def imag(self) -> Self: ...
3296-
def __int__(self) -> int: ...
3297-
def __float__(self) -> float: ...
3298-
def __complex__(self) -> complex: ...
3290+
3291+
class bool(_RealMixin, generic):
3292+
def __init__(self, value: object = ..., /) -> None: ...
3293+
def item(self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /) -> builtins.bool: ...
3294+
def tolist(self) -> builtins.bool: ...
3295+
@deprecated("In future, it will be an error for 'np.bool' scalars to be interpreted as an index")
3296+
def __index__(self, /) -> int: ...
32993297
def __abs__(self) -> Self: ...
33003298
__add__: _BoolOp[np.bool]
33013299
__radd__: _BoolOp[np.bool]
@@ -3332,13 +3330,12 @@ class bool(generic):
33323330
bool_: TypeAlias = bool
33333331

33343332
_StringType = TypeVar("_StringType", bound=str | bytes)
3335-
_ShapeType = TypeVar("_ShapeType", bound=_Shape)
33363333
_ObjectType = TypeVar("_ObjectType", bound=object)
33373334

33383335
# The `object_` constructor returns the passed object, so instances with type
33393336
# `object_` cannot exists (at runtime).
33403337
@final
3341-
class object_(generic):
3338+
class object_(_RealMixin, generic):
33423339
@overload
33433340
def __new__(cls, nothing_to_see_here: None = ..., /) -> None: ...
33443341
@overload
@@ -3353,16 +3350,6 @@ class object_(generic):
33533350
@overload
33543351
def __new__(cls, value: Any = ..., /) -> object | NDArray[object_]: ...
33553352

3356-
@property
3357-
def real(self) -> Self: ...
3358-
@property
3359-
def imag(self) -> Self: ...
3360-
# The 3 protocols below may or may not raise,
3361-
# depending on the underlying object
3362-
def __int__(self) -> int: ...
3363-
def __float__(self) -> float: ...
3364-
def __complex__(self) -> complex: ...
3365-
33663353
if sys.version_info >= (3, 12):
33673354
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
33683355

@@ -3379,7 +3366,7 @@ class _DatetimeScalar(Protocol):
33793366

33803367
# TODO: `item`/`tolist` returns either `dt.date`, `dt.datetime` or `int`
33813368
# depending on the unit
3382-
class datetime64(generic):
3369+
class datetime64(_RealMixin, generic):
33833370
@overload
33843371
def __init__(
33853372
self,
@@ -3417,25 +3404,29 @@ _ComplexValue: TypeAlias = (
34173404
| complex # `complex` is not a subtype of `SupportsComplex`
34183405
)
34193406

3420-
class integer(number[_NBit1]): # type: ignore
3407+
@type_check_only
3408+
class _RoundMixin:
3409+
@overload
3410+
def __round__(self, /, ndigits: None = None) -> int: ...
3411+
@overload
3412+
def __round__(self, /, ndigits: SupportsIndex) -> Self: ...
3413+
3414+
@type_check_only
3415+
class _IntegralMixin(_RealMixin):
34213416
@property
34223417
def numerator(self) -> Self: ...
34233418
@property
34243419
def denominator(self) -> L[1]: ...
3425-
@overload
3426-
def __round__(self, ndigits: None = ..., /) -> int: ...
3427-
@overload
3428-
def __round__(self, ndigits: SupportsIndex, /) -> Self: ...
34293420

3430-
# NOTE: `__index__` is technically defined in the bottom-most
3431-
# sub-classes (`int64`, `uint32`, etc)
3432-
def item(
3433-
self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /,
3434-
) -> int: ...
3421+
class integer(_IntegralMixin, _RoundMixin, number[_NBit1]): # type: ignore
3422+
def is_integer(self, /) -> L[True]: ...
3423+
def item(self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /) -> int: ...
34353424
def tolist(self) -> int: ...
3436-
def is_integer(self) -> L[True]: ...
3437-
def bit_count(self) -> int: ...
3438-
def __index__(self) -> int: ...
3425+
3426+
# NOTE: `bit_count` and `__index__` are technically defined in the concrete subtypes
3427+
def bit_count(self, /) -> int: ...
3428+
def __index__(self, /) -> int: ...
3429+
34393430
__truediv__: _IntTrueDiv[_NBit1]
34403431
__rtruediv__: _IntTrueDiv[_NBit1]
34413432
def __mod__(self, value: _IntLike_co, /) -> integer[Any]: ...
@@ -3495,23 +3486,16 @@ longlong = signedinteger[_NBitLongLong]
34953486

34963487
# TODO: `item`/`tolist` returns either `dt.timedelta` or `int`
34973488
# depending on the unit
3498-
class timedelta64(generic):
3489+
class timedelta64(_IntegralMixin, generic):
34993490
def __init__(
35003491
self,
35013492
value: None | int | _CharLike_co | dt.timedelta | timedelta64 = ...,
35023493
format: _CharLike_co | tuple[_CharLike_co, _IntLike_co] = ...,
35033494
/,
35043495
) -> None: ...
3505-
@property
3506-
def numerator(self) -> Self: ...
3507-
@property
3508-
def denominator(self) -> L[1]: ...
35093496

35103497
# NOTE: Only a limited number of units support conversion
35113498
# to builtin scalar types: `Y`, `M`, `ns`, `ps`, `fs`, `as`
3512-
def __int__(self) -> int: ...
3513-
def __float__(self) -> float: ...
3514-
def __complex__(self) -> complex: ...
35153499
def __neg__(self) -> Self: ...
35163500
def __pos__(self) -> Self: ...
35173501
def __abs__(self) -> Self: ...
@@ -3577,18 +3561,15 @@ ulonglong: TypeAlias = unsignedinteger[_NBitLongLong]
35773561

35783562
class inexact(number[_NBit1]): ... # type: ignore[misc]
35793563

3580-
_IntType = TypeVar("_IntType", bound=integer[Any])
3581-
3582-
class floating(inexact[_NBit1]):
3564+
class floating(_RealMixin, _RoundMixin, inexact[_NBit1]):
35833565
def __init__(self, value: _FloatValue = ..., /) -> None: ...
35843566
def item(self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /) -> float: ...
35853567
def tolist(self) -> float: ...
3586-
def is_integer(self) -> builtins.bool: ...
3587-
def as_integer_ratio(self) -> tuple[int, int]: ...
3588-
@overload
3589-
def __round__(self, ndigits: None = ..., /) -> int: ...
3590-
@overload
3591-
def __round__(self, ndigits: SupportsIndex, /) -> Self: ...
3568+
3569+
# NOTE: `is_integer` and `as_integer_ratio` are technically defined in the concrete subtypes
3570+
def is_integer(self, /) -> builtins.bool: ...
3571+
def as_integer_ratio(self, /) -> tuple[int, int]: ...
3572+
35923573
__add__: _FloatOp[_NBit1]
35933574
__radd__: _FloatOp[_NBit1]
35943575
__sub__: _FloatOp[_NBit1]
@@ -3617,7 +3598,7 @@ class float64(floating[_64Bit], float): # type: ignore[misc]
36173598
def __getformat__(self, typestr: L["double", "float"], /) -> str: ...
36183599
def __getnewargs__(self, /) -> tuple[float]: ...
36193600

3620-
# overrides for `floating` and `builtins.float` compatibility
3601+
# overrides for `floating` and `builtins.float` compatibility (`_RealMixin` doesn't work)
36213602
@property
36223603
def real(self) -> Self: ...
36233604
@property
@@ -3754,9 +3735,16 @@ class complexfloating(inexact[_NBit1], Generic[_NBit1, _NBit2]):
37543735
def real(self) -> floating[_NBit1]: ... # type: ignore[override]
37553736
@property
37563737
def imag(self) -> floating[_NBit2]: ... # type: ignore[override]
3757-
def __abs__(self) -> floating[_NBit1 | _NBit2]: ... # type: ignore[override]
3758-
# NOTE: Deprecated
3759-
# def __round__(self, ndigits=...): ...
3738+
3739+
# NOTE: `__complex__` is technically defined in the concrete subtypes
3740+
def __complex__(self, /) -> complex: ...
3741+
def __abs__(self, /) -> floating[_NBit1 | _NBit2]: ... # type: ignore[override]
3742+
@deprecated(
3743+
"The Python built-in `round` is deprecated for complex scalars, and will raise a `TypeError` in a future release. "
3744+
"Use `np.round` or `scalar.round` instead."
3745+
)
3746+
def __round__(self, /, ndigits: SupportsIndex | None = None) -> Self: ...
3747+
37603748
@overload
37613749
def __add__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ...
37623750
@overload
@@ -3871,7 +3859,7 @@ csingle: TypeAlias = complexfloating[_NBitSingle, _NBitSingle]
38713859
cdouble: TypeAlias = complexfloating[_NBitDouble, _NBitDouble]
38723860
clongdouble: TypeAlias = complexfloating[_NBitLongDouble, _NBitLongDouble]
38733861

3874-
class flexible(generic): ... # type: ignore
3862+
class flexible(_RealMixin, generic): ... # type: ignore
38753863

38763864
# TODO: `item`/`tolist` returns either `bytes` or `tuple`
38773865
# depending on whether or not it's used as an opaque bytes sequence
@@ -3881,13 +3869,7 @@ class void(flexible):
38813869
def __init__(self, value: _IntLike_co | bytes, /, dtype : None = ...) -> None: ...
38823870
@overload
38833871
def __init__(self, value: Any, /, dtype: _DTypeLikeVoid) -> None: ...
3884-
@property
3885-
def real(self) -> Self: ...
3886-
@property
3887-
def imag(self) -> Self: ...
3888-
def setfield(
3889-
self, val: ArrayLike, dtype: DTypeLike, offset: int = ...
3890-
) -> None: ...
3872+
def setfield(self, val: ArrayLike, dtype: DTypeLike, offset: int = ...) -> None: ...
38913873
@overload
38923874
def __getitem__(self, key: str | SupportsIndex, /) -> Any: ...
38933875
@overload
@@ -3899,9 +3881,7 @@ class void(flexible):
38993881
/,
39003882
) -> None: ...
39013883

3902-
class character(flexible): # type: ignore
3903-
def __int__(self) -> int: ...
3904-
def __float__(self) -> float: ...
3884+
class character(flexible): ... # type: ignore
39053885

39063886
# NOTE: Most `np.bytes_` / `np.str_` methods return their
39073887
# builtin `bytes` / `str` counterpart
@@ -3913,6 +3893,7 @@ class bytes_(character, bytes):
39133893
def __init__(
39143894
self, value: str, /, encoding: str = ..., errors: str = ...
39153895
) -> None: ...
3896+
def __bytes__(self, /) -> bytes: ...
39163897
def item(
39173898
self, args: L[0] | tuple[()] | tuple[L[0]] = ..., /,
39183899
) -> bytes: ...

numpy/typing/tests/data/fail/scalars.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ def func(a: np.float32) -> None: ...
8282
func(f2) # E: incompatible type
8383
func(f8) # E: incompatible type
8484

85-
round(c8) # E: No overload variant
86-
8785
c8.__getnewargs__() # E: Invalid self argument
8886
f2.__getnewargs__() # E: Invalid self argument
8987
f2.hex() # E: Invalid self argument

0 commit comments

Comments
 (0)