@@ -38,6 +38,7 @@ class Format(enum.IntEnum):
38
38
"__weakref__" ,
39
39
"__arg__" ,
40
40
"__globals__" ,
41
+ "__extra_names__" ,
41
42
"__code__" ,
42
43
"__ast_node__" ,
43
44
"__cell__" ,
@@ -82,6 +83,7 @@ def __init__(
82
83
# is created through __class__ assignment on a _Stringifier object.
83
84
self .__globals__ = None
84
85
self .__cell__ = None
86
+ self .__extra_names__ = None
85
87
# These are initially None but serve as a cache and may be set to a non-None
86
88
# value later.
87
89
self .__code__ = None
@@ -151,6 +153,8 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
151
153
if not self .__forward_is_class__ or param_name not in globals :
152
154
globals [param_name ] = param
153
155
locals .pop (param_name , None )
156
+ if self .__extra_names__ :
157
+ locals = {** locals , ** self .__extra_names__ }
154
158
155
159
arg = self .__forward_arg__
156
160
if arg .isidentifier () and not keyword .iskeyword (arg ):
@@ -231,6 +235,10 @@ def __eq__(self, other):
231
235
and self .__forward_is_class__ == other .__forward_is_class__
232
236
and self .__cell__ == other .__cell__
233
237
and self .__owner__ == other .__owner__
238
+ and (
239
+ (tuple (sorted (self .__extra_names__ .items ())) if self .__extra_names__ else None ) ==
240
+ (tuple (sorted (other .__extra_names__ .items ())) if other .__extra_names__ else None )
241
+ )
234
242
)
235
243
236
244
def __hash__ (self ):
@@ -241,6 +249,7 @@ def __hash__(self):
241
249
self .__forward_is_class__ ,
242
250
self .__cell__ ,
243
251
self .__owner__ ,
252
+ tuple (sorted (self .__extra_names__ .items ())) if self .__extra_names__ else None ,
244
253
))
245
254
246
255
def __or__ (self , other ):
@@ -274,6 +283,7 @@ def __init__(
274
283
cell = None ,
275
284
* ,
276
285
stringifier_dict ,
286
+ extra_names = None ,
277
287
):
278
288
# Either an AST node or a simple str (for the common case where a ForwardRef
279
289
# represent a single name).
@@ -285,49 +295,91 @@ def __init__(
285
295
self .__code__ = None
286
296
self .__ast_node__ = node
287
297
self .__globals__ = globals
298
+ self .__extra_names__ = extra_names
288
299
self .__cell__ = cell
289
300
self .__owner__ = owner
290
301
self .__stringifier_dict__ = stringifier_dict
291
302
292
303
def __convert_to_ast (self , other ):
293
304
if isinstance (other , _Stringifier ):
294
305
if isinstance (other .__ast_node__ , str ):
295
- return ast .Name (id = other .__ast_node__ )
296
- return other .__ast_node__
297
- elif isinstance (other , slice ):
306
+ return ast .Name (id = other .__ast_node__ ), other .__extra_names__
307
+ return other .__ast_node__ , other .__extra_names__
308
+ elif (
309
+ # In STRING format we don't bother with the create_unique_name() dance;
310
+ # it's better to emit the repr() of the object instead of an opaque name.
311
+ self .__stringifier_dict__ .format == Format .STRING
312
+ or other is None
313
+ or type (other ) in (str , int , float , bool , complex )
314
+ ):
315
+ return ast .Constant (value = other ), None
316
+ elif type (other ) is dict :
317
+ extra_names = {}
318
+ keys = []
319
+ values = []
320
+ for key , value in other .items ():
321
+ new_key , new_extra_names = self .__convert_to_ast (key )
322
+ if new_extra_names is not None :
323
+ extra_names .update (new_extra_names )
324
+ keys .append (new_key )
325
+ new_value , new_extra_names = self .__convert_to_ast (value )
326
+ if new_extra_names is not None :
327
+ extra_names .update (new_extra_names )
328
+ values .append (new_value )
329
+ return ast .Dict (keys , values ), extra_names
330
+ elif type (other ) in (list , tuple , set ):
331
+ extra_names = {}
332
+ elts = []
333
+ for elt in other :
334
+ new_elt , new_extra_names = self .__convert_to_ast (elt )
335
+ if new_extra_names is not None :
336
+ extra_names .update (new_extra_names )
337
+ elts .append (new_elt )
338
+ ast_class = {list : ast .List , tuple : ast .Tuple , set : ast .Set }[type (other )]
339
+ return ast_class (elts ), extra_names
340
+ else :
341
+ name = self .__stringifier_dict__ .create_unique_name ()
342
+ return ast .Name (id = name ), {name : other }
343
+
344
+ def __convert_to_ast_getitem (self , other ):
345
+ if isinstance (other , slice ):
346
+ extra_names = {}
347
+
348
+ def conv (obj ):
349
+ if obj is None :
350
+ return None
351
+ new_obj , new_extra_names = self .__convert_to_ast (obj )
352
+ if new_extra_names is not None :
353
+ extra_names .update (new_extra_names )
354
+ return new_obj
355
+
298
356
return ast .Slice (
299
- lower = (
300
- self .__convert_to_ast (other .start )
301
- if other .start is not None
302
- else None
303
- ),
304
- upper = (
305
- self .__convert_to_ast (other .stop )
306
- if other .stop is not None
307
- else None
308
- ),
309
- step = (
310
- self .__convert_to_ast (other .step )
311
- if other .step is not None
312
- else None
313
- ),
314
- )
357
+ lower = conv (other .start ),
358
+ upper = conv (other .stop ),
359
+ step = conv (other .step ),
360
+ ), extra_names
315
361
else :
316
- return ast . Constant ( value = other )
362
+ return self . __convert_to_ast ( other )
317
363
318
364
def __get_ast (self ):
319
365
node = self .__ast_node__
320
366
if isinstance (node , str ):
321
367
return ast .Name (id = node )
322
368
return node
323
369
324
- def __make_new (self , node ):
370
+ def __make_new (self , node , extra_names = None ):
371
+ new_extra_names = {}
372
+ if self .__extra_names__ is not None :
373
+ new_extra_names .update (self .__extra_names__ )
374
+ if extra_names is not None :
375
+ new_extra_names .update (extra_names )
325
376
stringifier = _Stringifier (
326
377
node ,
327
378
self .__globals__ ,
328
379
self .__owner__ ,
329
380
self .__forward_is_class__ ,
330
381
stringifier_dict = self .__stringifier_dict__ ,
382
+ extra_names = new_extra_names or None ,
331
383
)
332
384
self .__stringifier_dict__ .stringifiers .append (stringifier )
333
385
return stringifier
@@ -343,27 +395,37 @@ def __getitem__(self, other):
343
395
if self .__ast_node__ == "__classdict__" :
344
396
raise KeyError
345
397
if isinstance (other , tuple ):
346
- elts = [self .__convert_to_ast (elt ) for elt in other ]
398
+ extra_names = {}
399
+ elts = []
400
+ for elt in other :
401
+ new_elt , new_extra_names = self .__convert_to_ast_getitem (elt )
402
+ if new_extra_names is not None :
403
+ extra_names .update (new_extra_names )
404
+ elts .append (new_elt )
347
405
other = ast .Tuple (elts )
348
406
else :
349
- other = self .__convert_to_ast (other )
407
+ other , extra_names = self .__convert_to_ast_getitem (other )
350
408
assert isinstance (other , ast .AST ), repr (other )
351
- return self .__make_new (ast .Subscript (self .__get_ast (), other ))
409
+ return self .__make_new (ast .Subscript (self .__get_ast (), other ), extra_names )
352
410
353
411
def __getattr__ (self , attr ):
354
412
return self .__make_new (ast .Attribute (self .__get_ast (), attr ))
355
413
356
414
def __call__ (self , * args , ** kwargs ):
357
- return self .__make_new (
358
- ast .Call (
359
- self .__get_ast (),
360
- [self .__convert_to_ast (arg ) for arg in args ],
361
- [
362
- ast .keyword (key , self .__convert_to_ast (value ))
363
- for key , value in kwargs .items ()
364
- ],
365
- )
366
- )
415
+ extra_names = {}
416
+ ast_args = []
417
+ for arg in args :
418
+ new_arg , new_extra_names = self .__convert_to_ast (arg )
419
+ if new_extra_names is not None :
420
+ extra_names .update (new_extra_names )
421
+ ast_args .append (new_arg )
422
+ ast_kwargs = []
423
+ for key , value in kwargs .items ():
424
+ new_value , new_extra_names = self .__convert_to_ast (value )
425
+ if new_extra_names is not None :
426
+ extra_names .update (new_extra_names )
427
+ ast_kwargs .append (ast .keyword (key , new_value ))
428
+ return self .__make_new (ast .Call (self .__get_ast (), ast_args , ast_kwargs ), extra_names )
367
429
368
430
def __iter__ (self ):
369
431
yield self .__make_new (ast .Starred (self .__get_ast ()))
@@ -378,8 +440,9 @@ def __format__(self, format_spec):
378
440
379
441
def _make_binop (op : ast .AST ):
380
442
def binop (self , other ):
443
+ rhs , extra_names = self .__convert_to_ast (other )
381
444
return self .__make_new (
382
- ast .BinOp (self .__get_ast (), op , self . __convert_to_ast ( other ))
445
+ ast .BinOp (self .__get_ast (), op , rhs ), extra_names
383
446
)
384
447
385
448
return binop
@@ -402,8 +465,9 @@ def binop(self, other):
402
465
403
466
def _make_rbinop (op : ast .AST ):
404
467
def rbinop (self , other ):
468
+ new_other , extra_names = self .__convert_to_ast (other )
405
469
return self .__make_new (
406
- ast .BinOp (self . __convert_to_ast ( other ) , op , self .__get_ast ())
470
+ ast .BinOp (new_other , op , self .__get_ast ()), extra_names
407
471
)
408
472
409
473
return rbinop
@@ -426,12 +490,14 @@ def rbinop(self, other):
426
490
427
491
def _make_compare (op ):
428
492
def compare (self , other ):
493
+ rhs , extra_names = self .__convert_to_ast (other )
429
494
return self .__make_new (
430
495
ast .Compare (
431
496
left = self .__get_ast (),
432
497
ops = [op ],
433
- comparators = [self .__convert_to_ast (other )],
434
- )
498
+ comparators = [rhs ],
499
+ ),
500
+ extra_names ,
435
501
)
436
502
437
503
return compare
@@ -459,13 +525,15 @@ def unary_op(self):
459
525
460
526
461
527
class _StringifierDict (dict ):
462
- def __init__ (self , namespace , globals = None , owner = None , is_class = False ):
528
+ def __init__ (self , namespace , * , globals = None , owner = None , is_class = False , format ):
463
529
super ().__init__ (namespace )
464
530
self .namespace = namespace
465
531
self .globals = globals
466
532
self .owner = owner
467
533
self .is_class = is_class
468
534
self .stringifiers = []
535
+ self .next_id = 1
536
+ self .format = format
469
537
470
538
def __missing__ (self , key ):
471
539
fwdref = _Stringifier (
@@ -478,6 +546,11 @@ def __missing__(self, key):
478
546
self .stringifiers .append (fwdref )
479
547
return fwdref
480
548
549
+ def create_unique_name (self ):
550
+ name = f"__annotationlib_name_{ self .next_id } __"
551
+ self .next_id += 1
552
+ return name
553
+
481
554
482
555
def call_evaluate_function (evaluate , format , * , owner = None ):
483
556
"""Call an evaluate function. Evaluate functions are normally generated for
@@ -521,7 +594,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
521
594
# possibly constants if the annotate function uses them directly). We then
522
595
# convert each of those into a string to get an approximation of the
523
596
# original source.
524
- globals = _StringifierDict ({})
597
+ globals = _StringifierDict ({}, format = format )
525
598
if annotate .__closure__ :
526
599
freevars = annotate .__code__ .co_freevars
527
600
new_closure = []
@@ -544,9 +617,9 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
544
617
)
545
618
annos = func (Format .VALUE_WITH_FAKE_GLOBALS )
546
619
if _is_evaluate :
547
- return annos if isinstance ( annos , str ) else repr (annos )
620
+ return _stringify_single (annos )
548
621
return {
549
- key : val if isinstance ( val , str ) else repr (val )
622
+ key : _stringify_single (val )
550
623
for key , val in annos .items ()
551
624
}
552
625
elif format == Format .FORWARDREF :
@@ -569,7 +642,13 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
569
642
# that returns a bool and an defined set of attributes.
570
643
namespace = {** annotate .__builtins__ , ** annotate .__globals__ }
571
644
is_class = isinstance (owner , type )
572
- globals = _StringifierDict (namespace , annotate .__globals__ , owner , is_class )
645
+ globals = _StringifierDict (
646
+ namespace ,
647
+ globals = annotate .__globals__ ,
648
+ owner = owner ,
649
+ is_class = is_class ,
650
+ format = format ,
651
+ )
573
652
if annotate .__closure__ :
574
653
freevars = annotate .__code__ .co_freevars
575
654
new_closure = []
@@ -619,6 +698,16 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
619
698
raise ValueError (f"Invalid format: { format !r} " )
620
699
621
700
701
+ def _stringify_single (anno ):
702
+ if anno is ...:
703
+ return "..."
704
+ # We have to handle str specially to support PEP 563 stringified annotations.
705
+ elif isinstance (anno , str ):
706
+ return anno
707
+ else :
708
+ return repr (anno )
709
+
710
+
622
711
def get_annotate_from_class_namespace (obj ):
623
712
"""Retrieve the annotate function from a class namespace dictionary.
624
713
0 commit comments