Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
marcinsulikowski opened this issue Oct 4, 2019 · 1 comment · Fixed by #7632
Closed

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

marcinsulikowski opened this issue Oct 4, 2019 · 1 comment · Fixed by #7632
Assignees
Labels

Comments

@marcinsulikowski
Copy link

marcinsulikowski commented Oct 4, 2019

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?

@msullivan msullivan added needs discussion bug mypy got something wrong labels Oct 4, 2019
@msullivan msullivan self-assigned this Oct 4, 2019
@msullivan
Copy link
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.

msullivan added a commit that referenced this issue Oct 4, 2019
A recent typeshed change added a modulo argument to int.__pow__
which broke the plugin.

Tested manually. I didn't add a test because to be useful it would
need to be a cmdline test (so it uses the real typeshed) and this
doesn't seem worth adding a slow cmdline test for. Happy to add it if
someone disagrees though.

Fixes #7621.
msullivan added a commit that referenced this issue Oct 5, 2019
A recent typeshed change added a modulo argument to int.__pow__
which broke the plugin.

Tested manually. I didn't add a test because to be useful it would
need to be a cmdline test (so it uses the real typeshed) and this
doesn't seem worth adding a slow cmdline test for. Happy to add it if
someone disagrees though.

Fixes #7621.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants