mypy icon indicating copy to clipboard operation
mypy copied to clipboard

False positive when converting union of tuples to union of typed dicts

Open ojii opened this issue 2 years ago • 7 comments

Bug Report

Mypy fails to preserve types when a union of tuples is transformed to a union of typed dicts. see linked mypy play link

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.11&gist=84a763d09cf7fb68631fa74359d135e7

Expected Behavior

I expect the linked example to pass the type checker

Actual Behavior

Incompatible return value type (got "dict[Literal['a', 'b'], int | str]", expected "A | B") [return-value]

Your Environment

  • Mypy version used: 1.4.0 (compiled: yes)
  • Mypy command-line flags: --strict
  • Mypy configuration options from mypy.ini (and other config files):
[tool.mypy]
strict = true
files = [
    "src/",
    "tests/",
]
  • Python version used: 3.10.1

ojii avatar Jun 26 '23 01:06 ojii

try to reproduce it in a minimal example that isolates the problematic behavior. This will make it easier for others to understand and potentially provide a solution.

myselfAbdullah007 avatar Jun 26 '23 18:06 myselfAbdullah007

try to reproduce it in a minimal example that isolates the problematic behavior. This will make it easier for others to understand and potentially provide a solution.

the linked gist/playground seems pretty minimal to me. it's a smaller example of real world code I'm trying to type stronger.

ojii avatar Jun 27 '23 00:06 ojii

This

{
  "a": "foo",
  "b": "bar",
}

matches dict[Literal['a', 'b'], int | str], but doesn't match A nor B.

ikonst avatar Jun 27 '23 01:06 ikonst

Yes, but my code will result in a dict with only one key and one value, and all the types involved make it clear that dict_func must return A or B.

ojii avatar Jun 27 '23 02:06 ojii

Ahh, I see what's going on!

Given

my_tuple: tuple[Literal['a'], int] | tuple[Literal['b'], str]
...
key, value = my_tuple

I wouldn't expect mypy to keep track of the relation between the two variables key and value after the assignment. That is, it can know "key type is Literal['a', 'b']" but it cannot remember/keep track of the fact that:

  • key type is literal['a'] iff value type is int
  • key type is literal['b'] iff value type is str

By unpacking into key, value, you're bound to lose some knowledge.

Now,

my_tuple: tuple[Literal['a'], int] | tuple[Literal['b'], str]
...
return {my_tuple[0]: my_tuple[1]}

this is more debatable, though still obviously harder than figuring out the type of my_tuple[0] independently from my_tuple[1].

ikonst avatar Jun 27 '23 21:06 ikonst

By unpacking into key, value, you're bound to lose some knowledge.

Thank you for spelling it out like that, I can now actually see why this might be an unreasonable ask to mypy/a type checker. I think the problem was that to me as a human, it looks 'obviously type checkable', I can understand why this would be much harder for a program to do.

Should this ticket be closed? (I'd still argue this is a 'bug', but calling this a 'wontfix' also seems reasonable)

ojii avatar Jun 28 '23 00:06 ojii

Probably? :) You're also welcome to point out any section of the docs that you think we could improve to clarify that.

ikonst avatar Jun 28 '23 02:06 ikonst

Closing as per https://github.com/python/mypy/issues/15517#issuecomment-1610229738 We could do a better job of documenting cases where mypy can't reason about relationships between variables, but there are other issues that track this

hauntsaninja avatar Aug 24 '23 08:08 hauntsaninja