-
Notifications
You must be signed in to change notification settings - Fork 276
Closed
Labels
Description
Describe the Bug
from collections.abc import Iterable, Mapping
from typing import reveal_type, assert_type
def test1(arg: Mapping[str, int] | Iterable[tuple[str, int]]) -> None:
if isinstance(arg, Mapping):
reveal_type(arg) # E: Mapping[str, int] | Mapping[tuple[str, int], ?]
else:
reveal_type(arg) # E: Iterable[tuple[str, int]]
def test2(arg: Mapping[str, int] | Iterable[tuple[str, int]]) -> None:
if not isinstance(arg, Mapping):
reveal_type(arg) # E: Iterable[tuple[str, int]]
else:
reveal_type(arg) # E: Mapping[str, int] | Mapping[tuple[str, int], ?]Actual output:
INFO sandbox.py:6:20-25: revealed type: Mapping[tuple[str, int], Unknown]
INFO sandbox.py:8:20-25: revealed type: Iterable[tuple[str, int]]
INFO sandbox.py:12:20-25: revealed type: Iterable[tuple[str, int]]
INFO sandbox.py:14:20-25: revealed type: Mapping[tuple[str, int], Unknown]
Explanation:
- In the first test, narrowing by
isinstance(arg, Mapping), gives us a union becauseIterable[tuple[str, int]] & Mapping = Mapping[tuple[str, int], ?]is not empty. In the else branch, we can excludeMappingso onlyIterable[tuple[str, int]]remains - In the second test, narrowing by
not isinstance(arg, Mapping), then in the if-branch the type should beIterable - Mapping, and in the else branch we get (Mapping | Iterable) - (Iterable - Mapping) which simplifies to(Mapping | (Mapping & Iterable))
Note: formally, both tests are equivalent
def test1(arg: T) -> None:
if isinstance(arg, X):
# T & X
else:
# T - (T & X) = T - X
def test2(arg: T) -> None:
if not isinstance(arg, X):
# T - (T&X) = T - X
else:
# T - (T - (T&X)) = T & XSandbox Link
(Only applicable for extension issues) IDE Information
No response
Reactions are currently unavailable