mirror of https://github.com/astral-sh/ruff
[ty] Infer precise types for `isinstance(…)` calls involving typevars
This commit is contained in:
parent
5372bb3440
commit
f551224faf
|
|
@ -114,6 +114,7 @@ but fall back to `bool` otherwise.
|
|||
```py
|
||||
from enum import Enum
|
||||
from types import FunctionType
|
||||
from typing import TypeVar
|
||||
|
||||
class Answer(Enum):
|
||||
NO = 0
|
||||
|
|
@ -137,6 +138,7 @@ reveal_type(isinstance("", int)) # revealed: bool
|
|||
|
||||
class A: ...
|
||||
class SubclassOfA(A): ...
|
||||
class OtherSubclassOfA(A): ...
|
||||
class B: ...
|
||||
|
||||
reveal_type(isinstance(A, type)) # revealed: Literal[True]
|
||||
|
|
@ -161,6 +163,29 @@ def _(x: A | B, y: list[int]):
|
|||
else:
|
||||
reveal_type(x) # revealed: B & ~A
|
||||
reveal_type(isinstance(x, B)) # revealed: Literal[True]
|
||||
|
||||
T = TypeVar("T")
|
||||
T_bound_A = TypeVar("T_bound_A", bound=A)
|
||||
T_constrained = TypeVar("T_constrained", SubclassOfA, OtherSubclassOfA)
|
||||
|
||||
def _(
|
||||
x: T,
|
||||
x_bound_a: T_bound_A,
|
||||
x_constrained_sub_a: T_constrained,
|
||||
):
|
||||
reveal_type(isinstance(x, object)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x, A)) # revealed: bool
|
||||
|
||||
reveal_type(isinstance(x_bound_a, object)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x_bound_a, A)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x_bound_a, SubclassOfA)) # revealed: bool
|
||||
reveal_type(isinstance(x_bound_a, B)) # revealed: bool
|
||||
|
||||
reveal_type(isinstance(x_constrained_sub_a, object)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x_constrained_sub_a, A)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x_constrained_sub_a, SubclassOfA)) # revealed: bool
|
||||
reveal_type(isinstance(x_constrained_sub_a, OtherSubclassOfA)) # revealed: bool
|
||||
reveal_type(isinstance(x_constrained_sub_a, B)) # revealed: bool
|
||||
```
|
||||
|
||||
Certain special forms in the typing module are not instances of `type`, so are strictly-speaking
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ use crate::types::{
|
|||
ClassBase, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
||||
NormalizedVisitor, SpecialFormType, SubclassOfInner, SubclassOfType, Truthiness, Type,
|
||||
TypeContext, TypeMapping, TypeRelation, UnionBuilder, binding_type, definition_expression_type,
|
||||
infer_definition_types, walk_signature,
|
||||
TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, UnionBuilder, binding_type,
|
||||
definition_expression_type, infer_definition_types, walk_signature,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
|
@ -1268,6 +1268,19 @@ fn is_instance_truthiness<'db>(
|
|||
|
||||
Type::TypeAlias(alias) => is_instance_truthiness(db, alias.value_type(db), class),
|
||||
|
||||
Type::TypeVar(bound_typevar) => match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => is_instance_truthiness(db, Type::object(), class),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
is_instance_truthiness(db, bound, class)
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => always_true_if(
|
||||
constraints
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|c| is_instance_truthiness(db, *c, class).is_always_true()),
|
||||
),
|
||||
},
|
||||
|
||||
Type::BoundMethod(..)
|
||||
| Type::KnownBoundMethod(..)
|
||||
| Type::WrapperDescriptor(..)
|
||||
|
|
@ -1281,7 +1294,6 @@ fn is_instance_truthiness<'db>(
|
|||
| Type::PropertyInstance(..)
|
||||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::TypeVar(..)
|
||||
| Type::BoundSuper(..)
|
||||
| Type::TypeIs(..)
|
||||
| Type::Callable(..)
|
||||
|
|
|
|||
Loading…
Reference in New Issue