@carljm here’s an example where both mypy and pyright break the gradual guarantee, but would error consistently if they followed what was here.
I’m using TypeIs to get away from both of the typecheckers special casing isinstance in a way incompatible with the current materialization rules for gradual types:
Code sample in pyright playground
from typing import Any, TypeIs
def is_str(s: Any, /) -> TypeIs[str]:
return isinstance(s, str)
def breaks_guarantee(s: str, /) -> str:
if not is_str(s):
msg = f"Expected a str, got an instance of {type(s):!r}" # no error
return TypeError(msg) # no error
else:
return s
def breaking_guarantee_now(s: Any, /) -> str:
if not is_str(s):
msg = f"Expected a str, got an instance of {type(s):!r}" # no error
return TypeError(msg) # error
else:
return s
What’s proposed here is also consistent with the proposed handling of Never in Intersections and how we are able to distinguish attribute presence from attribute type.