-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Labels
bugmypy got something wrongmypy got something wrongtopic-ternary-expressiona if b else ca if b else ctopic-type-narrowingConditional type narrowing / binderConditional type narrowing / binder
Description
Consider the following test cases, which all work just fine:
from typing import TypeVar, Union, Callable, reveal_type
NOOP = lambda: None
class A: pass
def test_static_attr(x: Union[A, None]) -> None:
def foo(t: A) -> None: ...
l1: Callable[[], None] = (lambda: foo(x)) if x is not None else NOOP # ✅
r1: Callable[[], None] = NOOP if x is None else (lambda: foo(x)) # ✅
l2 = (lambda: foo(x)) if x is not None else NOOP # ✅
r2 = NOOP if x is None else (lambda: foo(x)) # ✅
reveal_type(l2) # N: Revealed type is "def ()" ✅
reveal_type(r2) # N: Revealed type is "def ()" ✅
def test_generic_attr(x: Union[A, None]) -> None:
T = TypeVar("T")
def bar(t: T) -> T: return t
l1: Callable[[], None] = (lambda: bar(x)) if x is None else NOOP # ✅
r1: Callable[[], None] = NOOP if x is not None else (lambda: bar(x)) # ✅
l2 = (lambda: bar(x)) if x is None else NOOP # ✅
r2 = NOOP if x is not None else (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:
from typing import TypeVar, Union, Callable, reveal_type
NOOP = lambda: None
class A: pass
class B:
attr: Union[A, None]
def test_static_with_attr(x: B) -> None:
def foo(t: A) -> None: ...
l1: Callable[[], None] = (lambda: foo(x.attr)) if x.attr is not None else NOOP # ❌
r1: Callable[[], None] = NOOP if x.attr is None else (lambda: foo(x.attr)) # ❌
l2 = (lambda: foo(x.attr)) if x.attr is not None else NOOP # ✅
r2 = NOOP if x.attr is None else (lambda: foo(x.attr)) # ❌
reveal_type(l2) # N: Revealed type is "def ()" ✅
reveal_type(r2) # N: Revealed type is "def ()" ✅
def test_generic_with_attr(x: B) -> None:
T = TypeVar("T")
def bar(t: T) -> T: return t
l1: Callable[[], None] = (lambda: bar(x.attr)) if x.attr is None else NOOP # ❌
r1: Callable[[], None] = NOOP if x.attr is not None else (lambda: bar(x.attr)) # ❌
l2 = (lambda: bar(x.attr)) if x.attr is None else NOOP # ✅
r2 = NOOP if x.attr is not None else (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
Labels
bugmypy got something wrongmypy got something wrongtopic-ternary-expressiona if b else ca if b else ctopic-type-narrowingConditional type narrowing / binderConditional type narrowing / binder