def test_duplicate_node_collect(self): self.ast_description = """ file: TestTemplate #def test_function foo bar #if True #set $foo = 1 #end if baz boo #if True #set $foo = 1 #end if #end def NOTE: This test will break if collect_writes is written using ast.ASTNode.insert_before. """ ast_root, function_node = self._build_function_template() function_node.append(ast.BufferWrite(ast.LiteralNode('foo'))) function_node.append(ast.BufferWrite(ast.LiteralNode('bar'))) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) function_node.append(if_node) function_node.append(ast.BufferWrite(ast.LiteralNode('baz'))) function_node.append(ast.BufferWrite(ast.LiteralNode('boo'))) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) optimization_analyzer = self._get_analyzer(ast_root) ast_root, function_node = self._build_function_template() tuple_node = ast.TupleLiteralNode() tuple_node.append(ast.LiteralNode('foo')) tuple_node.append(ast.LiteralNode('bar')) function_node.append(ast.BufferExtend(tuple_node)) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) function_node.append(if_node) tuple_node = ast.TupleLiteralNode() tuple_node.append(ast.LiteralNode('baz')) tuple_node.append(ast.LiteralNode('boo')) function_node.append(ast.BufferExtend(tuple_node)) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) expected_hash = hash(ast_root) got_hash = hash(optimization_analyzer.optimize_ast()) self.assertEqual(expected_hash, got_hash)
def test_index_scope_ok(self): self.ast_description = """ file: TestTemplate #def test_function #set $foo = {} #if True #set $foo[1] = 1 #end if #end def """ ast_root, function_node = self._build_function_template() assign_node1 = ast.AssignNode( ast.IdentifierNode('foo'), ast.DictLiteralNode()) function_node.append(assign_node1) if_node = ast.IfNode(ast.LiteralNode(True)) assign_node2 = ast.AssignNode( ast.SliceNode( ast.IdentifierNode('foo'), ast.LiteralNode(1)), ast.LiteralNode(1)) if_node.append(assign_node2) function_node.append(if_node) optimization_analyzer = self._get_analyzer(ast_root) try: optimization_analyzer.visit_ast(ast_root) except analyzer.SemanticAnalyzerError: self.fail('visit_ast raised SemanticAnalyzerError unexpectedly.')
def test_nested_else(self): self.ast_description = """ file: TestTemplate #def test_function #if True #set $foo = 1 #else #if #set $foo = 2 #else #set $foo = 3 #end if #end if $foo #end def """ ast_root, function_node, if_node = self._build_if_template() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) if_node_2 = ast.IfNode(ast.LiteralNode(True)) if_node_2.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(2))) if_node_2.else_.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(3))) if_node.else_.append(if_node_2) function_node.append(ast.PlaceholderNode('foo')) optimization_analyzer = self._get_analyzer(ast_root) try: optimization_analyzer.visit_ast(ast_root) except analyzer.SemanticAnalyzerError: self.fail('visit_ast raised SemanticAnalyzerError unexpectedly.')
def test_partial_nested_else_if(self): self.ast_description = """ file: TestTemplate #def test_function #if True #set $foo = 1 #else #if True #set $foo = 2 #end if #end if $foo #end def """ ast_root, function_node, if_node = self._build_if_template() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) if_node_2 = ast.IfNode(ast.LiteralNode(True)) if_node_2.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(2))) if_node.else_.append(if_node_2) function_node.append(ast.PlaceholderNode('foo')) optimization_analyzer = self._get_analyzer(ast_root) self.assertRaises(analyzer.SemanticAnalyzerError, optimization_analyzer.visit_ast, ast_root)
def test_collect_writes_join_if(self): self.ast_description = """ file: TestTemplate #def test_function foo bar #if True #set $foo = 1 #end if baz boo #end def """ ast_root, function_node = self._build_function_template() function_node.append(ast.BufferWrite(ast.LiteralNode('foo'))) function_node.append(ast.BufferWrite(ast.LiteralNode('bar'))) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) function_node.append(if_node) function_node.append(ast.BufferWrite(ast.LiteralNode('baz'))) function_node.append(ast.BufferWrite(ast.LiteralNode('boo'))) optimization_analyzer = self._get_analyzer(ast_root) ast_root, function_node = self._build_function_template() tuple_node = ast.TupleLiteralNode() tuple_node.append(ast.LiteralNode('foo')) tuple_node.append(ast.LiteralNode('bar')) function_node.append(ast.BufferExtend(tuple_node)) if_node = ast.IfNode() if_node.append(ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(1))) function_node.append(if_node) tuple_node = ast.TupleLiteralNode() tuple_node.append(ast.LiteralNode('baz')) tuple_node.append(ast.LiteralNode('boo')) function_node.append(ast.BufferExtend(tuple_node)) expected_hash = hash(ast_root) got_hash = hash(optimization_analyzer.optimize_ast()) self.assertEqual(expected_hash, got_hash)
def handle_condition(self, dom_node, attr_name): expr_ast = util.parse(dom_node.getAttribute(attr_name), 'rhs_expression') node_list = [] if_node = ast.IfNode(expr_ast) node_list.append(if_node) if_node.append(self.make_tag_node(dom_node)) for n in dom_node.childNodes: if_node.extend(self.build_ast(n)) if_node.extend(self.make_tag_node(dom_node, close=True)) return node_list
def _build_if_template(self, condition=None): """ Build a simple template with a function and an if statement. file: TestTemplate #def test_function #if True #end if #end def """ ast_root, function_node = self._build_function_template() condition_node = condition or ast.LiteralNode(True) if_node = ast.IfNode(condition_node) function_node.append(if_node) return (ast_root, function_node, if_node)
def analyzeIfNode(self, pnode): # If all of the children are nodes that get ignored or there are # no nodes, throw an error. if all([isinstance(pn, _BLANK_NODES) for pn in pnode.child_nodes]): self.compiler.error( SemanticAnalyzerError("can't define an empty #if block"), pos=pnode.pos) if_node = ast.IfNode(pos=pnode.pos) if_node.else_.pos = pnode.else_.pos if_node.test_expression = self.build_ast(pnode.test_expression)[0] for pn in self.optimize_parsed_nodes(pnode.child_nodes): if_node.extend(self.build_ast(pn)) if_node.child_nodes = self.optimize_buffer_writes( if_node.child_nodes) for pn in self.optimize_parsed_nodes(pnode.else_.child_nodes): if_node.else_.extend(self.build_ast(pn)) if_node.else_.child_nodes = self.optimize_buffer_writes( if_node.else_.child_nodes) return [if_node]
def test_empty_elif_fails(self): self.ast_description = """ file: TestTemplate #def test_function #if True #set $foo = True #elif False #end if #end def """ ast_root, def_node, if_node = self._build_if_template() assign_node = ast.AssignNode( ast.IdentifierNode('foo'), ast.LiteralNode(True)) if_node.append(assign_node) elif_node = ast.IfNode(ast.LiteralNode(False)) if_node.else_.append(elif_node) semantic_analyzer = self._get_analyzer(ast_root) self.assertRaises(analyzer.SemanticAnalyzerError, semantic_analyzer.get_ast)
def make_tag_node(self, dom_node, close=False, attr_ast=None): debug("make_tag_node", dom_node) node_list = [] node_name = dom_node.nodeName if close: if self.has_child_stuff(dom_node): node_list.append(ast.TextNode(u'</%(node_name)s>' % vars())) else: attr_text = ' '.join([ '%s="%s"' % (key, value) for key, value in dom_node.attributes.items() if not key.startswith('py:') ]) # fixme: this is starting to look fugly - hard to maintain and error # prone if self.has_child_stuff(dom_node): if attr_text: if attr_ast: node_list.append( ast.TextNode(u'<%(node_name)s %(attr_text)s' % vars())) node_list.extend(attr_ast) node_list.append(ast.TextNode(u'>')) else: node_list.append( ast.TextNode(u'<%(node_name)s %(attr_text)s>' % vars())) else: if attr_ast: node_list.append( ast.TextNode(u'<%(node_name)s' % vars())) node_list.extend(attr_ast) node_list.append(ast.TextNode(u'>')) else: node_list.append( ast.TextNode(u'<%(node_name)s>' % vars())) else: if attr_text: if attr_ast: # print "XXX make_tag_node", dom_node.nodeName, attr_ast node_list.append( ast.TextNode(u'<%(node_name)s %(attr_text)s' % vars())) node_list.extend(attr_ast) node_list.append(ast.TextNode(u' />')) else: node_list.append( ast.TextNode(u'<%(node_name)s %(attr_text)s />' % vars())) else: if attr_ast: node_list.append( ast.TextNode(u'<%(node_name)s' % vars())) node_list.extend(attr_ast) node_list.append(ast.TextNode(u' />')) else: node_list.append( ast.TextNode(u'<%(node_name)s />' % vars())) omit_tag = getattr(dom_node, 'omit_tag', False) omit_tag_ast = getattr(dom_node, 'omit_tag_ast', None) if omit_tag: if omit_tag_ast: if_node = ast.IfNode(omit_tag_ast) if_node.extend(node_list) return [if_node] else: return [] return node_list
def codegenASTFunctionNode(self, node): name = node.name node.uses_globals = False node.uses_filter_function = False node.uses_private_filter_function = False node.uses_buffer_write = False node.uses_buffer_extend = False self.function_stack.append(node) if node.parameter_list: parameter_list = self.generate_python( self.build_code(node.parameter_list)[0]) else: parameter_list = '' decorator_node = CodeNode('@template_method') # NOTE: for Cheetah compatibility, we have to handle the case where # Cheetah tries to pass a 'transaction' object through. hopefully this # doesn't have some other baggage coming with it. if self.options and self.options.cheetah_compatibility: if parameter_list: code_node = CodeNode( 'def %(name)s(%(parameter_list)s, **kargs):' % vars(), input_pos=node.pos) else: code_node = CodeNode('def %(name)s(**kargs):' % vars(), input_pos=node.pos) else: code_node = CodeNode('def %(name)s(%(parameter_list)s):' % vars(), input_pos=node.pos) needs_globals_added = True child_nodes = node.child_nodes if self.options and self.options.cheetah_compatibility: if_cheetah = CodeNode("if 'trans' in kargs:") code_node.append(if_cheetah) if_cheetah.append(CodeNode("_buffer = kargs['trans'].response()")) else_spitfire = CodeNode('else:') else_spitfire.append(CodeNode('_buffer = self.new_buffer()')) code_node.append(else_spitfire) else: # If the first node is an if statement with no else AND there's no # other statements, generate the if code first. That way, we can # avoid doing extra work when the condition is false. ie, avoid the # overhead of creating a new list setting up useless local variables # and joining all to get an empty string. Disable this in baked mode # until I figure out how to handle this. TODO: Do not perform # sanitization inside of a test_expression. Then we can remove this # baked_mode check. if child_nodes and len(child_nodes) == 1 and not self.baked_mode: if_node = child_nodes[0] if isinstance(if_node, ast.IfNode) and not if_node.else_.child_nodes: child_nodes = if_node.child_nodes # Insert code that does: # if not ($test_expression): # return '' new_if_condition = ast.IfNode( ast.UnaryOpNode('not', if_node.test_expression)) new_if_condition.append(ast.ReturnNode( ast.LiteralNode(''))) new_code = self.build_code(new_if_condition) if node.uses_globals: needs_globals_added = False code_node.append(CodeNode('_globals = globals()')) code_node.extend(new_code) code_node.append(CodeNode('_buffer = self.new_buffer()')) # Save the point where _globals and self_filter_funtion will go if used. # We don't append these here because we have to determine if these two # functions are used in the scope of the current ast.FunctionNode. insertion_point = len(code_node.child_nodes) if self.options and self.options.cheetah_cheats: node.uses_globals = True if_cheetah = CodeNode('if self.search_list:') if_cheetah.append( CodeNode('_self_search_list = self.search_list + [_globals]')) else_cheetah = CodeNode('else:') else_cheetah.append(CodeNode('_self_search_list = [_globals]')) code_node.append(if_cheetah) code_node.append(else_cheetah) for n in child_nodes: code_child_nodes = self.build_code(n) code_node.extend(code_child_nodes) if node.uses_globals and needs_globals_added: code_node.insert(insertion_point, CodeNode('_globals = globals()')) insertion_point += 1 if node.uses_filter_function: code_node.insert( insertion_point, CodeNode('_self_filter_function = self.filter_function')) insertion_point += 1 if node.uses_private_filter_function: code_node.insert( insertion_point, CodeNode( '_self_private_filter_function = self._filter_function')) insertion_point += 1 if node.uses_buffer_write: code_node.insert(insertion_point, CodeNode('_buffer_write = _buffer.write')) insertion_point += 1 if node.uses_buffer_extend: code_node.insert(insertion_point, CodeNode('_buffer_extend = _buffer.extend')) if self.options.cheetah_compatibility: if_cheetah = CodeNode("if 'trans' not in kargs:") if_cheetah.append(CodeNode('return _buffer.getvalue()')) code_node.append(if_cheetah) else: code_node.append(CodeNode('return _buffer.getvalue()')) self.function_stack.pop() return [decorator_node, code_node]