mirror of
https://github.com/astral-sh/ruff
synced 2026-01-24 06:50:59 -05:00
This makes it easier to read and edit (and review changes to) these files as source, even though it doesn't affect the rendering.
4.4 KiB
4.4 KiB
Narrowing for isinstance checks
Narrowing for isinstance(object, classinfo) expressions.
classinfo is a single type
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1 if flag else "a"
if isinstance(x, int):
reveal_type(x) # revealed: Literal[1]
if isinstance(x, str):
reveal_type(x) # revealed: Literal["a"]
if isinstance(x, int):
reveal_type(x) # revealed: Never
if isinstance(x, (int, object)):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
classinfo is a tuple of types
Note: isinstance(x, (int, str)) should not be confused with isinstance(x, tuple[(int, str)]).
The former is equivalent to isinstance(x, int | str):
def bool_instance() -> bool:
return True
flag, flag1, flag2 = bool_instance(), bool_instance(), bool_instance()
x = 1 if flag else "a"
if isinstance(x, (int, str)):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
else:
reveal_type(x) # revealed: Never
if isinstance(x, (int, bytes)):
reveal_type(x) # revealed: Literal[1]
if isinstance(x, (bytes, str)):
reveal_type(x) # revealed: Literal["a"]
# No narrowing should occur if a larger type is also
# one of the possibilities:
if isinstance(x, (int, object)):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
else:
reveal_type(x) # revealed: Never
y = 1 if flag1 else "a" if flag2 else b"b"
if isinstance(y, (int, str)):
reveal_type(y) # revealed: Literal[1] | Literal["a"]
if isinstance(y, (int, bytes)):
reveal_type(y) # revealed: Literal[1] | Literal[b"b"]
if isinstance(y, (str, bytes)):
reveal_type(y) # revealed: Literal["a"] | Literal[b"b"]
classinfo is a nested tuple of types
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1 if flag else "a"
if isinstance(x, (bool, (bytes, int))):
reveal_type(x) # revealed: Literal[1]
else:
reveal_type(x) # revealed: Literal["a"]
Class types
class A: ...
class B: ...
class C: ...
def get_object() -> object: ...
x = get_object()
if isinstance(x, A):
reveal_type(x) # revealed: A
if isinstance(x, B):
reveal_type(x) # revealed: A & B
else:
reveal_type(x) # revealed: A & ~B
if isinstance(x, (A, B)):
reveal_type(x) # revealed: A | B
elif isinstance(x, (A, C)):
reveal_type(x) # revealed: C & ~A & ~B
else:
# TODO: Should be simplified to ~A & ~B & ~C
reveal_type(x) # revealed: object & ~A & ~B & ~C
No narrowing for instances of builtins.type
def bool_instance() -> bool:
return True
flag = bool_instance()
t = type("t", (), {})
# This isn't testing what we want it to test if we infer anything more precise here:
reveal_type(t) # revealed: type
x = 1 if flag else "foo"
if isinstance(x, t):
reveal_type(x) # revealed: Literal[1] | Literal["foo"]
Do not use custom isinstance for narrowing
def bool_instance() -> bool:
return True
flag = bool_instance()
def isinstance(x, t):
return True
x = 1 if flag else "a"
if isinstance(x, int):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
Do support narrowing if isinstance is aliased
def bool_instance() -> bool:
return True
flag = bool_instance()
isinstance_alias = isinstance
x = 1 if flag else "a"
if isinstance_alias(x, int):
reveal_type(x) # revealed: Literal[1]
Do support narrowing if isinstance is imported
from builtins import isinstance as imported_isinstance
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1 if flag else "a"
if imported_isinstance(x, int):
reveal_type(x) # revealed: Literal[1]
Do not narrow if second argument is not a type
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1 if flag else "a"
# TODO: this should cause us to emit a diagnostic during
# type checking
if isinstance(x, "a"):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
# TODO: this should cause us to emit a diagnostic during
# type checking
if isinstance(x, "int"):
reveal_type(x) # revealed: Literal[1] | Literal["a"]
Do not narrow if there are keyword arguments
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1 if flag else "a"
# TODO: this should cause us to emit a diagnostic
# (`isinstance` has no `foo` parameter)
if isinstance(x, int, foo="bar"):
reveal_type(x) # revealed: Literal[1] | Literal["a"]