@@ -606,14 +606,18 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
606606 if not sym :
607607 continue
608608 node = sym .node
609- assert isinstance (node , TypeInfo )
609+ if not isinstance (node , TypeInfo ):
610+ self .defer (node )
611+ return
610612 typ = Instance (node , [self .str_type ()])
611613 elif name == "__annotations__" :
612614 sym = self .lookup_qualified ("__builtins__.dict" , Context (), suppress_errors = True )
613615 if not sym :
614616 continue
615617 node = sym .node
616- assert isinstance (node , TypeInfo )
618+ if not isinstance (node , TypeInfo ):
619+ self .defer (node )
620+ return
617621 typ = Instance (node , [self .str_type (), AnyType (TypeOfAny .special_form )])
618622 else :
619623 assert t is not None , f"type should be specified for { name } "
@@ -1374,7 +1378,7 @@ def analyze_class(self, defn: ClassDef) -> None:
13741378 defn .base_type_exprs .extend (defn .removed_base_type_exprs )
13751379 defn .removed_base_type_exprs .clear ()
13761380
1377- self .update_metaclass (defn )
1381+ self .infer_metaclass_and_bases_from_compat_helpers (defn )
13781382
13791383 bases = defn .base_type_exprs
13801384 bases , tvar_defs , is_protocol = self .clean_up_bases_and_infer_type_variables (
@@ -1390,20 +1394,25 @@ def analyze_class(self, defn: ClassDef) -> None:
13901394 self .defer ()
13911395
13921396 self .analyze_class_keywords (defn )
1393- result = self .analyze_base_classes (bases )
1394-
1395- if result is None or self .found_incomplete_ref (tag ):
1397+ bases_result = self .analyze_base_classes (bases )
1398+ if bases_result is None or self .found_incomplete_ref (tag ):
13961399 # Something was incomplete. Defer current target.
13971400 self .mark_incomplete (defn .name , defn )
13981401 return
13991402
1400- base_types , base_error = result
1403+ base_types , base_error = bases_result
14011404 if any (isinstance (base , PlaceholderType ) for base , _ in base_types ):
14021405 # We need to know the TypeInfo of each base to construct the MRO. Placeholder types
14031406 # are okay in nested positions, since they can't affect the MRO.
14041407 self .mark_incomplete (defn .name , defn )
14051408 return
14061409
1410+ declared_metaclass , should_defer = self .get_declared_metaclass (defn .name , defn .metaclass )
1411+ if should_defer or self .found_incomplete_ref (tag ):
1412+ # Metaclass was not ready. Defer current target.
1413+ self .mark_incomplete (defn .name , defn )
1414+ return
1415+
14071416 if self .analyze_typeddict_classdef (defn ):
14081417 if defn .info :
14091418 self .setup_type_vars (defn , tvar_defs )
@@ -1422,7 +1431,7 @@ def analyze_class(self, defn: ClassDef) -> None:
14221431 with self .scope .class_scope (defn .info ):
14231432 self .configure_base_classes (defn , base_types )
14241433 defn .info .is_protocol = is_protocol
1425- self .analyze_metaclass (defn )
1434+ self .recalculate_metaclass (defn , declared_metaclass )
14261435 defn .info .runtime_protocol = False
14271436 for decorator in defn .decorators :
14281437 self .analyze_class_decorator (defn , decorator )
@@ -1968,7 +1977,7 @@ def calculate_class_mro(
19681977 if hook :
19691978 hook (ClassDefContext (defn , FakeExpression (), self ))
19701979
1971- def update_metaclass (self , defn : ClassDef ) -> None :
1980+ def infer_metaclass_and_bases_from_compat_helpers (self , defn : ClassDef ) -> None :
19721981 """Lookup for special metaclass declarations, and update defn fields accordingly.
19731982
19741983 * six.with_metaclass(M, B1, B2, ...)
@@ -2046,30 +2055,33 @@ def is_base_class(self, t: TypeInfo, s: TypeInfo) -> bool:
20462055 visited .add (base .type )
20472056 return False
20482057
2049- def analyze_metaclass (self , defn : ClassDef ) -> None :
2050- if defn .metaclass :
2058+ def get_declared_metaclass (
2059+ self , name : str , metaclass_expr : Expression | None
2060+ ) -> tuple [Instance | None , bool ]:
2061+ """Returns either metaclass instance or boolean whether we should defer."""
2062+ declared_metaclass = None
2063+ if metaclass_expr :
20512064 metaclass_name = None
2052- if isinstance (defn . metaclass , NameExpr ):
2053- metaclass_name = defn . metaclass .name
2054- elif isinstance (defn . metaclass , MemberExpr ):
2055- metaclass_name = get_member_expr_fullname (defn . metaclass )
2065+ if isinstance (metaclass_expr , NameExpr ):
2066+ metaclass_name = metaclass_expr .name
2067+ elif isinstance (metaclass_expr , MemberExpr ):
2068+ metaclass_name = get_member_expr_fullname (metaclass_expr )
20562069 if metaclass_name is None :
2057- self .fail (f'Dynamic metaclass not supported for "{ defn . name } "' , defn . metaclass )
2058- return
2059- sym = self .lookup_qualified (metaclass_name , defn . metaclass )
2070+ self .fail (f'Dynamic metaclass not supported for "{ name } "' , metaclass_expr )
2071+ return None , False
2072+ sym = self .lookup_qualified (metaclass_name , metaclass_expr )
20602073 if sym is None :
20612074 # Probably a name error - it is already handled elsewhere
2062- return
2075+ return None , False
20632076 if isinstance (sym .node , Var ) and isinstance (get_proper_type (sym .node .type ), AnyType ):
20642077 # 'Any' metaclass -- just ignore it.
20652078 #
20662079 # TODO: A better approach would be to record this information
20672080 # and assume that the type object supports arbitrary
20682081 # attributes, similar to an 'Any' base class.
2069- return
2082+ return None , False
20702083 if isinstance (sym .node , PlaceholderNode ):
2071- self .defer (defn )
2072- return
2084+ return None , True # defer later in the caller
20732085
20742086 # Support type aliases, like `_Meta: TypeAlias = type`
20752087 if (
@@ -2083,16 +2095,20 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
20832095 metaclass_info = sym .node
20842096
20852097 if not isinstance (metaclass_info , TypeInfo ) or metaclass_info .tuple_type is not None :
2086- self .fail (f'Invalid metaclass "{ metaclass_name } "' , defn . metaclass )
2087- return
2098+ self .fail (f'Invalid metaclass "{ metaclass_name } "' , metaclass_expr )
2099+ return None , False
20882100 if not metaclass_info .is_metaclass ():
20892101 self .fail (
2090- 'Metaclasses not inheriting from "type" are not supported' , defn . metaclass
2102+ 'Metaclasses not inheriting from "type" are not supported' , metaclass_expr
20912103 )
2092- return
2104+ return None , False
20932105 inst = fill_typevars (metaclass_info )
20942106 assert isinstance (inst , Instance )
2095- defn .info .declared_metaclass = inst
2107+ declared_metaclass = inst
2108+ return declared_metaclass , False
2109+
2110+ def recalculate_metaclass (self , defn : ClassDef , declared_metaclass : Instance | None ) -> None :
2111+ defn .info .declared_metaclass = declared_metaclass
20962112 defn .info .metaclass_type = defn .info .calculate_metaclass_type ()
20972113 if any (info .is_protocol for info in defn .info .mro ):
20982114 if (
@@ -2104,13 +2120,15 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
21042120 abc_meta = self .named_type_or_none ("abc.ABCMeta" , [])
21052121 if abc_meta is not None : # May be None in tests with incomplete lib-stub.
21062122 defn .info .metaclass_type = abc_meta
2107- if defn .info .metaclass_type is None :
2123+ if declared_metaclass is not None and defn .info .metaclass_type is None :
21082124 # Inconsistency may happen due to multiple baseclasses even in classes that
21092125 # do not declare explicit metaclass, but it's harder to catch at this stage
21102126 if defn .metaclass is not None :
21112127 self .fail (f'Inconsistent metaclass structure for "{ defn .name } "' , defn )
21122128 else :
2113- if defn .info .metaclass_type .type .has_base ("enum.EnumMeta" ):
2129+ if defn .info .metaclass_type and defn .info .metaclass_type .type .has_base (
2130+ "enum.EnumMeta"
2131+ ):
21142132 defn .info .is_enum = True
21152133 if defn .type_vars :
21162134 self .fail ("Enum class cannot be generic" , defn )
0 commit comments