templater: complain about invalid application of '%' operator (BC)
Before, '{x % y % z ...}' was silently evaluated as '{x % y}'. We no longer
need this hack since the web template bugs was fixed by earlier patches.
At this point, the error message may contain '<generator *>', which will
be fixed later.
--- a/mercurial/templateutil.py Sun Mar 18 21:18:57 2018 +0900
+++ b/mercurial/templateutil.py Sun Mar 18 21:01:23 2018 +0900
@@ -567,6 +567,20 @@
return (_("template filter '%s' is not compatible with keyword '%s'")
% (fn, sym))
+def _checkeditermaps(darg, d):
+ try:
+ for v in d:
+ if not isinstance(v, dict):
+ raise TypeError
+ yield v
+ except TypeError:
+ sym = findsymbolicname(darg)
+ if sym:
+ raise error.ParseError(_("keyword '%s' is not iterable of mappings")
+ % sym)
+ else:
+ raise error.ParseError(_("%r is not iterable of mappings") % d)
+
def _iteroverlaymaps(context, origmapping, newmappings):
"""Generate combined mappings from the original mapping and an iterable
of partial mappings to override the original"""
@@ -578,28 +592,17 @@
def runmap(context, mapping, data):
darg, targ = data
d = evalrawexp(context, mapping, darg)
+ # TODO: a generator should be rejected because it is a thunk of lazy
+ # string, but we can't because hgweb abuses generator as a keyword
+ # that returns a list of dicts.
if isinstance(d, wrapped):
diter = d.itermaps(context)
else:
- try:
- diter = iter(d)
- except TypeError:
- sym = findsymbolicname(darg)
- if sym:
- raise error.ParseError(_("keyword '%s' is not iterable") % sym)
- else:
- raise error.ParseError(_("%r is not iterable") % d)
-
+ diter = _checkeditermaps(darg, d)
for i, v in enumerate(diter):
- if isinstance(v, dict):
- lm = context.overlaymap(mapping, v)
- lm['index'] = i
- yield evalrawexp(context, lm, targ)
- else:
- # v is not an iterable of dicts, this happen when 'key'
- # has been fully expanded already and format is useless.
- # If so, return the expanded value.
- yield v
+ lm = context.overlaymap(mapping, v)
+ lm['index'] = i
+ yield evalrawexp(context, lm, targ)
def runmember(context, mapping, data):
darg, memb = data
--- a/tests/test-command-template.t Sun Mar 18 21:18:57 2018 +0900
+++ b/tests/test-command-template.t Sun Mar 18 21:01:23 2018 +0900
@@ -3210,10 +3210,13 @@
$ hg log -R latesttag -r tip -T '{rev % "a"}\n'
- hg: parse error: keyword 'rev' is not iterable
+ hg: parse error: keyword 'rev' is not iterable of mappings
[255]
$ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
- hg: parse error: None is not iterable
+ hg: parse error: None is not iterable of mappings
+ [255]
+ $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
+ hg: parse error: <generator *> is not iterable of mappings (glob)
[255]
Test new-style inline templating of non-list/dict type:
@@ -3228,7 +3231,7 @@
$ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
branch: default
$ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
- hg: parse error: None is not iterable
+ hg: parse error: None is not iterable of mappings
[255]
$ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
branch: default