def _transform_children(self, node): if not isinstance(node, Node): raise ValueError('Expected a Node, got %r' % node) # In these trees, strings and Nodes are leaves leaves = (str, Node) tree_map = partial(tree.tree_map, leaves=leaves) children = tree_map(partial(self.transform), node.getChildrenTree()) return node.__class__(*children)
def test_transformer_base_class(self): 'Transformer base class' test = partial(self._base, Transformer()) # Transformer.transform doesn't change anything test('a = 0', 'a = 0') test('a = b', 'a = b') test('if t == u-1: a = b,f(c)', 'if t == u-1: a = b,f(c)') # ... # Transformer.transform makes deep copies by default code = 'if t == u: a,b = [1]+[2]\nreturn sum(b,a)' a = parse(code) b = Transformer().transform(a) b.node.nodes[1] = parse('yield x').node.nodes[0] b.node.nodes[0].tests[0][0].ops[0] = parse('t > 0').node.nodes[0].expr b.node.nodes[0].tests[0][1].nodes[0].expr.left.nodes[0] = 5 self.assertNotSimilar(b, parse(code)) self.assertSimilar(a, parse(code))
def transformAssign(self, node): # Only transform constant assignments if not is_const(node.expr): return self._transform_children(node) # '__foo' names come from each lhs name at the head of the assignment: # 'l = a,b = 0,1' -> 'l = a,b = __a,__b', { '__a':0, '__b':1 } # # Unpack the rhs enough to map each '__foo' name to a value. # 'a,l = 0,[1,2]' -> 'a,l = __a,__l', { '__a':0, '__l':[1,2] } # 'a,l = 2' -> SyntaxError def lvalue_name(node): 'Construct a "magic" name to represent an l-value.' prefix = sep = '__' dot = '_' if isinstance(node, AssName): return prefix + node.name elif isinstance(node, AssAttr): name = node.attrname expr = node.expr while isinstance(expr, Getattr): name = sep.join([expr.attrname, name]) expr = expr.expr if isinstance(expr, Name): expr_name = expr.name else: expr_name = dot return prefix + sep.join([expr_name, name]) # In these trees, strings and tuples are leaves leaves = (str, tuple, AssAttr, AssName) tree_zip = partial(tree.tree_zip, leaves=leaves) flatten = partial(tree.flatten, leaves=leaves) tree_embeds = partial(tree.tree_embeds, leaves=leaves) # Grab the (right-most) lhs and the rhs lhs, rhs = node.nodes[-1], node.expr # Associate constants with l-value names if not tree_embeds(lhs, rhs): raise SyntaxError('Not enough r-values to unpack: %s' % node) zipped = flatten(tree_zip(lhs, rhs)) const_ast_for = map_keys(lambda v: lvalue_name(v), dict(zipped)) # Gather name<->const mappings for names we haven't seen before name_for = {} for name in const_ast_for.keys(): if name not in self.const_for.keys(): self.const_for[name] = eval_ast(Expression(const_ast_for[name])) assert const_ast_for[name] not in name_for name_for[const_ast_for[name]] = name class C(Transformer): def transform(self, node): if isinstance(node, Node) and node in name_for.keys(): return Name(name_for[node]) else: return super(C, self).transform(node) return Assign(node.nodes, C().transform(rhs))
def transformAssign(self, node): # Only transform constant assignments if not is_const(node.expr): return self._transform_children(node) # '__foo' names come from each lhs name at the head of the assignment: # 'l = a,b = 0,1' -> 'l = a,b = __a,__b', { '__a':0, '__b':1 } # # Unpack the rhs enough to map each '__foo' name to a value. # 'a,l = 0,[1,2]' -> 'a,l = __a,__l', { '__a':0, '__l':[1,2] } # 'a,l = 2' -> SyntaxError def lvalue_name(node): 'Construct a "magic" name to represent an l-value.' prefix = sep = '__' dot = '_' if isinstance(node, AssName): return prefix + node.name elif isinstance(node, AssAttr): name = node.attrname expr = node.expr while isinstance(expr, Getattr): name = sep.join([expr.attrname, name]) expr = expr.expr if isinstance(expr, Name): expr_name = expr.name else: expr_name = dot return prefix + sep.join([expr_name, name]) # In these trees, strings and tuples are leaves leaves = (str, tuple, AssAttr, AssName) tree_zip = partial(tree.tree_zip, leaves=leaves) flatten = partial(tree.flatten, leaves=leaves) tree_embeds = partial(tree.tree_embeds, leaves=leaves) # Grab the (right-most) lhs and the rhs lhs, rhs = node.nodes[-1], node.expr # Associate constants with l-value names if not tree_embeds(lhs, rhs): raise SyntaxError('Not enough r-values to unpack: %s' % node) zipped = flatten(tree_zip(lhs, rhs)) const_ast_for = map_keys(lambda v: lvalue_name(v), dict(zipped)) # Gather name<->const mappings for names we haven't seen before name_for = {} for name in const_ast_for.keys(): if name not in self.const_for.keys(): self.const_for[name] = eval_ast(Expression( const_ast_for[name])) assert const_ast_for[name] not in name_for name_for[const_ast_for[name]] = name class C(Transformer): def transform(self, node): if isinstance(node, Node) and node in name_for.keys(): return Name(name_for[node]) else: return super(C, self).transform(node) return Assign(node.nodes, C().transform(rhs))