Skip to content

Commit 2031330

Browse files
cocolatozzzeek
authored andcommitted
fix percent escape not working when not at the beginning of the line
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. Fixes: #323 Closes: #383 Pull-request: #383 Pull-request-sha: ab8e747 Change-Id: I3c494222443320e3681758e3892f44cf7748fe5f
1 parent dc66614 commit 2031330

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

doc/build/unreleased/323.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.. change::
2+
:tags: bug, lexer
3+
:tickets: 323
4+
5+
Fixed parsing issue where attempting to render a single percent sign ``%``
6+
using an escaped percent ``%%`` would not function correctly if the escaped
7+
percent were not the first character on a line. Pull request courtesy Hai
8+
Zhu.

mako/lexer.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,12 @@ def match_text(self):
357357
r"""
358358
(.*?) # anything, followed by:
359359
(
360-
(?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based
361-
# comment preceded by a
362-
# consumed newline and whitespace
360+
(?<=\n)(?=[ \t]*(?=%(?!%)|\#\#)) # an eval or line-based
361+
# comment, preceded by a
362+
# consumed newline and whitespace
363+
|
364+
(?<!%)(?=%%+) # consume the first percent sign
365+
# out of a group of percent signs
363366
|
364367
(?=\${) # an expression
365368
|

mako/testing/helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def result_lines(result):
1919
]
2020

2121

22+
def result_raw_lines(result):
23+
return [x for x in re.split(r"\r?\n", result) if x.strip() != ""]
24+
25+
2226
def make_path(
2327
filespec: Union[Path, str],
2428
make_absolute: bool = True,

test/test_lexer.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,59 @@ def test_percent_escape(self):
201201
{},
202202
[
203203
Text("""\n\n""", (1, 1)),
204-
Text("""% some whatever.\n\n""", (3, 2)),
205-
Text(" %% more some whatever\n", (5, 2)),
204+
Text("""% some whatever.\n\n """, (3, 2)),
205+
Text("% more some whatever\n", (5, 6)),
206206
ControlLine("if", "if foo:", False, (6, 1)),
207207
ControlLine("if", "endif", True, (7, 1)),
208208
Text(" ", (8, 1)),
209209
],
210210
),
211211
)
212212

213+
def test_percent_escape2(self):
214+
template = """%% do something
215+
%%% do something
216+
if <some condition>:
217+
%%%% do something
218+
"""
219+
node = Lexer(template).parse()
220+
self._compare(
221+
node,
222+
TemplateNode(
223+
{},
224+
[
225+
Text("% do something\n", (1, 2)),
226+
Text(
227+
"%% do something\nif <some condition>:\n ", (2, 2)
228+
),
229+
Text("%%% do something\n ", (4, 6)),
230+
],
231+
),
232+
)
233+
234+
def test_percent_escape_with_control_block(self):
235+
template = """
236+
% for i in [1, 2, 3]:
237+
%% do something ${i}
238+
% endfor
239+
"""
240+
node = Lexer(template).parse()
241+
self._compare(
242+
node,
243+
TemplateNode(
244+
{},
245+
[
246+
Text("\n", (1, 1)),
247+
ControlLine("for", "for i in [1, 2, 3]:", False, (2, 1)),
248+
Text(" ", (3, 1)),
249+
Text("% do something ", (3, 6)),
250+
Expression("i", [], (3, 21)),
251+
Text("\n", (3, 25)),
252+
ControlLine("for", "endfor", True, (4, 1)),
253+
],
254+
),
255+
)
256+
213257
def test_old_multiline_comment(self):
214258
template = """#*"""
215259
node = Lexer(template).parse()

test/test_template.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from mako.testing.fixtures import TemplateTest
1616
from mako.testing.helpers import flatten_result
1717
from mako.testing.helpers import result_lines
18+
from mako.testing.helpers import result_raw_lines
1819

1920

2021
class ctx:
@@ -1667,3 +1668,34 @@ class FuturesTest(TemplateTest):
16671668
def test_future_import(self):
16681669
t = Template("${ x / y }", future_imports=["division"])
16691670
assert result_lines(t.render(x=12, y=5)) == ["2.4"]
1671+
1672+
1673+
class EscapeTest(TemplateTest):
1674+
def test_percent_escape(self):
1675+
t = Template(
1676+
"""%% do something
1677+
%%% do something
1678+
if <some condition>:
1679+
%%%% do something
1680+
"""
1681+
)
1682+
assert result_raw_lines(t.render()) == [
1683+
"% do something",
1684+
"%% do something",
1685+
"if <some condition>:",
1686+
" %%% do something",
1687+
]
1688+
1689+
def test_percent_escape2(self):
1690+
t = Template(
1691+
"""
1692+
% for i in [1, 2, 3]:
1693+
%% do something ${i}
1694+
% endfor
1695+
"""
1696+
)
1697+
assert result_raw_lines(t.render()) == [
1698+
" % do something 1",
1699+
" % do something 2",
1700+
" % do something 3",
1701+
]

0 commit comments

Comments
 (0)