@@ -510,7 +510,7 @@ def visit_UnaryOp(self, node):
510
510
return self ._replace_node ("_upg_UnaryOp_Not" , node ) if isinstance (node .op , ast .Not ) else node
511
511
512
512
replacer = RewriteName ()
513
- root = ast .parse (expr .strip (), mode = "eval" ). body
513
+ root = ast .parse (expr .strip (), mode = "exec" )
514
514
visited = replacer .visit (root )
515
515
return (ast_unparse (visited ).strip (), SelfPrintEvalContext (replacer .replaces ))
516
516
@@ -521,36 +521,40 @@ class _Replacer(ast.NodeTransformer):
521
521
def __init__ (self , mapping ):
522
522
self .mapping = collections .defaultdict (list )
523
523
for key , value in mapping .items ():
524
- key_ast = ast .parse (key , mode = "eval" ).body
524
+ key_ast = key if isinstance ( key , ast . AST ) else ast .parse (key , mode = "eval" ).body
525
525
self .mapping [key_ast .__class__ .__name__ ].append ((key_ast , value ))
526
526
527
527
def _no_match (self , left , right ):
528
528
return False
529
529
530
530
def _match (self , left , right ):
531
- same_ctx = getattr ( left , "ctx" , None ). __class__ is getattr (right , "ctx" , None ).__class__
531
+ same_ctx = not hasattr ( right , "ctx" ) or getattr (left , "ctx" , None ). __class__ is right . ctx .__class__
532
532
cname = left .__class__ .__name__
533
533
same_type = right .__class__ .__name__ == cname
534
534
matcher = getattr (self , "_match_" + cname , self ._no_match )
535
535
return matcher (left , right ) if same_type and same_ctx else False
536
536
537
537
def _match_Constant (self , left , right ):
538
538
# we don't care about kind for u-strings
539
- return type (left .value ) is type (right .value ) and left .value == right .value
539
+ return right .value is literal_replace .WILDCARD or (
540
+ type (left .value ) is type (right .value ) and left .value == right .value
541
+ )
540
542
541
543
def _match_Num (self , left , right ):
542
544
# Dreprecated, for Python <3.8
543
- return type (left .n ) is type (right .n ) and left .n == right .n
545
+ return right . n is literal_replace . WILDCARD or ( type (left .n ) is type (right .n ) and left .n == right .n )
544
546
545
547
def _match_Str (self , left , right ):
546
548
# Deprecated, for Python <3.8
547
- return left .s == right .s
549
+ return right . s is literal_replace . WILDCARD or left .s == right .s
548
550
549
551
def _match_Name (self , left , right ):
550
- return left .id == right .id
552
+ return right . id is literal_replace . WILDCARD or left .id == right .id
551
553
552
554
def _match_Attribute (self , left , right ):
553
- return left .attr == right .attr and self ._match (left .value , right .value )
555
+ return (right .attr is literal_replace .WILDCARD or left .attr == right .attr ) and self ._match (
556
+ left .value , right .value
557
+ )
554
558
555
559
def _match_List (self , left , right ):
556
560
return len (left .elts ) == len (right .elts ) and all (
@@ -581,14 +585,23 @@ def visit(self, node):
581
585
if node .__class__ .__name__ in self .mapping :
582
586
for target_ast , new in self .mapping [node .__class__ .__name__ ]:
583
587
if self ._match (node , target_ast ):
584
- return ast .parse (new , mode = "eval" ).body
588
+ # since we use eval below, non-callable must be an expression (no assignment)
589
+ return new (node ) if callable (new ) else ast .parse (new , mode = "eval" ).body
585
590
return super (_Replacer , self ).visit (node )
586
591
587
592
588
593
def literal_replace (expr , mapping ):
589
594
if ast_unparse is None :
590
595
_logger .critical ("AST unparse unavailable" )
591
596
return expr
592
- root = ast .parse (expr .strip (), mode = "eval" ). body
597
+ root = ast .parse (expr .strip (), mode = "exec" )
593
598
visited = _Replacer (mapping ).visit (root )
594
599
return ast_unparse (visited ).strip ()
600
+
601
+
602
+ class _Symbol (str ):
603
+ def __repr__ (self ):
604
+ return str (self )
605
+
606
+
607
+ literal_replace .WILDCARD = _Symbol ("*" )
0 commit comments