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

False-positive when using Union[Tuple[...]] #9791

Open
bansan85 opened this issue Dec 10, 2020 · 9 comments
Open

False-positive when using Union[Tuple[...]] #9791

bansan85 opened this issue Dec 10, 2020 · 9 comments
Labels
bug mypy got something wrong

Comments

@bansan85
Copy link

bansan85 commented Dec 10, 2020

Bug Report / To Reproduce

I have a incorrect Unsupported operand types for + ("int" and "str") when using Union[Tuple[...]].

To Reproduce

Have a look at this example:

from typing import Union, Tuple, List

variable: List[Union[Tuple[int, int], Tuple[str, str]]] = []

element1 = variable[0]

print(element1[0] + element1[1])

Run mypy.

Expected Behavior

No warning.

Actual Behavior

I have:

Unsupported operand types for + ("int" and "str")

But it's wrong due to the fact that element1 is a Union (right) but it's the same variable on both side. So both type on the left and the right of the "+" should be the same.

Your Environment

  • Mypy version used: mypy 0.790
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files):
[mypy]
python_version = 3.8
ignore_missing_imports = True
follow_imports = silent
show_column_numbers = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_unreachable = True
strict_equality = True
  • Python version used: Python 3.7.3
  • Operating system and version: Windows 10 20H2 (19042.630)
@bansan85 bansan85 added the bug mypy got something wrong label Dec 10, 2020
@nmay231
Copy link

nmay231 commented Dec 30, 2020

You can go even simpler than that.

from typing import Union

x: Union[str, int] = 1
print(x + x)

@hauntsaninja
Copy link
Collaborator

Your simplification actually demonstrates a different issue: #2008, upon which we recently came to a consensus to change the behaviour.

@nmay231
Copy link

nmay231 commented Dec 30, 2020

I guess the issues are different. Thanks for linking to the other issue 👍

I posted that example since there appears to be a typo in the original description that makes it seem the same. The code should probably be the following if it is referring to the issue that mypy doesn't remember "linking" between types:

- print(element1[0] + element1[0])
+ print(element1[0] + element1[1])

@bansan85
Copy link
Author

bansan85 commented Dec 30, 2020

@nmay231 You're right about the simplification and the typo. I edit my original post to fix typo even the problem still exists with the typo.

@Luttik
Copy link

Luttik commented Sep 2, 2021

I ran into this one which seems slightly different from #2008, and rather a more clearly bad behavior than the original post in this thread. Because List[int] is a subgroup of List[Union[str, int]].

x: List[Union[str, int]] = [1]
y: List[int] = [1]

z = x + y

which also results in:

Unsupported operand
 types for + ("List[Union[str, int]]" and "List[int]")

@hauntsaninja
Copy link
Collaborator

That one is intentional. List[int] is not a subtype of List[Union[str, int]], see https://mypy.readthedocs.io/en/stable/common_issues.html#invariance-vs-covariance

@solsword
Copy link

solsword commented Oct 11, 2022

Chiming in because I have a slightly different example which might be useful as an additional test:

from typing import Union, List, Tuple

val1: Union[List[str], Tuple[str, ...]] = ['hi', 'bye']
val2: Union[List[str], Tuple[str, ...]] = ('hi', 'bye')

new1 = val1[:1] + val1[1:]
new2 = val2[:1] + val2[1:]

print(new1)
print(new2)

The actual code I was using was using if isinstance(thing, (list, tuple)): ... and then expected that I'd be able to add two slices of the thing together, but MyPy warned about the possible incompatible operation just like it does with the more explicitly-typed code above.

I'm not sure if this is #2008 or not, but the discussion there seems wide-ranging that they mention this bug there so I thought I'd add my comment here.

I thought I'd also mention my current (non-ideal) workaround:

I just cast to Tuple[str, ...], which might prevent some type-checking for the list possibility but for my code doesn't give any errors. The result is:

from typing import Union, List, Tuple, cast

val1: Union[List[str], Tuple[str, ...]] = ['hi', 'bye']
val2: Union[List[str], Tuple[str, ...]] = ('hi', 'bye')

val1 = cast(Tuple[str, ...], val1)
val2 = cast(Tuple[str, ...], val2)

new1 = val1[:1] + val1[1:]
new2 = val2[:1] + val2[1:]

print(new1)
print(new2)

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Oct 11, 2022

It's #2008 in that mypy would narrow the type of val1 if we had #2008 semantics, e.g. the following type checks:

from typing import Union, List, Tuple

val1: Union[List[str], Tuple[str, ...]]
val2: Union[List[str], Tuple[str, ...]]
val1 = ['hi', 'bye']
val2 = ('hi', 'bye')

new1 = val1[:1] + val1[1:]
new2 = val2[:1] + val2[1:]

print(new1)
print(new2)

But there is still an underlying issue which is that the code is sound even if the type is not narrowed. But mypy isn't going to fix that any time soon because it would involve tracking the provenance of the types of expressions.

@solsword
Copy link

Thanks for the comment! I figured given the volume of discussion over on #2008 that this is going to happen when it happens, and I'm comfortable enough with my workaround to leave it in place for now.

I considered simply creating duplicate conditional branches that separately isinstance for list vs. tuple, but that would be actively harmful in terms of Don't Repeat Yourself and I figured that a bit of a type checking hack is better than allowing the type checker to convince me to write objectively worse code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

5 participants