@@ -139,23 +139,18 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N
139139 typeddict_bases_set .add ("TypedDict" )
140140 else :
141141 self .fail ('Duplicate base class "TypedDict"' , defn )
142- elif isinstance (expr , RefExpr ) and self .is_typeddict (expr ):
143- assert expr .fullname
144- if expr .fullname not in typeddict_bases_set :
145- 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 )
146151 typeddict_bases .append (expr )
147152 else :
148- assert isinstance (expr .node , TypeInfo )
149- self .fail (f'Duplicate base class "{ expr .node .name } "' , defn )
150- elif isinstance (expr , IndexExpr ) and self .is_typeddict (expr .base ):
151- assert isinstance (expr .base , RefExpr )
152- assert expr .base .fullname
153- if expr .base .fullname not in typeddict_bases_set :
154- typeddict_bases_set .add (expr .base .fullname )
155- typeddict_bases .append (expr )
156- else :
157- assert isinstance (expr .base .node , TypeInfo )
158- self .fail (f'Duplicate base class "{ expr .base .node .name } "' , defn )
153+ self .fail (f'Duplicate base class "{ info .name } "' , defn )
159154 else :
160155 self .fail ("All bases of a new TypedDict must be TypedDict types" , defn )
161156
@@ -192,30 +187,13 @@ def add_keys_and_types_from_base(
192187 readonly_keys : set [str ],
193188 ctx : Context ,
194189 ) -> None :
190+ info = self ._parse_typeddict_base (base , ctx )
195191 base_args : list [Type ] = []
196- if isinstance (base , RefExpr ):
197- if isinstance (base .node , TypeAlias ):
198- # Only old TypeAlias / plain assignment, PEP695 `type` stmt
199- # cannot be used as a base class
200- target = get_proper_type (base .node .target )
201- assert isinstance (target , TypedDictType )
202- info = target .fallback .type
203- elif isinstance (base .node , TypeInfo ):
204- info = base .node
205- else :
206- assert False
207- elif isinstance (base , IndexExpr ):
208- assert isinstance (base .base , RefExpr )
209- assert isinstance (base .base .node , TypeInfo )
210- info = base .base .node
192+ if isinstance (base , IndexExpr ):
211193 args = self .analyze_base_args (base , ctx )
212194 if args is None :
213195 return
214196 base_args = args
215- else :
216- assert isinstance (base , CallExpr )
217- assert isinstance (base .analyzed , TypedDictExpr )
218- info = base .analyzed .info
219197
220198 assert info .typeddict_type is not None
221199 base_typed_dict = info .typeddict_type
@@ -241,6 +219,26 @@ def add_keys_and_types_from_base(
241219 required_keys .update (base_typed_dict .required_keys )
242220 readonly_keys .update (base_typed_dict .readonly_keys )
243221
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+
244242 def analyze_base_args (self , base : IndexExpr , ctx : Context ) -> list [Type ] | None :
245243 """Analyze arguments of base type expressions as types.
246244
@@ -537,7 +535,7 @@ def parse_typeddict_args(
537535 return "" , [], [], True , [], False
538536 dictexpr = args [1 ]
539537 tvar_defs = self .api .get_and_bind_all_tvars ([t for k , t in dictexpr .items ])
540- res = self .parse_typeddict_fields_with_types (dictexpr .items , call )
538+ res = self .parse_typeddict_fields_with_types (dictexpr .items )
541539 if res is None :
542540 # One of the types is not ready, defer.
543541 return None
@@ -546,7 +544,7 @@ def parse_typeddict_args(
546544 return args [0 ].value , items , types , total , tvar_defs , ok
547545
548546 def parse_typeddict_fields_with_types (
549- self , dict_items : list [tuple [Expression | None , Expression ]], context : Context
547+ self , dict_items : list [tuple [Expression | None , Expression ]]
550548 ) -> tuple [list [str ], list [Type ], bool ] | None :
551549 """Parse typed dict items passed as pairs (name expression, type expression).
552550
0 commit comments