changeset: 559:18d9733eec5a tag: tip user: Benjamin Trofatter date: Mon Apr 02 02:21:42 2012 -0500 files: mako/codegen.py mako/lexer.py test/test_template.py description: Fixed ticket #146 - Auto-insertion of pass statements when control blocks are left empty. Added ternary_stack to the lexer to keep track of ternary control lines and their children so that codegen can determine if the block is empty at template module write time. Added tests to confirm that passes are being inserted where needed. diff -r 6b158a590740 -r 18d9733eec5a mako/codegen.py --- a/mako/codegen.py Fri Mar 30 20:05:58 2012 -0400 +++ b/mako/codegen.py Mon Apr 02 02:21:42 2012 -0500 @@ -765,8 +765,6 @@ def visitControlLine(self, node): if node.isend: - if not node.get_children(): - self.printer.writeline("pass") self.printer.writeline(None) if node.has_loop_context: self.printer.writeline('finally:') @@ -779,6 +777,15 @@ else: text = node.text self.printer.writeline(text) + children = node.get_children() + # this covers the two situations where we want to insert a pass: + # 1) a ternary control line with no children and + # 2) a primary control line with nothing but its own ternary + # and end control lines + if (not node.get_children or all( + isinstance(c, parsetree.ControlLine) for c in children) and + all((node.is_ternary(c.keyword) or c.isend) for c in children)): + self.printer.writeline("pass") def visitText(self, node): self.write_source_comment(node) diff -r 6b158a590740 -r 18d9733eec5a mako/lexer.py --- a/mako/lexer.py Fri Mar 30 20:05:58 2012 -0400 +++ b/mako/lexer.py Mon Apr 02 02:21:42 2012 -0500 @@ -25,6 +25,7 @@ self.match_position = 0 self.tag = [] self.control_line = [] + self.ternary_stack = [] self.disable_unicode = disable_unicode self.encoding = input_encoding @@ -134,8 +135,15 @@ self.template.nodes.append(node) # build a set of child nodes for the control line # (used for loop variable detection) + # also build a set of child nodes on ternary control lines + # (used for determining if a pass needs to be auto-inserted if self.control_line: - self.control_line[-1].nodes.append(node) + control_frame = self.control_line[-1] + control_frame.nodes.append(node) + if not (isinstance(node, parsetree.ControlLine) and + control_frame.is_ternary(node.keyword)): + if self.ternary_stack and self.ternary_stack[-1]: + self.ternary_stack[-1][-1].nodes.append(node) if isinstance(node, parsetree.Tag): if len(self.tag): node.parent = self.tag[-1] @@ -143,9 +151,14 @@ elif isinstance(node, parsetree.ControlLine): if node.isend: self.control_line.pop() + self.ternary_stack.pop() elif node.is_primary: self.control_line.append(node) - elif len(self.control_line) and \ + self.ternary_stack.append([]) + elif self.control_line and \ + self.control_line[-1].is_ternary(node.keyword): + self.ternary_stack[-1].append(node) + elif self.control_line and \ not self.control_line[-1].is_ternary(node.keyword): raise exceptions.SyntaxException( "Keyword '%s' not a legal ternary for keyword '%s'" % diff -r 6b158a590740 -r 18d9733eec5a test/test_template.py --- a/test/test_template.py Fri Mar 30 20:05:58 2012 -0400 +++ b/test/test_template.py Mon Apr 02 02:21:42 2012 -0500 @@ -798,7 +798,7 @@ "yes x has test" ] - def test_blank_control(self): + def test_blank_control_1(self): self._do_memory_test( """ % if True: @@ -808,6 +808,81 @@ filters=lambda s:s.strip() ) + def test_blank_control_2(self): + self._do_memory_test( + """ + % if True: + % elif True: + % endif + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_3(self): + self._do_memory_test( + """ + % if True: + % else: + % endif + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_4(self): + self._do_memory_test( + """ + % if True: + % elif True: + % else: + % endif + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_5(self): + self._do_memory_test( + """ + % for x in range(10): + % endfor + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_6(self): + self._do_memory_test( + """ + % while False: + % endwhile + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_7(self): + self._do_memory_test( + """ + % try: + % except: + % endtry + """, + "", + filters=lambda s:s.strip() + ) + + def test_blank_control_8(self): + self._do_memory_test( + """ + % with open('x', 'w') as fp: + % endwith + """, + "", + filters=lambda s:s.strip() + ) + def test_multiline_control(self): t = Template(""" % for x in \\