@@ -606,14 +606,18 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
606
606
if not sym :
607
607
continue
608
608
node = sym .node
609
- assert isinstance (node , TypeInfo )
609
+ if not isinstance (node , TypeInfo ):
610
+ self .defer (node )
611
+ return
610
612
typ = Instance (node , [self .str_type ()])
611
613
elif name == "__annotations__" :
612
614
sym = self .lookup_qualified ("__builtins__.dict" , Context (), suppress_errors = True )
613
615
if not sym :
614
616
continue
615
617
node = sym .node
616
- assert isinstance (node , TypeInfo )
618
+ if not isinstance (node , TypeInfo ):
619
+ self .defer (node )
620
+ return
617
621
typ = Instance (node , [self .str_type (), AnyType (TypeOfAny .special_form )])
618
622
else :
619
623
assert t is not None , f"type should be specified for { name } "
@@ -1374,7 +1378,7 @@ def analyze_class(self, defn: ClassDef) -> None:
1374
1378
defn .base_type_exprs .extend (defn .removed_base_type_exprs )
1375
1379
defn .removed_base_type_exprs .clear ()
1376
1380
1377
- self .update_metaclass (defn )
1381
+ self .infer_metaclass_and_bases_from_compat_helpers (defn )
1378
1382
1379
1383
bases = defn .base_type_exprs
1380
1384
bases , tvar_defs , is_protocol = self .clean_up_bases_and_infer_type_variables (
@@ -1390,20 +1394,25 @@ def analyze_class(self, defn: ClassDef) -> None:
1390
1394
self .defer ()
1391
1395
1392
1396
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 ):
1396
1399
# Something was incomplete. Defer current target.
1397
1400
self .mark_incomplete (defn .name , defn )
1398
1401
return
1399
1402
1400
- base_types , base_error = result
1403
+ base_types , base_error = bases_result
1401
1404
if any (isinstance (base , PlaceholderType ) for base , _ in base_types ):
1402
1405
# We need to know the TypeInfo of each base to construct the MRO. Placeholder types
1403
1406
# are okay in nested positions, since they can't affect the MRO.
1404
1407
self .mark_incomplete (defn .name , defn )
1405
1408
return
1406
1409
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
+
1407
1416
if self .analyze_typeddict_classdef (defn ):
1408
1417
if defn .info :
1409
1418
self .setup_type_vars (defn , tvar_defs )
@@ -1422,7 +1431,7 @@ def analyze_class(self, defn: ClassDef) -> None:
1422
1431
with self .scope .class_scope (defn .info ):
1423
1432
self .configure_base_classes (defn , base_types )
1424
1433
defn .info .is_protocol = is_protocol
1425
- self .analyze_metaclass (defn )
1434
+ self .recalculate_metaclass (defn , declared_metaclass )
1426
1435
defn .info .runtime_protocol = False
1427
1436
for decorator in defn .decorators :
1428
1437
self .analyze_class_decorator (defn , decorator )
@@ -1968,7 +1977,7 @@ def calculate_class_mro(
1968
1977
if hook :
1969
1978
hook (ClassDefContext (defn , FakeExpression (), self ))
1970
1979
1971
- def update_metaclass (self , defn : ClassDef ) -> None :
1980
+ def infer_metaclass_and_bases_from_compat_helpers (self , defn : ClassDef ) -> None :
1972
1981
"""Lookup for special metaclass declarations, and update defn fields accordingly.
1973
1982
1974
1983
* six.with_metaclass(M, B1, B2, ...)
@@ -2046,30 +2055,33 @@ def is_base_class(self, t: TypeInfo, s: TypeInfo) -> bool:
2046
2055
visited .add (base .type )
2047
2056
return False
2048
2057
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 :
2051
2064
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 )
2056
2069
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 )
2060
2073
if sym is None :
2061
2074
# Probably a name error - it is already handled elsewhere
2062
- return
2075
+ return None , False
2063
2076
if isinstance (sym .node , Var ) and isinstance (get_proper_type (sym .node .type ), AnyType ):
2064
2077
# 'Any' metaclass -- just ignore it.
2065
2078
#
2066
2079
# TODO: A better approach would be to record this information
2067
2080
# and assume that the type object supports arbitrary
2068
2081
# attributes, similar to an 'Any' base class.
2069
- return
2082
+ return None , False
2070
2083
if isinstance (sym .node , PlaceholderNode ):
2071
- self .defer (defn )
2072
- return
2084
+ return None , True # defer later in the caller
2073
2085
2074
2086
# Support type aliases, like `_Meta: TypeAlias = type`
2075
2087
if (
@@ -2083,16 +2095,20 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
2083
2095
metaclass_info = sym .node
2084
2096
2085
2097
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
2088
2100
if not metaclass_info .is_metaclass ():
2089
2101
self .fail (
2090
- 'Metaclasses not inheriting from "type" are not supported' , defn . metaclass
2102
+ 'Metaclasses not inheriting from "type" are not supported' , metaclass_expr
2091
2103
)
2092
- return
2104
+ return None , False
2093
2105
inst = fill_typevars (metaclass_info )
2094
2106
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
2096
2112
defn .info .metaclass_type = defn .info .calculate_metaclass_type ()
2097
2113
if any (info .is_protocol for info in defn .info .mro ):
2098
2114
if (
@@ -2104,13 +2120,15 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
2104
2120
abc_meta = self .named_type_or_none ("abc.ABCMeta" , [])
2105
2121
if abc_meta is not None : # May be None in tests with incomplete lib-stub.
2106
2122
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 :
2108
2124
# Inconsistency may happen due to multiple baseclasses even in classes that
2109
2125
# do not declare explicit metaclass, but it's harder to catch at this stage
2110
2126
if defn .metaclass is not None :
2111
2127
self .fail (f'Inconsistent metaclass structure for "{ defn .name } "' , defn )
2112
2128
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
+ ):
2114
2132
defn .info .is_enum = True
2115
2133
if defn .type_vars :
2116
2134
self .fail ("Enum class cannot be generic" , defn )
0 commit comments