diff --git a/doc/build/unreleased/323.rst b/doc/build/unreleased/323.rst new file mode 100644 index 00000000..8f23d8be --- /dev/null +++ b/doc/build/unreleased/323.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, lexer + :tickets: 323 + + Fixed parsing issue where attempting to render a single percent sign ``%`` + using an escaped percent ``%%`` would not function correctly if the escaped + percent were not the first character on a line. Pull request courtesy Hai + Zhu. diff --git a/mako/lexer.py b/mako/lexer.py index 34f17dc9..8ce3fd6e 100644 --- a/mako/lexer.py +++ b/mako/lexer.py @@ -357,9 +357,12 @@ def match_text(self): r""" (.*?) # anything, followed by: ( - (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based - # comment preceded by a - # consumed newline and whitespace + (?<=\n)(?=[ \t]*(?=%(?!%)|\#\#)) # an eval or line-based + # comment, preceded by a + # consumed newline and whitespace + | + (?: + %%%% do something + """ + node = Lexer(template).parse() + self._compare( + node, + TemplateNode( + {}, + [ + Text("% do something\n", (1, 2)), + Text( + "%% do something\nif :\n ", (2, 2) + ), + Text("%%% do something\n ", (4, 6)), + ], + ), + ) + + def test_percent_escape_with_control_block(self): + template = """ +% for i in [1, 2, 3]: + %% do something ${i} +% endfor +""" + node = Lexer(template).parse() + self._compare( + node, + TemplateNode( + {}, + [ + Text("\n", (1, 1)), + ControlLine("for", "for i in [1, 2, 3]:", False, (2, 1)), + Text(" ", (3, 1)), + Text("% do something ", (3, 6)), + Expression("i", [], (3, 21)), + Text("\n", (3, 25)), + ControlLine("for", "endfor", True, (4, 1)), + ], + ), + ) + def test_old_multiline_comment(self): template = """#*""" node = Lexer(template).parse() diff --git a/test/test_template.py b/test/test_template.py index 62fd21d6..79017f7b 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -15,6 +15,7 @@ from mako.testing.fixtures import TemplateTest from mako.testing.helpers import flatten_result from mako.testing.helpers import result_lines +from mako.testing.helpers import result_raw_lines class ctx: @@ -1667,3 +1668,34 @@ class FuturesTest(TemplateTest): def test_future_import(self): t = Template("${ x / y }", future_imports=["division"]) assert result_lines(t.render(x=12, y=5)) == ["2.4"] + + +class EscapeTest(TemplateTest): + def test_percent_escape(self): + t = Template( + """%% do something +%%% do something +if : + %%%% do something +""" + ) + assert result_raw_lines(t.render()) == [ + "% do something", + "%% do something", + "if :", + " %%% do something", + ] + + def test_percent_escape2(self): + t = Template( + """ +% for i in [1, 2, 3]: + %% do something ${i} +% endfor +""" + ) + assert result_raw_lines(t.render()) == [ + " % do something 1", + " % do something 2", + " % do something 3", + ]