@@ -6804,18 +6804,20 @@ def narrow_type_by_identity_equality(
68046804 continue
68056805 expr = operands [j ]
68066806
6807- current_type_range = self .get_isinstance_type ( expr )
6808- if current_type_range is not None :
6809- target_type = make_simplified_union ([ tr . item for tr in current_type_range ])
6810- if isinstance (target_type , AnyType ):
6811- # Avoid widening to Any for checks like `type(x) is type(y: Any)`.
6812- # We patch this here because it is desirable to widen to any for cases like
6813- # isinstance(x, (y: Any))
6814- continue
6807+ current_type_range = self .get_type_range_of_type ( operand_types [ j ] )
6808+ if current_type_range is None :
6809+ continue
6810+ if isinstance (get_proper_type ( current_type_range . item ) , AnyType ):
6811+ # Avoid widening to Any for checks like `type(x) is type(y: Any)`.
6812+ # We patch this here because it is desirable to widen to any for cases like
6813+ # isinstance(x, (y: Any))
6814+ continue
68156815 if_map , else_map = conditional_types_to_typemaps (
68166816 expr_in_type_expr ,
68176817 * self .conditional_types_with_intersection (
6818- self .lookup_type (expr_in_type_expr ), current_type_range , expr_in_type_expr
6818+ self .lookup_type (expr_in_type_expr ),
6819+ [current_type_range ],
6820+ expr_in_type_expr ,
68196821 ),
68206822 )
68216823
@@ -7899,51 +7901,87 @@ def is_writable_attribute(self, node: Node) -> bool:
78997901 return first_item .var .is_settable_property
79007902 return False
79017903
7902- def get_isinstance_type (self , expr : Expression ) -> list [TypeRange ] | None :
7904+ def get_isinstance_type (
7905+ self , expr : Expression , flatten_tuples : bool = True
7906+ ) -> list [TypeRange ] | None :
79037907 """Get the type(s) resulting from an isinstance check.
79047908
79057909 Returns an empty list for isinstance(x, ()).
79067910 """
79077911 if isinstance (expr , OpExpr ) and expr .op == "|" :
7908- left = self .get_isinstance_type (expr .left )
7909- if left is None and is_literal_none (expr .left ):
7912+ left : list [TypeRange ] | None
7913+ right : list [TypeRange ] | None
7914+ if is_literal_none (expr .left ):
79107915 left = [TypeRange (NoneType (), is_upper_bound = False )]
7911- right = self .get_isinstance_type (expr .right )
7912- if right is None and is_literal_none (expr .right ):
7916+ else :
7917+ left = self .get_isinstance_type (expr .left , flatten_tuples = False )
7918+ if is_literal_none (expr .right ):
79137919 right = [TypeRange (NoneType (), is_upper_bound = False )]
7920+ else :
7921+ right = self .get_isinstance_type (expr .right , flatten_tuples = False )
79147922 if left is None or right is None :
79157923 return None
79167924 return left + right
7917- all_types = get_proper_types (flatten_types (self .lookup_type (expr )))
7918- types : list [TypeRange ] = []
7919- for typ in all_types :
7920- if isinstance (typ , FunctionLike ) and typ .is_type_obj ():
7921- # If a type is generic, `isinstance` can only narrow its variables to Any.
7922- any_parameterized = fill_typevars_with_any (typ .type_object ())
7923- # Tuples may have unattended type variables among their items
7924- if isinstance (any_parameterized , TupleType ):
7925- erased_type = erase_typevars (any_parameterized )
7926- else :
7927- erased_type = any_parameterized
7928- types .append (TypeRange (erased_type , is_upper_bound = False ))
7929- elif isinstance (typ , TypeType ):
7930- # Type[A] means "any type that is a subtype of A" rather than "precisely type A"
7931- # we indicate this by setting is_upper_bound flag
7932- is_upper_bound = True
7933- if isinstance (typ .item , NoneType ):
7934- # except for Type[None], because "'NoneType' is not an acceptable base type"
7935- is_upper_bound = False
7936- types .append (TypeRange (typ .item , is_upper_bound = is_upper_bound ))
7937- elif isinstance (typ , Instance ) and typ .type .fullname == "builtins.type" :
7938- object_type = Instance (typ .type .mro [- 1 ], [])
7939- types .append (TypeRange (object_type , is_upper_bound = True ))
7940- elif isinstance (typ , Instance ) and typ .type .fullname == "types.UnionType" and typ .args :
7941- types .append (TypeRange (UnionType (typ .args ), is_upper_bound = False ))
7942- elif isinstance (typ , AnyType ):
7943- types .append (TypeRange (typ , is_upper_bound = False ))
7944- else : # we didn't see an actual type, but rather a variable with unknown value
7925+
7926+ if flatten_tuples :
7927+ type_ranges = []
7928+ for typ in flatten_types_if_tuple (self .lookup_type (expr )):
7929+ type_range = self .get_type_range_of_type (typ )
7930+ if type_range is None :
7931+ return None
7932+ type_ranges .append (type_range )
7933+ return type_ranges
7934+
7935+ else :
7936+ type_range = self .get_type_range_of_type (self .lookup_type (expr ))
7937+ if type_range is None :
79457938 return None
7946- return types
7939+ return [type_range ]
7940+
7941+ def get_type_range_of_type (self , typ : Type ) -> TypeRange | None :
7942+ typ = get_proper_type (typ )
7943+ if isinstance (typ , TypeVarType ):
7944+ typ = get_proper_type (typ .upper_bound )
7945+
7946+ if isinstance (typ , UnionType ):
7947+ type_ranges = [self .get_type_range_of_type (item ) for item in typ .items ]
7948+ is_upper_bound = any (t .is_upper_bound for t in type_ranges if t is not None )
7949+ item = make_simplified_union ([t .item for t in type_ranges if t is not None ])
7950+ return TypeRange (item , is_upper_bound = is_upper_bound )
7951+ if isinstance (typ , FunctionLike ) and typ .is_type_obj ():
7952+ # If a type is generic, `isinstance` can only narrow its variables to Any.
7953+ any_parameterized = fill_typevars_with_any (typ .type_object ())
7954+ # Tuples may have unattended type variables among their items
7955+ if isinstance (any_parameterized , TupleType ):
7956+ erased_type = erase_typevars (any_parameterized )
7957+ else :
7958+ erased_type = any_parameterized
7959+ return TypeRange (erased_type , is_upper_bound = False )
7960+ if isinstance (typ , TypeType ):
7961+ # Type[A] means "any type that is a subtype of A" rather than "precisely type A"
7962+ # we indicate this by setting is_upper_bound flag
7963+ is_upper_bound = True
7964+ if isinstance (typ .item , NoneType ):
7965+ # except for Type[None], because "'NoneType' is not an acceptable base type"
7966+ is_upper_bound = False
7967+ return TypeRange (typ .item , is_upper_bound = is_upper_bound )
7968+ if isinstance (typ , AnyType ):
7969+ return TypeRange (typ , is_upper_bound = False )
7970+ if isinstance (typ , Instance ) and typ .type .fullname == "builtins.type" :
7971+ object_type = Instance (typ .type .mro [- 1 ], [])
7972+ return TypeRange (object_type , is_upper_bound = True )
7973+ if isinstance (typ , Instance ) and typ .type .fullname == "types.UnionType" and typ .args :
7974+ return TypeRange (UnionType (typ .args ), is_upper_bound = False )
7975+ if isinstance (typ , Instance ) and typ .type .fullname == "typing._SpecialForm" :
7976+ # This is probably an alias to a Union object. We don't have the args here so we can't
7977+ # conclude anything
7978+ return None
7979+ if not is_subtype (self .named_type ("builtins.type" ), typ ):
7980+ # We saw something, but it couldn't possibly be valid
7981+ return TypeRange (UninhabitedType (), is_upper_bound = False )
7982+
7983+ # This is e.g. a variable of type object, so we can't conclude anything
7984+ return None
79477985
79487986 def is_literal_enum (self , n : Expression ) -> bool :
79497987 """Returns true if this expression (with the given type context) is an Enum literal.
@@ -8642,13 +8680,13 @@ def flatten(t: Expression) -> list[Expression]:
86428680 return [t ]
86438681
86448682
8645- def flatten_types (t : Type ) -> list [Type ]:
8683+ def flatten_types_if_tuple (t : Type ) -> list [Type ]:
86468684 """Flatten a nested sequence of tuples into one list of nodes."""
86478685 t = get_proper_type (t )
86488686 if isinstance (t , UnionType ):
8649- return [b for a in t .items for b in flatten_types (a )]
8687+ return [b for a in t .items for b in flatten_types_if_tuple (a )]
86508688 if isinstance (t , TupleType ):
8651- return [b for a in t .items for b in flatten_types (a )]
8689+ return [b for a in t .items for b in flatten_types_if_tuple (a )]
86528690 elif is_named_instance (t , "builtins.tuple" ):
86538691 return [t .args [0 ]]
86548692 return [t ]
0 commit comments