Skip to content

Commit

Permalink
More improvements to pow stubs (#7737)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood authored Apr 28, 2022
1 parent 5df8de7 commit c3ebc7e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 5 deletions.
19 changes: 17 additions & 2 deletions stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: ...
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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: ...
Expand Down
39 changes: 36 additions & 3 deletions test_cases/stdlib/builtins/test_pow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(<smallint>, <smallint>, <smallint>)
assert_type(pow(2, 35, 3), int) # pow(<smallint>, <bigint>, <smallint>)

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()`
Expand All @@ -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(<pos-float>, <pos-or-neg-float>) -> float
# pow(<neg-float>, <pos-or-neg-float>) -> complex
assert_type(pow(4.7, 7.4), Any)
Expand All @@ -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)

0 comments on commit c3ebc7e

Please sign in to comment.