Skip to content

mypy loses type of optional Tuple variable after setting default in runtime #5393

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

Closed
blazewicz opened this issue Jul 26, 2018 · 8 comments
Closed
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-union-types

Comments

@blazewicz
Copy link

Please provide more information to help us understand the issue:

  • Are you reporting a bug, or opening a feature request?

reporting a bug

  • Please insert below the code you are checking with mypy,
    or a mock-up repro if the source is private. We would appreciate
    if you try to simplify your case to a minimal repro.
from typing import Tuple, Optional


def foo(value: Optional[int]) -> None:
    """"Optional argument of type int with default value set in runtime."""
    if value is None:
        value = 42
    reveal_type(value)  # builtins.int
    value.bit_length()  # no error


def bar(value: Tuple[int, list]) -> None:
    """Argument of a tuple type."""
    reveal_type(value)     # Tuple[builtins.int, builtins.list[Any]]
    reveal_type(value[0])  # builtins.int
    reveal_type(value[1])  # builtins.list[Any]
    value[1].sort()  # no errors


def baz(value: Optional[Tuple[int, list]]) -> None:
    """mypy loses actual type of argument after setting default value in runtime."""
    if value is None:
        value = (0, [])  # no errors here, types match
    # unexpected Union type below, notice both elements are identical
    reveal_type(value)     # Union[Tuple[builtins.int, builtins.list[Any]], Tuple[builtins.int, builtins.list[Any]]]
    # now we have only garbage
    reveal_type(value[0])  # Union[builtins.object*, Any]
    reveal_type(value[1])  # Union[builtins.object*, Any]
    value[1].sort()  # error: Item "object" of "Union[object, Any]" has no attribute "sort"
  • What is the actual behavior/output?

mypy loses actual type of object

  • What is the behavior/output you expect?

no errors in code above

  • What are the versions of mypy and Python you are using?
    Do you see the same issue after installing mypy from Git master?

fresh mypy from git master

  • What are the mypy flags you are using? (For example --strict-optional)

none

@gvanrossum
Copy link
Member

gvanrossum commented Jul 26, 2018 via email

@blazewicz
Copy link
Author

changing list -> List[int] doesn't fix anything, I've found this problem with a custom type and a custom class as tuple elements, code above is simplified case

mypyerror.py:27: error: Revealed type is 'Union[Tuple[builtins.int, builtins.list[builtins.int]], Tuple[builtins.int, builtins.list[builtins.int]]]'
mypyerror.py:28: error: Revealed type is 'Union[builtins.object*, Any]'
mypyerror.py:29: error: Revealed type is 'Union[builtins.object*, Any]'
mypyerror.py:30: error: Item "object" of "Union[object, Any]" has no attribute "sort"

@gvanrossum gvanrossum added the bug mypy got something wrong label Jul 26, 2018
@gvanrossum
Copy link
Member

OK, looks like no union simplification is happening in this case. I'm declaring it a bug. Thanks for reporting!

@blazewicz
Copy link
Author

Problem with Union simplification seems to be caused by different fallback values of both tuples inside of Union created after if x is None part.

One element has builtins.tuple[builtins.int] and the other builtins.tuple[Any].

Following quick patch fixes this particular bug.

diff --git a/mypy/types.py b/mypy/types.py
index f9d5d3c2..371ba427 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1097,12 +1097,12 @@ class TupleType(Type):
         return visitor.visit_tuple_type(self)

     def __hash__(self) -> int:
-        return hash((tuple(self.items), self.fallback))
+        return hash((tuple(self.items)))

     def __eq__(self, other: object) -> bool:
         if not isinstance(other, TupleType):
             return NotImplemented
-        return self.items == other.items and self.fallback == other.fallback
+        return self.items == other.items

     def serialize(self) -> JsonDict:
         return {'.class': 'TupleType',

@gvanrossum
Copy link
Member

gvanrossum commented Jul 26, 2018 via email

@blazewicz
Copy link
Author

With this patch some tests fail, but I'll try to make it into a proper PR.

@ilevkivskyi
Copy link
Member

Following quick patch fixes this particular bug.

This hides the problem, fallback contains some actual info about tuples (e.g. for named tuples), so we should figure out why fallbacks are different instead of just ignoring them.

@ilevkivskyi
Copy link
Member

The issue is now fixed, likely by #6442

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-union-types
Projects
None yet
Development

No branches or pull requests

3 participants