python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.42k stars 2.82k forks source link

Incorrect type reification on optional union attribute in conditional #7884

Open elprans opened 4 years ago

elprans commented 4 years ago

Repro:

bug.py:


from __future__ import annotations

from typing import *

class B:
    pass

class A:
    name: Optional[Union[str, B]]

def foo(a: A):
    if isinstance(a.name, str):
        pass
    elif isinstance(a.name, B):
        pass
    else:
        raise AssertionError

    bar(a.name)

def bar(arg: Union[str, B]):
    pass

mypy incorrectly reports an error:

bug.py:22: error: Argument 1 to "bar" has incompatible type "object"; expected "Union[str, B]"

Interestingly, when a.name is aliased with another name, the issue disappers, i.e. the following is checked without an issue:

ok.py:

from __future__ import annotations

from typing import *

class B:
    pass

class A:
    name: Optional[Union[str, B]]

def foo(a: A):
    a_name = a.name

    if isinstance(a_name, str):
        pass
    elif isinstance(a_name, B):
        pass
    else:
        raise AssertionError

    bar(a_name)

def bar(arg: Union[str, B]):
    pass
ilevkivskyi commented 4 years ago

I confirm this. This honestly looks like a mystery, it looks like mypy applies a join instead of a union for attribute expressions, but understanding why would require some investigation.