Skip to content

Commit 8606516

Browse files
cocolatozzzeek
authored andcommitted
Support the direct passing of dictionary literals.
Support the direct passing of dictionary literals when calling functions and fix the errors caused by nested braces. This revises the fix that was released in 1.3.4 and then reverted in 1.3.5. Pull request by Hai Zhu and Jose Galvez. Fixes: #400 Fixes: #401 Closes: #414 Pull-request: #414 Pull-request-sha: c6aa6be Change-Id: If51a7d0847552191ca2a919e834ed5040e3a6666
1 parent ee988d2 commit 8606516

File tree

3 files changed

+230
-6
lines changed

3 files changed

+230
-6
lines changed

doc/build/unreleased/400.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: 400, 401
4+
5+
Support the direct passing of dictionary literals when calling functions
6+
and fix the errors caused by nested braces. This revises the fix that was
7+
released in 1.3.4 and then reverted in 1.3.5. Pull request by Hai Zhu and
8+
Jose Galvez.

mako/parsetree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def _parse_attributes(self, expressions, nonexpressions):
322322
for key in self.attributes:
323323
if key in expressions:
324324
expr = []
325-
for x in re.compile(r"(\${.+?})", re.S).split(
325+
for x in re.compile(r"(\${(?:[^$]*?{.+|.+?)})", re.S).split(
326326
self.attributes[key]
327327
):
328328
m = re.compile(r"^\${(.+?)}$", re.S).match(x)

test/test_lexer.py

Lines changed: 221 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -810,20 +810,29 @@ def test_tricky_expression(self):
810810
),
811811
)
812812

813-
def test_dict_expression_issue_400_regression(self):
813+
@pytest.mark.parametrize(
814+
"expr",
815+
[
816+
("${foo}${bar}",),
817+
("file_${foo}_bat_${bar}.py",),
818+
("${foo}_bat_${bar}",),
819+
("${foo}${bar}.py",),
820+
],
821+
)
822+
def test_dict_expression_issue_400_regression(self, expr):
814823
"""test for issue #401.
815824
816825
This was the regression case for #400
817826
818827
"""
819-
template = '<%include file="${foo}${bar}"/>'
828+
template = f'<%include file="{expr}"/>'
820829

821830
nodes = Lexer(template).parse()
822831
self._compare(
823832
nodes,
824833
TemplateNode(
825834
{},
826-
[IncludeTag("include", {"file": "${foo}${bar}"}, (1, 1), [])],
835+
[IncludeTag("include", {"file": f"{expr}"}, (1, 1), [])],
827836
),
828837
)
829838

@@ -838,7 +847,7 @@ def test_ampersand_issue_412(self):
838847
TemplateNode({}, [Text("\nproperty = <&node>;\n\n", (1, 1))]),
839848
)
840849

841-
def _dont_test_dict_expression_issue_400(self):
850+
def test_dict_expression_issue_400(self):
842851
"""test for issue #400"""
843852
template = """
844853
<%def name="dtest(d)">
@@ -897,7 +906,7 @@ def _dont_test_dict_expression_issue_400(self):
897906
),
898907
)
899908

900-
def _dont_test_dict_expression_2_issue_400(self):
909+
def test_dict_expression_2_issue_400(self):
901910
"""test for issue #400"""
902911
template = """
903912
<%def name="thing(thing)">
@@ -1006,6 +1015,213 @@ def _dont_test_dict_expression_2_issue_400(self):
10061015
),
10071016
)
10081017

1018+
def test_dict_expression(self):
1019+
template = """
1020+
<%def name="dtest(d)">
1021+
% for k,v in d.items():
1022+
${k} = ${v}
1023+
% endfor
1024+
</%def>
1025+
<%self:dtest d="${
1026+
{
1027+
'id':'4',
1028+
'foo':'barr'
1029+
}
1030+
}" />
1031+
"""
1032+
nodes = Lexer(template).parse()
1033+
self._compare(
1034+
nodes,
1035+
TemplateNode(
1036+
{},
1037+
[
1038+
Text("\n ", (1, 1)),
1039+
DefTag(
1040+
"def",
1041+
{"name": "dtest(d)"},
1042+
(2, 9),
1043+
[
1044+
Text("\n", (2, 31)),
1045+
ControlLine(
1046+
"for", "for k,v in d.items():", False, (3, 1)
1047+
),
1048+
Text(" ", (4, 1)),
1049+
Expression("k", [], (4, 13)),
1050+
Text(" = ", (4, 17)),
1051+
Expression("v", [], (4, 20)),
1052+
Text("\n", (4, 24)),
1053+
ControlLine("for", "endfor", True, (5, 1)),
1054+
Text(" ", (6, 1)),
1055+
],
1056+
),
1057+
Text("\n ", (6, 16)),
1058+
CallNamespaceTag(
1059+
"self:dtest",
1060+
{
1061+
"d": "${\n\
1062+
{\n\
1063+
'id':'4',\n\
1064+
'foo':'barr'\n\
1065+
}\n\
1066+
}"
1067+
},
1068+
(7, 9),
1069+
[],
1070+
),
1071+
Text("\n ", (12, 30)),
1072+
],
1073+
),
1074+
)
1075+
1076+
def test_dict_expression_2(self):
1077+
template = """
1078+
<%def name="thing(thing)">
1079+
${type(thing)}
1080+
</%def>
1081+
<%self:thing thing="foo" />
1082+
<%self:thing thing="${5}" />
1083+
<%self:thing thing="${[1,2,3]}" />
1084+
<%self:thing thing="${{'id':'4'}}" />
1085+
<%
1086+
foo="this is foo"
1087+
g=False
1088+
%>
1089+
<%def name="bar(x, y)">
1090+
${x} ${y}
1091+
</%def>
1092+
<%self:bar x="${{'id':4}}" y="x${g and '1' or '2'}y"/>
1093+
<%def name="dtest(d)">
1094+
% for k,v in d.items():
1095+
${k} = ${v}
1096+
% endfor
1097+
% if 'embeded' in d and 'name' in d['embeded']:
1098+
${d['embeded']['name']}
1099+
% endif
1100+
</%def>
1101+
<%self:dtest d="${ {
1102+
'x-on:click':'foo',
1103+
'foo':'bar',
1104+
'embeded':{'name':'J Doe'}
1105+
} }" />
1106+
"""
1107+
nodes = Lexer(template).parse()
1108+
self._compare(
1109+
nodes,
1110+
TemplateNode(
1111+
{},
1112+
[
1113+
Text("\n ", (1, 1)),
1114+
DefTag(
1115+
"def",
1116+
{"name": "thing(thing)"},
1117+
(2, 9),
1118+
[
1119+
Text("\n ", (2, 35)),
1120+
Expression("type(thing)", [], (3, 13)),
1121+
Text("\n ", (3, 27)),
1122+
],
1123+
),
1124+
Text("\n ", (4, 16)),
1125+
CallNamespaceTag(
1126+
"self:thing", {"thing": "foo"}, (5, 9), []
1127+
),
1128+
Text("\n ", (5, 36)),
1129+
CallNamespaceTag(
1130+
"self:thing", {"thing": "${5}"}, (6, 9), []
1131+
),
1132+
Text("\n ", (6, 37)),
1133+
CallNamespaceTag(
1134+
"self:thing", {"thing": "${[1,2,3]}"}, (7, 9), []
1135+
),
1136+
Text("\n ", (7, 43)),
1137+
CallNamespaceTag(
1138+
"self:thing", {"thing": "${{'id':'4'}}"}, (8, 9), []
1139+
),
1140+
Text("\n ", (8, 46)),
1141+
Code(
1142+
'\nfoo="this is foo"\ng=False\n \n',
1143+
False,
1144+
(9, 9),
1145+
),
1146+
Text("\n ", (12, 11)),
1147+
DefTag(
1148+
"def",
1149+
{"name": "bar(x, y)"},
1150+
(13, 9),
1151+
[
1152+
Text("\n ", (13, 32)),
1153+
Expression("x", [], (14, 13)),
1154+
Text(" ", (14, 17)),
1155+
Expression("y", [], (14, 18)),
1156+
Text("\n ", (14, 22)),
1157+
],
1158+
),
1159+
Text("\n ", (15, 16)),
1160+
CallNamespaceTag(
1161+
"self:bar",
1162+
{"x": "${{'id':4}}", "y": "x${g and '1' or '2'}y"},
1163+
(16, 9),
1164+
[],
1165+
),
1166+
Text("\n ", (16, 63)),
1167+
DefTag(
1168+
"def",
1169+
{"name": "dtest(d)"},
1170+
(17, 9),
1171+
[
1172+
Text("\n", (17, 31)),
1173+
ControlLine(
1174+
"for", "for k,v in d.items():", False, (18, 1)
1175+
),
1176+
Text(" ", (19, 1)),
1177+
Expression("k", [], (19, 9)),
1178+
Text(" = ", (19, 13)),
1179+
Expression("v", [], (19, 16)),
1180+
Text("\n", (19, 20)),
1181+
ControlLine("for", "endfor", True, (20, 1)),
1182+
ControlLine(
1183+
"if",
1184+
"if 'embeded' in d and \
1185+
'name' in d['embeded']:",
1186+
False,
1187+
(21, 1),
1188+
),
1189+
Text(" ", (22, 1)),
1190+
Expression("d['embeded']['name']", [], (22, 9)),
1191+
Text("\n", (22, 32)),
1192+
ControlLine("if", "endif", True, (23, 1)),
1193+
Text(" ", (24, 1)),
1194+
],
1195+
),
1196+
Text("\n ", (24, 16)),
1197+
CallNamespaceTag(
1198+
"self:dtest",
1199+
{
1200+
"d": "${ {\n\
1201+
'x-on:click':'foo',\n\
1202+
'foo':'bar',\n\
1203+
'embeded':{'name':'J Doe'}\n\
1204+
} }"
1205+
},
1206+
(25, 9),
1207+
[],
1208+
),
1209+
Text("\n ", (29, 16)),
1210+
],
1211+
),
1212+
)
1213+
1214+
def test_brace_expression(self):
1215+
template = '<%include file="${foo}${bar}"/>'
1216+
nodes = Lexer(template).parse()
1217+
self._compare(
1218+
nodes,
1219+
TemplateNode(
1220+
{},
1221+
[IncludeTag("include", {"file": "${foo}${bar}"}, (1, 1), [])],
1222+
),
1223+
)
1224+
10091225
def test_tricky_code(self):
10101226
template = """<% print('hi %>') %>"""
10111227
nodes = Lexer(template).parse()

0 commit comments

Comments
 (0)