Regression with type narrowing of bool
Regression in 0.920 / 0.930 compared with 0.910.
from typing import Any
def func() -> Any:
...
var = func()
reveal_type(var) # Any
assert isinstance(var, bool)
reveal_type(var) # bool
if var:
reveal_type(var) # Literal[True]
pass
reveal_type(var) # Any -> should still be 'bool'!
The type should not be lost if it can't be narrowed to Literal[True] or Literal[False]. It's a side effect of #10389. The issue was already mentioned https://github.com/python/mypy/pull/10389#issuecomment-835805544 but hasn't been addressed yet.
/CC: @ethan-leba
Ok, looks like I was able to debug this problem. It is not really related to https://github.com/python/mypy/pull/10389, but this PR just made this problem visible. But, theoretically you can come up with other examples. Like:
from typing import Any
var: Any
reveal_type(var) # N: Revealed type is "Any"
assert isinstance(var, bool) or isinstance(var, str)
reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]"
if isinstance(var, bool):
reveal_type(var) # N: Revealed type is "builtins.bool"
# Should be `Union[builtins.bool, builtins.str]`, but:
reveal_type(var) # N: Revealed type is "Any"
What is the cause of this problem? https://github.com/python/mypy/blob/f96446ce2f99a86210b21d39c7aec4b13a8bfc66/mypy/binder.py#L232-L237
- When we use
allow_jump, it ends up with twoframeobjects inself.options_on_return[index] - Each type in
self.options_on_return[index]is only a part of the original type - So, when we run this check:
if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]):it evaluates toTrueandAnyis returned
I guess we should write proper type joiner for frame object. Two frames with Var(x): Literal[True] and Var(x): Literal[False] should be joined as Var(x): bool. The same with union types.
I have a related issue. Not sure if the same cause, though. In this code:
https://github.com/life4/deal/blob/48c6b1dbc51781f95ebba19c618f875ca9199ad4/deal/_runtime/_contracts.py#L67-L74
contracts variable should be inferred as Contracts after contracts = cls(func) but now it is Optional[Any].
- Explicitly annotating
clsdoesn't help. - If I explicitly annotate
contracts: Optional[Contracts], it stays that way, no type refinement is done bycontracts = cls(func) - If I add an explicit
assert contracts is not None, it also doesn't help,contractsstaysOptional[Contracts].
In mypy 0.910 everything was fine. In pyright everything is also correctly inferred.