diff -r 448f7ec247e2 -r 7c902a8345ef mercurial/templateutil.py --- 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