Skip to content

mypy 0.730 no longer recognizes x**y literals to be ints #7621

Closed

Description

@marcinsulikowski

I noticed a regression when switching from 0.720 to 0.730. Here's the code:

def f() -> int:
    size = 2 ** 20
    reveal_type(size)
    return size

Expected behavior

With mypy 0.720 (no matter whether the new or the old semantic analyzer is used), I get:

$ mypy --strict f.py
f.py:3: note: Revealed type is 'builtins.int'

Actual behavior

With mypy 0.730 (Python 3.6.8), I get:

$ mypy --strict f.py
f.py:3: note: Revealed type is 'Any'
f.py:4: error: Returning Any from function declared to return "int"

Notes

The behavior from 0.720 is what I'd expect.

One thing I noticed is that the typeshed indeed declares that __pow__ for two ints returns Any. This makes sense because 2 ** -2 is a float. The only recent change which I can see in the typeshed though is the following:

--- a/stdlib/2and3/builtins.pyi
+++ b/stdlib/2and3/builtins.pyi
@@ -168,7 +168,7 @@ class int:
     def __rtruediv__(self, x: int) -> float: ...
     def __rmod__(self, x: int) -> int: ...
     def __rdivmod__(self, x: int) -> Tuple[int, int]: ...
-    def __pow__(self, x: int) -> Any: ...  # Return type can be int or float, depending on x.
+    def __pow__(self, __x: int, __modulo: Optional[int] = ...) -> Any: ...  # Return type can be int or float, depending on x.
     def __rpow__(self, x: int) -> Any: ...
     def __and__(self, n: int) -> int: ...

(see: python/typeshed@b2cd972)

This means that the Any return type has always been there but mypy used to correctly recognize that 2 ** 20 is an int. It's difficult for me to tell whether the behavior in 0.730 is a regression compared to 0.720 or actually the desired behavior – it seems to me that mypy follows the typeshed more precisely here. However, maybe it's worth to handle literals like 2 ** 30 in a way that mypy knows that this is always int?

Activity

  1. self-assigned this
    on Oct 4, 2019
  2. msullivan commented on Oct 4, 2019

    @msullivan
    Collaborator

    Investigating some, the issue is that mypy actually does already have a plugin to handle exponentiation by a constant as a special case, but adding a new argument to __pow__ in typeshed broke it. Should be an easy fix.

  3. added a commit that references this issue on Oct 4, 2019
    44718b8
  4. added a commit that references this issue on Oct 5, 2019
    61e08cc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions