Skip to content

Irregular inference with ternary based on attribute. #19561

Description

@randolf-scholz

Consider the following test cases, which all work just fine:

fromtypingimportTypeVar, Union, Callable, reveal_typeNOOP=lambda: NoneclassA: passdeftest_static_attr(x: Union[A, None]) ->None:
deffoo(t: A) ->None: ...
l1: Callable[[], None] = (lambda: foo(x)) ifxisnotNoneelseNOOP# ✅r1: Callable[[], None] =NOOPifxisNoneelse (lambda: foo(x)) # ✅l2= (lambda: foo(x)) ifxisnotNoneelseNOOP# ✅r2=NOOPifxisNoneelse (lambda: foo(x)) # ✅reveal_type(l2) # N: Revealed type is "def ()" ✅reveal_type(r2) # N: Revealed type is "def ()" ✅deftest_generic_attr(x: Union[A, None]) ->None:
T=TypeVar("T")
defbar(t: T) ->T: returntl1: Callable[[], None] = (lambda: bar(x)) ifxisNoneelseNOOP# ✅r1: Callable[[], None] =NOOPifxisnotNoneelse (lambda: bar(x)) # ✅l2= (lambda: bar(x)) ifxisNoneelseNOOP# ✅r2=NOOPifxisnotNoneelse (lambda: bar(x)) # ✅reveal_type(l2) # N: Revealed type is "def ()" ✅reveal_type(r2) # N: Revealed type is "def ()" ✅

However, when we add a level of indirection by introducing a class B with B.attr: A | None, and basing the decision on this attribute, it sometimes works and sometimes doesn't:

fromtypingimportTypeVar, Union, Callable, reveal_typeNOOP=lambda: NoneclassA: passclassB:
attr: Union[A, None]
deftest_static_with_attr(x: B) ->None:
deffoo(t: A) ->None: ...
l1: Callable[[], None] = (lambda: foo(x.attr)) ifx.attrisnotNoneelseNOOP# ❌r1: Callable[[], None] =NOOPifx.attrisNoneelse (lambda: foo(x.attr)) # ❌l2= (lambda: foo(x.attr)) ifx.attrisnotNoneelseNOOP# ✅r2=NOOPifx.attrisNoneelse (lambda: foo(x.attr)) # ❌reveal_type(l2) # N: Revealed type is "def ()" ✅reveal_type(r2) # N: Revealed type is "def ()" ✅deftest_generic_with_attr(x: B) ->None:
T=TypeVar("T")
defbar(t: T) ->T: returntl1: Callable[[], None] = (lambda: bar(x.attr)) ifx.attrisNoneelseNOOP# ❌r1: Callable[[], None] =NOOPifx.attrisnotNoneelse (lambda: bar(x.attr)) # ❌l2= (lambda: bar(x.attr)) ifx.attrisNoneelseNOOP# ✅r2=NOOPifx.attrisnotNoneelse (lambda: bar(x.attr)) # ✅reveal_type(l2) # N: Revealed type is "def ()" ✅reveal_type(r2) # N: Revealed type is "def () -> __main__.A | None" ❌

https://mypy-play.net/?mypy=1.17.0&python=3.12&gist=106fbfcd39eee8f3a2c51d45b154b5fb

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions