Mercurial > hg
changeset 34425:12bfecd0ffe6
formatter: fix default list/dict generator to be evaluated more than once
Before, _hybrid.gen must be a generator which could be consumed only once.
It was okay in templatekw.py since template keywords are functions which
create temporary hybrid objects, but the formatter doesn't work in that way.
To work around the issue, this patch makes _hybrid.gen optionally be a
function returning a generator.
Thanks to Pulkit for finding this issue.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 01 Oct 2017 08:37:04 +0100 |
parents | e416819d9ebb |
children | ae2fcf7af409 |
files | mercurial/formatter.py mercurial/templatekw.py tests/test-obsolete.t |
diffstat | 3 files changed, 15 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/formatter.py Wed Sep 27 21:38:48 2017 +0900 +++ b/mercurial/formatter.py Sun Oct 01 08:37:04 2017 +0100 @@ -348,15 +348,14 @@ data = util.sortdict(_iteritems(data)) def f(): yield _plainconverter.formatdict(data, key, value, fmt, sep) - return templatekw.hybriddict(data, key=key, value=value, fmt=fmt, - gen=f()) + return templatekw.hybriddict(data, key=key, value=value, fmt=fmt, gen=f) @staticmethod def formatlist(data, name, fmt, sep): '''build object that can be evaluated as either plain string or list''' data = list(data) def f(): yield _plainconverter.formatlist(data, name, fmt, sep) - return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f()) + return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f) class templateformatter(baseformatter): def __init__(self, ui, out, topic, opts):
--- a/mercurial/templatekw.py Wed Sep 27 21:38:48 2017 +0900 +++ b/mercurial/templatekw.py Sun Oct 01 08:37:04 2017 +0100 @@ -39,15 +39,12 @@ def __init__(self, gen, values, makemap, joinfmt): if gen is not None: - self.gen = gen + self.gen = gen # generator or function returning generator self._values = values self._makemap = makemap self.joinfmt = joinfmt - @util.propertycache def gen(self): - return self._defaultgen() - def _defaultgen(self): - """Generator to stringify this as {join(self, ' ')}""" + """Default generator to stringify this as {join(self, ' ')}""" for i, x in enumerate(self._values): if i > 0: yield ' ' @@ -104,9 +101,12 @@ def unwraphybrid(thing): """Return an object which can be stringified possibly by using a legacy template""" - if not util.safehasattr(thing, 'gen'): + gen = getattr(thing, 'gen', None) + if gen is None: return thing - return thing.gen + if callable(gen): + return gen() + return gen def unwrapvalue(thing): """Move the inner value object out of the wrapper""" @@ -685,7 +685,7 @@ # Format the successorssets def render(d): t = [] - for i in d.gen: + for i in d.gen(): t.append(i) return "".join(t)
--- a/tests/test-obsolete.t Wed Sep 27 21:38:48 2017 +0900 +++ b/tests/test-obsolete.t Sun Oct 01 08:37:04 2017 +0100 @@ -760,8 +760,12 @@ 3de5eca88c00 ????-??-?? (glob) $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n' user=test <test@example.net> - $ hg debugobsolete -r6 -T '{metadata}\n' + $ hg debugobsolete -r6 -T '{metadata}\n{metadata}\n' + 'user': 'test <test@example.net>' 'user': 'test <test@example.net>' + $ hg debugobsolete -r6 -T '{succnodes}\n{succnodes}\n' + 3de5eca88c00aa039da7399a220f4a5221faa585 + 3de5eca88c00aa039da7399a220f4a5221faa585 $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n' 0 test <test@example.net>