@@ -138,47 +138,48 @@ def check_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool
138
138
def check_namedtuple (self ,
139
139
node : Expression ,
140
140
var_name : Optional [str ],
141
- is_func_scope : bool ) -> Tuple [bool , Optional [TypeInfo ]]:
141
+ is_func_scope : bool ) -> Tuple [Optional [ str ] , Optional [TypeInfo ]]:
142
142
"""Check if a call defines a namedtuple.
143
143
144
144
The optional var_name argument is the name of the variable to
145
145
which this is assigned, if any.
146
146
147
147
Return a tuple of two items:
148
- * Can it be a valid named tuple?
148
+ * Internal name of the named tuple (e.g. the name passed as an argument to namedtuple)
149
+ or None if it is not a valid named tuple
149
150
* Corresponding TypeInfo, or None if not ready.
150
151
151
152
If the definition is invalid but looks like a namedtuple,
152
153
report errors but return (some) TypeInfo.
153
154
"""
154
155
if not isinstance (node , CallExpr ):
155
- return False , None
156
+ return None , None
156
157
call = node
157
158
callee = call .callee
158
159
if not isinstance (callee , RefExpr ):
159
- return False , None
160
+ return None , None
160
161
fullname = callee .fullname
161
162
if fullname == 'collections.namedtuple' :
162
163
is_typed = False
163
164
elif fullname == 'typing.NamedTuple' :
164
165
is_typed = True
165
166
else :
166
- return False , None
167
+ return None , None
167
168
result = self .parse_namedtuple_args (call , fullname )
168
169
if result :
169
- items , types , defaults , ok = result
170
+ items , types , defaults , typename , ok = result
170
171
else :
171
- # This is a valid named tuple but some types are not ready.
172
- return True , None
173
- if not ok :
174
172
# Error. Construct dummy return value.
175
173
if var_name :
176
174
name = var_name
177
175
else :
178
176
name = 'namedtuple@' + str (call .line )
179
177
info = self .build_namedtuple_typeinfo (name , [], [], {}, node .line )
180
178
self .store_namedtuple_info (info , name , call , is_typed )
181
- return True , info
179
+ return name , info
180
+ if not ok :
181
+ # This is a valid named tuple but some types are not ready.
182
+ return typename , None
182
183
183
184
# We use the variable name as the class name if it exists. If
184
185
# it doesn't, we use the name passed as an argument. We prefer
@@ -188,7 +189,7 @@ def check_namedtuple(self,
188
189
if var_name :
189
190
name = var_name
190
191
else :
191
- name = cast ( Union [ StrExpr , BytesExpr , UnicodeExpr ], call . args [ 0 ]). value
192
+ name = typename
192
193
193
194
if var_name is None or is_func_scope :
194
195
# There are two special cases where need to give it a unique name derived
@@ -228,7 +229,7 @@ def check_namedtuple(self,
228
229
if name != var_name or is_func_scope :
229
230
# NOTE: we skip local namespaces since they are not serialized.
230
231
self .api .add_symbol_skip_local (name , info )
231
- return True , info
232
+ return typename , info
232
233
233
234
def store_namedtuple_info (self , info : TypeInfo , name : str ,
234
235
call : CallExpr , is_typed : bool ) -> None :
@@ -237,26 +238,30 @@ def store_namedtuple_info(self, info: TypeInfo, name: str,
237
238
call .analyzed .set_line (call .line , call .column )
238
239
239
240
def parse_namedtuple_args (self , call : CallExpr , fullname : str
240
- ) -> Optional [Tuple [List [str ], List [Type ], List [Expression ], bool ]]:
241
+ ) -> Optional [Tuple [List [str ], List [Type ], List [Expression ],
242
+ str , bool ]]:
241
243
"""Parse a namedtuple() call into data needed to construct a type.
242
244
243
- Returns a 4 -tuple:
245
+ Returns a 5 -tuple:
244
246
- List of argument names
245
247
- List of argument types
246
- - Number of arguments that have a default value
247
- - Whether the definition typechecked.
248
+ - List of default values
249
+ - First argument of namedtuple
250
+ - Whether all types are ready.
248
251
249
- Return None if at least one of the types is not ready .
252
+ Return None if the definition didn't typecheck .
250
253
"""
251
254
# TODO: Share code with check_argument_count in checkexpr.py?
252
255
args = call .args
253
256
if len (args ) < 2 :
254
- return self .fail_namedtuple_arg ("Too few arguments for namedtuple()" , call )
257
+ self .fail ("Too few arguments for namedtuple()" , call )
258
+ return None
255
259
defaults = [] # type: List[Expression]
256
260
if len (args ) > 2 :
257
261
# Typed namedtuple doesn't support additional arguments.
258
262
if fullname == 'typing.NamedTuple' :
259
- return self .fail_namedtuple_arg ("Too many arguments for NamedTuple()" , call )
263
+ self .fail ("Too many arguments for NamedTuple()" , call )
264
+ return None
260
265
for i , arg_name in enumerate (call .arg_names [2 :], 2 ):
261
266
if arg_name == 'defaults' :
262
267
arg = args [i ]
@@ -272,38 +277,42 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str
272
277
)
273
278
break
274
279
if call .arg_kinds [:2 ] != [ARG_POS , ARG_POS ]:
275
- return self .fail_namedtuple_arg ("Unexpected arguments to namedtuple()" , call )
280
+ self .fail ("Unexpected arguments to namedtuple()" , call )
281
+ return None
276
282
if not isinstance (args [0 ], (StrExpr , BytesExpr , UnicodeExpr )):
277
- return self .fail_namedtuple_arg (
283
+ self .fail (
278
284
"namedtuple() expects a string literal as the first argument" , call )
285
+ return None
286
+ typename = cast (Union [StrExpr , BytesExpr , UnicodeExpr ], call .args [0 ]).value
279
287
types = [] # type: List[Type]
280
- ok = True
281
288
if not isinstance (args [1 ], (ListExpr , TupleExpr )):
282
289
if (fullname == 'collections.namedtuple'
283
290
and isinstance (args [1 ], (StrExpr , BytesExpr , UnicodeExpr ))):
284
291
str_expr = args [1 ]
285
292
items = str_expr .value .replace (',' , ' ' ).split ()
286
293
else :
287
- return self .fail_namedtuple_arg (
294
+ self .fail (
288
295
"List or tuple literal expected as the second argument to namedtuple()" , call )
296
+ return None
289
297
else :
290
298
listexpr = args [1 ]
291
299
if fullname == 'collections.namedtuple' :
292
300
# The fields argument contains just names, with implicit Any types.
293
301
if any (not isinstance (item , (StrExpr , BytesExpr , UnicodeExpr ))
294
302
for item in listexpr .items ):
295
- return self .fail_namedtuple_arg ("String literal expected as namedtuple() item" ,
296
- call )
303
+ self .fail ("String literal expected as namedtuple() item" , call )
304
+ return None
297
305
items = [cast (Union [StrExpr , BytesExpr , UnicodeExpr ], item ).value
298
306
for item in listexpr .items ]
299
307
else :
300
308
# The fields argument contains (name, type) tuples.
301
309
result = self .parse_namedtuple_fields_with_types (listexpr .items , call )
302
- if result :
303
- items , types , _ , ok = result
304
- else :
310
+ if result is None :
305
311
# One of the types is not ready, defer.
306
312
return None
313
+ items , types , _ , ok = result
314
+ if not ok :
315
+ return [], [], [], typename , False
307
316
if not types :
308
317
types = [AnyType (TypeOfAny .unannotated ) for _ in items ]
309
318
underscore = [item for item in items if item .startswith ('_' )]
@@ -313,50 +322,46 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str
313
322
if len (defaults ) > len (items ):
314
323
self .fail ("Too many defaults given in call to namedtuple()" , call )
315
324
defaults = defaults [:len (items )]
316
- return items , types , defaults , ok
325
+ return items , types , defaults , typename , True
317
326
318
327
def parse_namedtuple_fields_with_types (self , nodes : List [Expression ], context : Context
319
328
) -> Optional [Tuple [List [str ], List [Type ],
320
- List [Expression ],
321
- bool ]]:
329
+ List [Expression ], bool ]]:
322
330
"""Parse typed named tuple fields.
323
331
324
- Return (names, types, defaults, error occurred), or None if at least one of
325
- the types is not ready.
332
+ Return (names, types, defaults, whether types are all ready), or None if error occurred.
326
333
"""
327
334
items = [] # type: List[str]
328
335
types = [] # type: List[Type]
329
336
for item in nodes :
330
337
if isinstance (item , TupleExpr ):
331
338
if len (item .items ) != 2 :
332
- return self .fail_namedtuple_arg ("Invalid NamedTuple field definition" ,
333
- item )
339
+ self .fail ("Invalid NamedTuple field definition" , item )
340
+ return None
334
341
name , type_node = item .items
335
342
if isinstance (name , (StrExpr , BytesExpr , UnicodeExpr )):
336
343
items .append (name .value )
337
344
else :
338
- return self .fail_namedtuple_arg ("Invalid NamedTuple() field name" , item )
345
+ self .fail ("Invalid NamedTuple() field name" , item )
346
+ return None
339
347
try :
340
348
type = expr_to_unanalyzed_type (type_node )
341
349
except TypeTranslationError :
342
- return self .fail_namedtuple_arg ('Invalid field type' , type_node )
350
+ self .fail ('Invalid field type' , type_node )
351
+ return None
343
352
analyzed = self .api .anal_type (type )
344
353
# Workaround #4987 and avoid introducing a bogus UnboundType
345
354
if isinstance (analyzed , UnboundType ):
346
355
analyzed = AnyType (TypeOfAny .from_error )
347
356
# These should be all known, otherwise we would defer in visit_assignment_stmt().
348
357
if analyzed is None :
349
- return None
358
+ return [], [], [], False
350
359
types .append (analyzed )
351
360
else :
352
- return self .fail_namedtuple_arg ("Tuple expected as NamedTuple() field" , item )
361
+ self .fail ("Tuple expected as NamedTuple() field" , item )
362
+ return None
353
363
return items , types , [], True
354
364
355
- def fail_namedtuple_arg (self , message : str , context : Context
356
- ) -> Tuple [List [str ], List [Type ], List [Expression ], bool ]:
357
- self .fail (message , context )
358
- return [], [], [], False
359
-
360
365
def build_namedtuple_typeinfo (self ,
361
366
name : str ,
362
367
items : List [str ],
0 commit comments