-
- Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Bug Report
When taking the and of multiple isinstances/typeguards, it seems like only the most recent is used to constrain the variable whose type is being checked.
To Reproduce
See Gist: https://gist.github.com/a4cc96c4bb9207a3ae6a344016d2e46d
from typing import Any, TypeGuard, reveal_type def int_or_str(x: Any) -> TypeGuard[int | str]: return isinstance(x, int | str) def int_or_list(x: Any) -> TypeGuard[int | list]: return isinstance(x, int | list) def working_in_pyright(x: Any) -> TypeGuard[int]: if isinstance(x, int | str) and isinstance(x, int | list): reveal_type(x) # pyright: int, mypy: int | list return True return False def failing_in_both(x: Any) -> TypeGuard[int]: if int_or_str(x) and int_or_list(x): reveal_type(x) # int | list return True return False # incidentally, changing `and` to `or` results in the correct output `int | str | list`Expected Behavior
I think the code reveal_type(x) should return int in each of these examples.
I'm not sure about the failing_in_both example: I vaguely understand that user-defined type guards do not constrain the negative case of an if (but I haven't yet understood the rationale) -- does similar reasoning explain why the type guards on the LHS and RHS of the and do not combine?
I mean, technically, I could write a TypeGuard that modifies the argument, and so I guess the LHS of the and could cease to hold when the RHS is called; is this why? Would @erictraut's StrictTypeGuard python/typing#1013 be able to resolve this?
Actual Behavior
main.py:14: note: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" main.py:21: note: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" Success: no issues found in 1 source file Your Environment
- Mypy version used: 0.991
- Python version used: 3.11