7575 BoolTypeQuery ,
7676 CallableArgument ,
7777 CallableType ,
78+ CollectAliasesVisitor ,
7879 DeletedType ,
7980 EllipsisType ,
8081 ErasedType ,
@@ -275,7 +276,9 @@ def __init__(
275276 self .prohibit_special_class_field_types = prohibit_special_class_field_types
276277 # Allow variables typed as Type[Any] and type (useful for base classes).
277278 self .allow_type_any = allow_type_any
278- self .allow_type_var_tuple = False
279+ # Level of nesting at which a TypeVarTuple is allowed. Note we specify exact level
280+ # to prohibit things like Unpack[list[Ts]], which are not supported.
281+ self .allow_type_var_tuple = - 1
279282 self .allow_unpack = allow_unpack
280283 # Set when we are analyzing a default of a type variable.
281284 self .analyzing_tvar_def = analyzing_tvar_def
@@ -453,7 +456,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
453456 self .fail (msg , t , code = codes .VALID_TYPE )
454457 return AnyType (TypeOfAny .from_error )
455458 assert isinstance (tvar_def , TypeVarTupleType )
456- if not self .allow_type_var_tuple :
459+ if self .allow_type_var_tuple != self . nesting_level :
457460 self .fail (
458461 f'TypeVarTuple "{ t .name } " is only valid with an unpack' ,
459462 t ,
@@ -808,9 +811,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
808811 if not self .allow_unpack :
809812 self .fail (message_registry .INVALID_UNPACK_POSITION , t , code = codes .VALID_TYPE )
810813 return AnyType (TypeOfAny .from_error )
811- self .allow_type_var_tuple = True
814+ self .allow_type_var_tuple = self . nesting_level + 1
812815 result = UnpackType (self .anal_type (t .args [0 ]), line = t .line , column = t .column )
813- self .allow_type_var_tuple = False
816+ self .allow_type_var_tuple = - 1
814817 return result
815818 elif fullname in SELF_TYPE_NAMES :
816819 if t .args :
@@ -1161,9 +1164,9 @@ def visit_unpack_type(self, t: UnpackType) -> Type:
11611164 if not self .allow_unpack :
11621165 self .fail (message_registry .INVALID_UNPACK_POSITION , t .type , code = codes .VALID_TYPE )
11631166 return AnyType (TypeOfAny .from_error )
1164- self .allow_type_var_tuple = True
1167+ self .allow_type_var_tuple = self . nesting_level + 1
11651168 result = UnpackType (self .anal_type (t .type ), from_star_syntax = t .from_star_syntax )
1166- self .allow_type_var_tuple = False
1169+ self .allow_type_var_tuple = - 1
11671170 return result
11681171
11691172 def visit_parameters (self , t : Parameters ) -> Type :
@@ -2500,7 +2503,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
25002503 return t
25012504 new_nodes = self .seen_nodes | {t .alias }
25022505 visitor = DivergingAliasDetector (new_nodes )
2503- _ = get_proper_type ( t ).accept (visitor )
2506+ _ = t . expand_once ( skip_normalization = True ).accept (visitor )
25042507 if visitor .diverging :
25052508 self .diverging = True
25062509 return t
@@ -2518,6 +2521,15 @@ def detect_diverging_alias(node: TypeAlias, target: Type) -> bool:
25182521 They may be handy in rare cases, e.g. to express a union of non-mixed nested lists:
25192522 Nested = Union[T, Nested[List[T]]] ~> Union[T, List[T], List[List[T]], ...]
25202523 """
2524+ is_recursive = node ._is_recursive
2525+ if is_recursive is None :
2526+ is_recursive = node in node .target .accept (CollectAliasesVisitor ())
2527+ if not is_recursive :
2528+ # Fast path: this is not a recursive alias at all.
2529+ return False
2530+ # Note we only cache positive case, caching negative case is risky, as this type alias
2531+ # (or importantly any other alias it uses) may be not ready yet.
2532+ node ._is_recursive = True
25212533 visitor = DivergingAliasDetector ({node })
25222534 _ = target .accept (visitor )
25232535 return visitor .diverging
0 commit comments