30
30
StrExpr ,
31
31
TempNode ,
32
32
TupleExpr ,
33
+ TypeAlias ,
33
34
TypedDictExpr ,
34
35
TypeInfo ,
35
36
)
50
51
TypedDictType ,
51
52
TypeOfAny ,
52
53
TypeVarLikeType ,
54
+ get_proper_type ,
53
55
)
54
56
55
57
TPDICT_CLASS_ERROR : Final = (
@@ -137,23 +139,18 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N
137
139
typeddict_bases_set .add ("TypedDict" )
138
140
else :
139
141
self .fail ('Duplicate base class "TypedDict"' , defn )
140
- elif isinstance (expr , RefExpr ) and self .is_typeddict (expr ):
141
- assert expr .fullname
142
- if expr .fullname not in typeddict_bases_set :
143
- typeddict_bases_set .add (expr .fullname )
142
+ elif (
143
+ isinstance (expr , RefExpr )
144
+ and self .is_typeddict (expr )
145
+ or isinstance (expr , IndexExpr )
146
+ and self .is_typeddict (expr .base )
147
+ ):
148
+ info = self ._parse_typeddict_base (expr , defn )
149
+ if info .fullname not in typeddict_bases_set :
150
+ typeddict_bases_set .add (info .fullname )
144
151
typeddict_bases .append (expr )
145
152
else :
146
- assert isinstance (expr .node , TypeInfo )
147
- self .fail (f'Duplicate base class "{ expr .node .name } "' , defn )
148
- elif isinstance (expr , IndexExpr ) and self .is_typeddict (expr .base ):
149
- assert isinstance (expr .base , RefExpr )
150
- assert expr .base .fullname
151
- if expr .base .fullname not in typeddict_bases_set :
152
- typeddict_bases_set .add (expr .base .fullname )
153
- typeddict_bases .append (expr )
154
- else :
155
- assert isinstance (expr .base .node , TypeInfo )
156
- self .fail (f'Duplicate base class "{ expr .base .node .name } "' , defn )
153
+ self .fail (f'Duplicate base class "{ info .name } "' , defn )
157
154
else :
158
155
self .fail ("All bases of a new TypedDict must be TypedDict types" , defn )
159
156
@@ -190,22 +187,13 @@ def add_keys_and_types_from_base(
190
187
readonly_keys : set [str ],
191
188
ctx : Context ,
192
189
) -> None :
190
+ info = self ._parse_typeddict_base (base , ctx )
193
191
base_args : list [Type ] = []
194
- if isinstance (base , RefExpr ):
195
- assert isinstance (base .node , TypeInfo )
196
- info = base .node
197
- elif isinstance (base , IndexExpr ):
198
- assert isinstance (base .base , RefExpr )
199
- assert isinstance (base .base .node , TypeInfo )
200
- info = base .base .node
192
+ if isinstance (base , IndexExpr ):
201
193
args = self .analyze_base_args (base , ctx )
202
194
if args is None :
203
195
return
204
196
base_args = args
205
- else :
206
- assert isinstance (base , CallExpr )
207
- assert isinstance (base .analyzed , TypedDictExpr )
208
- info = base .analyzed .info
209
197
210
198
assert info .typeddict_type is not None
211
199
base_typed_dict = info .typeddict_type
@@ -231,6 +219,26 @@ def add_keys_and_types_from_base(
231
219
required_keys .update (base_typed_dict .required_keys )
232
220
readonly_keys .update (base_typed_dict .readonly_keys )
233
221
222
+ def _parse_typeddict_base (self , base : Expression , ctx : Context ) -> TypeInfo :
223
+ if isinstance (base , RefExpr ):
224
+ if isinstance (base .node , TypeInfo ):
225
+ return base .node
226
+ elif isinstance (base .node , TypeAlias ):
227
+ # Only old TypeAlias / plain assignment, PEP695 `type` stmt
228
+ # cannot be used as a base class
229
+ target = get_proper_type (base .node .target )
230
+ assert isinstance (target , TypedDictType )
231
+ return target .fallback .type
232
+ else :
233
+ assert False
234
+ elif isinstance (base , IndexExpr ):
235
+ assert isinstance (base .base , RefExpr )
236
+ return self ._parse_typeddict_base (base .base , ctx )
237
+ else :
238
+ assert isinstance (base , CallExpr )
239
+ assert isinstance (base .analyzed , TypedDictExpr )
240
+ return base .analyzed .info
241
+
234
242
def analyze_base_args (self , base : IndexExpr , ctx : Context ) -> list [Type ] | None :
235
243
"""Analyze arguments of base type expressions as types.
236
244
@@ -527,7 +535,7 @@ def parse_typeddict_args(
527
535
return "" , [], [], True , [], False
528
536
dictexpr = args [1 ]
529
537
tvar_defs = self .api .get_and_bind_all_tvars ([t for k , t in dictexpr .items ])
530
- res = self .parse_typeddict_fields_with_types (dictexpr .items , call )
538
+ res = self .parse_typeddict_fields_with_types (dictexpr .items )
531
539
if res is None :
532
540
# One of the types is not ready, defer.
533
541
return None
@@ -536,7 +544,7 @@ def parse_typeddict_args(
536
544
return args [0 ].value , items , types , total , tvar_defs , ok
537
545
538
546
def parse_typeddict_fields_with_types (
539
- self , dict_items : list [tuple [Expression | None , Expression ]], context : Context
547
+ self , dict_items : list [tuple [Expression | None , Expression ]]
540
548
) -> tuple [list [str ], list [Type ], bool ] | None :
541
549
"""Parse typed dict items passed as pairs (name expression, type expression).
542
550
@@ -609,10 +617,11 @@ def build_typeddict_typeinfo(
609
617
# Helpers
610
618
611
619
def is_typeddict (self , expr : Expression ) -> bool :
612
- return (
613
- isinstance (expr , RefExpr )
614
- and isinstance (expr .node , TypeInfo )
620
+ return isinstance (expr , RefExpr ) and (
621
+ isinstance (expr .node , TypeInfo )
615
622
and expr .node .typeddict_type is not None
623
+ or isinstance (expr .node , TypeAlias )
624
+ and isinstance (get_proper_type (expr .node .target ), TypedDictType )
616
625
)
617
626
618
627
def fail (self , msg : str , ctx : Context , * , code : ErrorCode | None = None ) -> None :
0 commit comments