From ceb8b6a4aeb94799a604b120669678e9f23a148d Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Thu, 28 Apr 2022 10:33:33 -0600 Subject: [PATCH] More improvements to `pow` stubs --- stdlib/builtins.pyi | 19 +++++++++++-- test_cases/stdlib/builtins/test_pow.py | 39 ++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index a5b4ba07163f..052e8421943d 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -254,7 +254,9 @@ class int: @overload def __pow__(self, __x: int, __modulo: int) -> int: ... @overload - def __pow__(self, __x: Literal[0], __modulo: None = ...) -> Literal[1]: ... + def __pow__(self, __x: Literal[0]) -> Literal[1]: ... + @overload + def __pow__(self, __x: Literal[0], __modulo: None) -> Literal[1]: ... @overload def __pow__(self, __x: _PositiveInteger, __modulo: None = ...) -> int: ... @overload @@ -327,7 +329,12 @@ class float: def __rtruediv__(self, __x: float) -> float: ... def __rmod__(self, __x: float) -> float: ... def __rdivmod__(self, __x: float) -> tuple[float, float]: ... - # Returns complex if the argument is negative. + @overload + def __rpow__(self, __x: _PositiveInteger, __modulo: None = ...) -> float: ... + @overload + def __rpow__(self, __x: _NegativeInteger, __mod: None = ...) -> complex: ... + # Returning `complex` for the general case gives too many false-positive errors. + @overload def __rpow__(self, __x: float, __mod: None = ...) -> Any: ... def __getnewargs__(self) -> tuple[float]: ... def __trunc__(self) -> int: ... @@ -1422,6 +1429,10 @@ if sys.version_info >= (3, 8): @overload def pow(base: int, exp: int, mod: None = ...) -> Any: ... @overload + def pow(base: _PositiveInteger, exp: float, mod: None = ...) -> float: ... + @overload + def pow(base: _NegativeInteger, exp: float, mod: None = ...) -> complex: ... + @overload def pow(base: float, exp: int, mod: None = ...) -> float: ... # float base & float exp could return float or complex # return type must be Any (same as complex base, complex exp), @@ -1455,6 +1466,10 @@ else: @overload def pow(__base: int, __exp: int, __mod: None = ...) -> Any: ... @overload + def pow(__base: _PositiveInteger, __exp: float, __mod: None = ...) -> float: ... + @overload + def pow(__base: _NegativeInteger, __exp: float, __mod: None = ...) -> complex: ... + @overload def pow(__base: float, __exp: int, __mod: None = ...) -> float: ... @overload def pow(__base: float, __exp: complex | _SupportsSomeKindOfPow, __mod: None = ...) -> Any: ... diff --git a/test_cases/stdlib/builtins/test_pow.py b/test_cases/stdlib/builtins/test_pow.py index c20ab76d70b7..093f05e18786 100644 --- a/test_cases/stdlib/builtins/test_pow.py +++ b/test_cases/stdlib/builtins/test_pow.py @@ -3,24 +3,55 @@ from typing import Any, NoReturn from typing_extensions import Literal, assert_type -assert_type(pow(1, 0), Literal[1]) # See #7163 -assert_type(pow(1, 0, None), Literal[1]) # See #7163 +# See #7163 +assert_type(pow(1, 0), Literal[1]) +assert_type(1**0, Literal[1]) +assert_type(pow(1, 0, None), Literal[1]) + assert_type(pow(2, 4, 0), NoReturn) + assert_type(pow(2, 4), int) +assert_type(2**4, int) assert_type(pow(4, 6, None), int) + assert_type(pow(5, -7), float) +assert_type(5**-7, float) + assert_type(pow(2, 4, 5), int) # pow(, , ) assert_type(pow(2, 35, 3), int) # pow(, , ) + +assert_type(pow(2, 8.5), float) +assert_type(2**8.6, float) +assert_type(pow(2, 8.6, None), float) + +# TODO: Why does this pass pyright but not mypy?? +# assert_type((-2) ** 0.5, complex) + +assert_type(pow((-5), 8.42, None), complex) + assert_type(pow(4.6, 8), float) +assert_type(4.6**8, float) assert_type(pow(5.1, 4, None), float) + assert_type(pow(complex(6), 6.2), complex) +assert_type(complex(6) ** 6.2, complex) assert_type(pow(complex(9), 7.3, None), complex) + assert_type(pow(Fraction(), 4, None), Fraction) +assert_type(Fraction() ** 4, Fraction) + assert_type(pow(Fraction(3, 7), complex(1, 8)), complex) +assert_type(Fraction(3, 7) ** complex(1, 8), complex) + assert_type(pow(complex(4, -8), Fraction(2, 3)), complex) +assert_type(complex(4, -8) ** Fraction(2, 3), complex) + assert_type(pow(Decimal("1.0"), Decimal("1.6")), Decimal) +assert_type(Decimal("1.0") ** Decimal("1.6"), Decimal) + assert_type(pow(Decimal("1.0"), Decimal("1.0"), Decimal("1.0")), Decimal) assert_type(pow(Decimal("4.6"), 7, None), Decimal) +assert_type(Decimal("4.6") ** 7, Decimal) # These would ideally be more precise, but `Any` is acceptable # They have to be `Any` due to the fact that type-checkers can't distinguish between positive and negative numbers for the second argument to `pow()` @@ -29,6 +60,8 @@ assert_type(pow(4, 65), Any) assert_type(pow(2, -45), Any) assert_type(pow(3, 57, None), Any) +assert_type(pow(67, 0.98, None), Any) +assert_type(87**7.32, Any) # pow(, ) -> float # pow(, ) -> complex assert_type(pow(4.7, 7.4), Any) @@ -37,4 +70,4 @@ assert_type(pow(8.2, -9.8), Any) assert_type(pow(4.7, 9.2, None), Any) # See #7046 -- float for a positive 1st arg, complex otherwise -assert_type((-2) ** 0.5, Any) +assert_type((-95) ** 8.42, Any)