comparison mercurial/templatekw.py @ 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 89aec1834a86
children b3073e175c17
comparison
equal deleted inserted replaced
34424:e416819d9ebb 34425:12bfecd0ffe6
37 - "{files|json}" 37 - "{files|json}"
38 """ 38 """
39 39
40 def __init__(self, gen, values, makemap, joinfmt): 40 def __init__(self, gen, values, makemap, joinfmt):
41 if gen is not None: 41 if gen is not None:
42 self.gen = gen 42 self.gen = gen # generator or function returning generator
43 self._values = values 43 self._values = values
44 self._makemap = makemap 44 self._makemap = makemap
45 self.joinfmt = joinfmt 45 self.joinfmt = joinfmt
46 @util.propertycache
47 def gen(self): 46 def gen(self):
48 return self._defaultgen() 47 """Default generator to stringify this as {join(self, ' ')}"""
49 def _defaultgen(self):
50 """Generator to stringify this as {join(self, ' ')}"""
51 for i, x in enumerate(self._values): 48 for i, x in enumerate(self._values):
52 if i > 0: 49 if i > 0:
53 yield ' ' 50 yield ' '
54 yield self.joinfmt(x) 51 yield self.joinfmt(x)
55 def itermaps(self): 52 def itermaps(self):
102 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x) 99 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
103 100
104 def unwraphybrid(thing): 101 def unwraphybrid(thing):
105 """Return an object which can be stringified possibly by using a legacy 102 """Return an object which can be stringified possibly by using a legacy
106 template""" 103 template"""
107 if not util.safehasattr(thing, 'gen'): 104 gen = getattr(thing, 'gen', None)
105 if gen is None:
108 return thing 106 return thing
109 return thing.gen 107 if callable(gen):
108 return gen()
109 return gen
110 110
111 def unwrapvalue(thing): 111 def unwrapvalue(thing):
112 """Move the inner value object out of the wrapper""" 112 """Move the inner value object out of the wrapper"""
113 if not util.safehasattr(thing, '_value'): 113 if not util.safehasattr(thing, '_value'):
114 return thing 114 return thing
683 data.append(h) 683 data.append(h)
684 684
685 # Format the successorssets 685 # Format the successorssets
686 def render(d): 686 def render(d):
687 t = [] 687 t = []
688 for i in d.gen: 688 for i in d.gen():
689 t.append(i) 689 t.append(i)
690 return "".join(t) 690 return "".join(t)
691 691
692 def gen(data): 692 def gen(data):
693 yield "; ".join(render(d) for d in data) 693 yield "; ".join(render(d) for d in data)