##// END OF EJS Templates
templater: move hybrid class and functions to templateutil module...
Yuya Nishihara -
r36939:32f9b7e3 default
parent child Browse files
Show More
@@ -143,7 +143,7 b' from mercurial import ('
143 143 registrar,
144 144 revlog,
145 145 scmutil,
146 templatekw,
146 templateutil,
147 147 upgrade,
148 148 util,
149 149 vfs as vfsmod,
@@ -375,12 +375,12 b' def lfsfiles(context, mapping):'
375 375 makemap = lambda v: {
376 376 'file': v,
377 377 'lfsoid': pointers[v].oid() if pointers[v] else None,
378 'lfspointer': templatekw.hybriddict(pointer(v)),
378 'lfspointer': templateutil.hybriddict(pointer(v)),
379 379 }
380 380
381 381 # TODO: make the separator ', '?
382 f = templatekw._showlist('lfs_file', files, templ, mapping)
383 return templatekw._hybrid(f, files, makemap, pycompat.identity)
382 f = templateutil._showlist('lfs_file', files, templ, mapping)
383 return templateutil.hybrid(f, files, makemap, pycompat.identity)
384 384
385 385 @command('debuglfsupload',
386 386 [('r', 'rev', [], _('upload large files introduced by REV'))])
@@ -35,7 +35,7 b' from mercurial import ('
35 35 registrar,
36 36 revsetlang,
37 37 smartset,
38 templatekw,
38 templateutil,
39 39 )
40 40
41 41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
@@ -237,7 +237,7 b' def remotenameskw(context, mapping):'
237 237 if 'remotebranches' in repo.names:
238 238 remotenames += repo.names['remotebranches'].names(repo, ctx.node())
239 239
240 return templatekw.compatlist(context, mapping, 'remotename', remotenames,
240 return templateutil.compatlist(context, mapping, 'remotename', remotenames,
241 241 plural='remotenames')
242 242
243 243 @templatekeyword('remotebookmarks', requires={'repo', 'ctx', 'templ'})
@@ -250,7 +250,7 b' def remotebookmarkskw(context, mapping):'
250 250 if 'remotebookmarks' in repo.names:
251 251 remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
252 252
253 return templatekw.compatlist(context, mapping, 'remotebookmark',
253 return templateutil.compatlist(context, mapping, 'remotebookmark',
254 254 remotebmarks, plural='remotebookmarks')
255 255
256 256 @templatekeyword('remotebranches', requires={'repo', 'ctx', 'templ'})
@@ -263,7 +263,7 b' def remotebrancheskw(context, mapping):'
263 263 if 'remotebranches' in repo.names:
264 264 remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
265 265
266 return templatekw.compatlist(context, mapping, 'remotebranch',
266 return templateutil.compatlist(context, mapping, 'remotebranch',
267 267 remotebranches, plural='remotebranches')
268 268
269 269 def _revsetutil(repo, subset, x, rtypes):
@@ -359,14 +359,15 b' class _templateconverter(object):'
359 359 data = util.sortdict(_iteritems(data))
360 360 def f():
361 361 yield _plainconverter.formatdict(data, key, value, fmt, sep)
362 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
362 return templateutil.hybriddict(data, key=key, value=value, fmt=fmt,
363 gen=f)
363 364 @staticmethod
364 365 def formatlist(data, name, fmt, sep):
365 366 '''build object that can be evaluated as either plain string or list'''
366 367 data = list(data)
367 368 def f():
368 369 yield _plainconverter.formatlist(data, name, fmt, sep)
369 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f)
370 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f)
370 371
371 372 class templateformatter(baseformatter):
372 373 def __init__(self, ui, out, topic, opts):
@@ -17,7 +17,6 b' from . import ('
17 17 node,
18 18 pycompat,
19 19 registrar,
20 templatekw,
21 20 templateutil,
22 21 url,
23 22 util,
@@ -366,7 +365,7 b' def slashpath(path):'
366 365 @templatefilter('splitlines')
367 366 def splitlines(text):
368 367 """Any text. Split text into a list of lines."""
369 return templatekw.hybridlist(text.splitlines(), name='line')
368 return templateutil.hybridlist(text.splitlines(), name='line')
370 369
371 370 @templatefilter('stringescape')
372 371 def stringescape(text):
@@ -23,156 +23,24 b' from . import ('
23 23 pycompat,
24 24 registrar,
25 25 scmutil,
26 templateutil,
26 27 util,
27 28 )
28 29
29 class _hybrid(object):
30 """Wrapper for list or dict to support legacy template
31
32 This class allows us to handle both:
33 - "{files}" (legacy command-line-specific list hack) and
34 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
35 and to access raw values:
36 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
37 - "{get(extras, key)}"
38 - "{files|json}"
39 """
40
41 def __init__(self, gen, values, makemap, joinfmt, keytype=None):
42 if gen is not None:
43 self.gen = gen # generator or function returning generator
44 self._values = values
45 self._makemap = makemap
46 self.joinfmt = joinfmt
47 self.keytype = keytype # hint for 'x in y' where type(x) is unresolved
48 def gen(self):
49 """Default generator to stringify this as {join(self, ' ')}"""
50 for i, x in enumerate(self._values):
51 if i > 0:
52 yield ' '
53 yield self.joinfmt(x)
54 def itermaps(self):
55 makemap = self._makemap
56 for x in self._values:
57 yield makemap(x)
58 def __contains__(self, x):
59 return x in self._values
60 def __getitem__(self, key):
61 return self._values[key]
62 def __len__(self):
63 return len(self._values)
64 def __iter__(self):
65 return iter(self._values)
66 def __getattr__(self, name):
67 if name not in (r'get', r'items', r'iteritems', r'iterkeys',
68 r'itervalues', r'keys', r'values'):
69 raise AttributeError(name)
70 return getattr(self._values, name)
71
72 class _mappable(object):
73 """Wrapper for non-list/dict object to support map operation
74
75 This class allows us to handle both:
76 - "{manifest}"
77 - "{manifest % '{rev}:{node}'}"
78 - "{manifest.rev}"
79
80 Unlike a _hybrid, this does not simulate the behavior of the underling
81 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
82 """
83
84 def __init__(self, gen, key, value, makemap):
85 if gen is not None:
86 self.gen = gen # generator or function returning generator
87 self._key = key
88 self._value = value # may be generator of strings
89 self._makemap = makemap
90
91 def gen(self):
92 yield pycompat.bytestr(self._value)
93
94 def tomap(self):
95 return self._makemap(self._key)
96
97 def itermaps(self):
98 yield self.tomap()
99
100 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
101 """Wrap data to support both dict-like and string-like operations"""
102 prefmt = pycompat.identity
103 if fmt is None:
104 fmt = '%s=%s'
105 prefmt = pycompat.bytestr
106 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
107 lambda k: fmt % (prefmt(k), prefmt(data[k])))
108
109 def hybridlist(data, name, fmt=None, gen=None):
110 """Wrap data to support both list-like and string-like operations"""
111 prefmt = pycompat.identity
112 if fmt is None:
113 fmt = '%s'
114 prefmt = pycompat.bytestr
115 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
116
117 def unwraphybrid(thing):
118 """Return an object which can be stringified possibly by using a legacy
119 template"""
120 gen = getattr(thing, 'gen', None)
121 if gen is None:
122 return thing
123 if callable(gen):
124 return gen()
125 return gen
126
127 def unwrapvalue(thing):
128 """Move the inner value object out of the wrapper"""
129 if not util.safehasattr(thing, '_value'):
130 return thing
131 return thing._value
132
133 def wraphybridvalue(container, key, value):
134 """Wrap an element of hybrid container to be mappable
135
136 The key is passed to the makemap function of the given container, which
137 should be an item generated by iter(container).
138 """
139 makemap = getattr(container, '_makemap', None)
140 if makemap is None:
141 return value
142 if util.safehasattr(value, '_makemap'):
143 # a nested hybrid list/dict, which has its own way of map operation
144 return value
145 return _mappable(None, key, value, makemap)
146
147 def compatdict(context, mapping, name, data, key='key', value='value',
148 fmt=None, plural=None, separator=' '):
149 """Wrap data like hybriddict(), but also supports old-style list template
150
151 This exists for backward compatibility with the old-style template. Use
152 hybriddict() for new template keywords.
153 """
154 c = [{key: k, value: v} for k, v in data.iteritems()]
155 t = context.resource(mapping, 'templ')
156 f = _showlist(name, c, t, mapping, plural, separator)
157 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
158
159 def compatlist(context, mapping, name, data, element=None, fmt=None,
160 plural=None, separator=' '):
161 """Wrap data like hybridlist(), but also supports old-style list template
162
163 This exists for backward compatibility with the old-style template. Use
164 hybridlist() for new template keywords.
165 """
166 t = context.resource(mapping, 'templ')
167 f = _showlist(name, data, t, mapping, plural, separator)
168 return hybridlist(data, name=element or name, fmt=fmt, gen=f)
30 _hybrid = templateutil.hybrid
31 _mappable = templateutil.mappable
32 _showlist = templateutil._showlist
33 hybriddict = templateutil.hybriddict
34 hybridlist = templateutil.hybridlist
35 compatdict = templateutil.compatdict
36 compatlist = templateutil.compatlist
169 37
170 38 def showdict(name, data, mapping, plural=None, key='key', value='value',
171 39 fmt=None, separator=' '):
172 40 ui = mapping.get('ui')
173 41 if ui:
174 ui.deprecwarn("templatekw.showdict() is deprecated, use compatdict()",
175 '4.6')
42 ui.deprecwarn("templatekw.showdict() is deprecated, use "
43 "templateutil.compatdict()", '4.6')
176 44 c = [{key: k, value: v} for k, v in data.iteritems()]
177 45 f = _showlist(name, c, mapping['templ'], mapping, plural, separator)
178 46 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
@@ -180,82 +48,13 b' def showdict(name, data, mapping, plural'
180 48 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
181 49 ui = mapping.get('ui')
182 50 if ui:
183 ui.deprecwarn("templatekw.showlist() is deprecated, use compatlist()",
184 '4.6')
51 ui.deprecwarn("templatekw.showlist() is deprecated, use "
52 "templateutil.compatlist()", '4.6')
185 53 if not element:
186 54 element = name
187 55 f = _showlist(name, values, mapping['templ'], mapping, plural, separator)
188 56 return hybridlist(values, name=element, gen=f)
189 57
190 def _showlist(name, values, templ, mapping, plural=None, separator=' '):
191 '''expand set of values.
192 name is name of key in template map.
193 values is list of strings or dicts.
194 plural is plural of name, if not simply name + 's'.
195 separator is used to join values as a string
196
197 expansion works like this, given name 'foo'.
198
199 if values is empty, expand 'no_foos'.
200
201 if 'foo' not in template map, return values as a string,
202 joined by 'separator'.
203
204 expand 'start_foos'.
205
206 for each value, expand 'foo'. if 'last_foo' in template
207 map, expand it instead of 'foo' for last key.
208
209 expand 'end_foos'.
210 '''
211 strmapping = pycompat.strkwargs(mapping)
212 if not plural:
213 plural = name + 's'
214 if not values:
215 noname = 'no_' + plural
216 if noname in templ:
217 yield templ(noname, **strmapping)
218 return
219 if name not in templ:
220 if isinstance(values[0], bytes):
221 yield separator.join(values)
222 else:
223 for v in values:
224 r = dict(v)
225 r.update(mapping)
226 yield r
227 return
228 startname = 'start_' + plural
229 if startname in templ:
230 yield templ(startname, **strmapping)
231 vmapping = mapping.copy()
232 def one(v, tag=name):
233 try:
234 vmapping.update(v)
235 # Python 2 raises ValueError if the type of v is wrong. Python
236 # 3 raises TypeError.
237 except (AttributeError, TypeError, ValueError):
238 try:
239 # Python 2 raises ValueError trying to destructure an e.g.
240 # bytes. Python 3 raises TypeError.
241 for a, b in v:
242 vmapping[a] = b
243 except (TypeError, ValueError):
244 vmapping[name] = v
245 return templ(tag, **pycompat.strkwargs(vmapping))
246 lastname = 'last_' + name
247 if lastname in templ:
248 last = values.pop()
249 else:
250 last = None
251 for v in values:
252 yield one(v)
253 if last is not None:
254 yield one(last, tag=lastname)
255 endname = 'end_' + plural
256 if endname in templ:
257 yield templ(endname, **strmapping)
258
259 58 def getlatesttags(context, mapping, pattern=None):
260 59 '''return date, distance and name for the latest tag of rev'''
261 60 repo = context.resource(mapping, 'repo')
@@ -498,7 +498,7 b' def dict_(context, mapping, args):'
498 498
499 499 data.update((k, evalfuncarg(context, mapping, v))
500 500 for k, v in args['kwargs'].iteritems())
501 return templatekw.hybriddict(data)
501 return templateutil.hybriddict(data)
502 502
503 503 @templatefunc('diff([includepattern [, excludepattern]])')
504 504 def diff(context, mapping, args):
@@ -548,7 +548,7 b' def files(context, mapping, args):'
548 548 ctx = context.resource(mapping, 'ctx')
549 549 m = ctx.match([raw])
550 550 files = list(ctx.matches(m))
551 return templatekw.compatlist(context, mapping, "file", files)
551 return templateutil.compatlist(context, mapping, "file", files)
552 552
553 553 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
554 554 def fill(context, mapping, args):
@@ -718,7 +718,7 b' def join(context, mapping, args):'
718 718 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
719 719 # abuses generator as a keyword that returns a list of dicts.
720 720 joinset = evalrawexp(context, mapping, args[0])
721 joinset = templatekw.unwrapvalue(joinset)
721 joinset = templateutil.unwrapvalue(joinset)
722 722 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
723 723 joiner = " "
724 724 if len(args) > 1:
@@ -808,7 +808,7 b' def max_(context, mapping, args, **kwarg'
808 808 except (TypeError, ValueError):
809 809 # i18n: "max" is a keyword
810 810 raise error.ParseError(_("max first argument should be an iterable"))
811 return templatekw.wraphybridvalue(iterable, x, x)
811 return templateutil.wraphybridvalue(iterable, x, x)
812 812
813 813 @templatefunc('min(iterable)')
814 814 def min_(context, mapping, args, **kwargs):
@@ -823,7 +823,7 b' def min_(context, mapping, args, **kwarg'
823 823 except (TypeError, ValueError):
824 824 # i18n: "min" is a keyword
825 825 raise error.ParseError(_("min first argument should be an iterable"))
826 return templatekw.wraphybridvalue(iterable, x, x)
826 return templateutil.wraphybridvalue(iterable, x, x)
827 827
828 828 @templatefunc('mod(a, b)')
829 829 def mod(context, mapping, args):
@@ -847,7 +847,7 b' def obsfateoperations(context, mapping, '
847 847
848 848 try:
849 849 data = obsutil.markersoperations(markers)
850 return templatekw.hybridlist(data, name='operation')
850 return templateutil.hybridlist(data, name='operation')
851 851 except (TypeError, KeyError):
852 852 # i18n: "obsfateoperations" is a keyword
853 853 errmsg = _("obsfateoperations first argument should be an iterable")
@@ -864,7 +864,7 b' def obsfatedate(context, mapping, args):'
864 864
865 865 try:
866 866 data = obsutil.markersdates(markers)
867 return templatekw.hybridlist(data, name='date', fmt='%d %d')
867 return templateutil.hybridlist(data, name='date', fmt='%d %d')
868 868 except (TypeError, KeyError):
869 869 # i18n: "obsfatedate" is a keyword
870 870 errmsg = _("obsfatedate first argument should be an iterable")
@@ -881,7 +881,7 b' def obsfateusers(context, mapping, args)'
881 881
882 882 try:
883 883 data = obsutil.markersusers(markers)
884 return templatekw.hybridlist(data, name='user')
884 return templateutil.hybridlist(data, name='user')
885 885 except (TypeError, KeyError, ValueError):
886 886 # i18n: "obsfateusers" is a keyword
887 887 msg = _("obsfateusers first argument should be an iterable of "
@@ -1120,7 +1120,7 b' def expandaliases(tree, aliases):'
1120 1120
1121 1121 def _flatten(thing):
1122 1122 '''yield a single stream from a possibly nested set of iterators'''
1123 thing = templatekw.unwraphybrid(thing)
1123 thing = templateutil.unwraphybrid(thing)
1124 1124 if isinstance(thing, bytes):
1125 1125 yield thing
1126 1126 elif isinstance(thing, str):
@@ -1134,7 +1134,7 b' def _flatten(thing):'
1134 1134 yield pycompat.bytestr(thing)
1135 1135 else:
1136 1136 for i in thing:
1137 i = templatekw.unwraphybrid(i)
1137 i = templateutil.unwraphybrid(i)
1138 1138 if isinstance(i, bytes):
1139 1139 yield i
1140 1140 elif i is None:
@@ -13,7 +13,6 b' from .i18n import _'
13 13 from . import (
14 14 error,
15 15 pycompat,
16 templatekw,
17 16 util,
18 17 )
19 18
@@ -23,9 +22,219 b' class ResourceUnavailable(error.Abort):'
23 22 class TemplateNotFound(error.Abort):
24 23 pass
25 24
25 class hybrid(object):
26 """Wrapper for list or dict to support legacy template
27
28 This class allows us to handle both:
29 - "{files}" (legacy command-line-specific list hack) and
30 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
31 and to access raw values:
32 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
33 - "{get(extras, key)}"
34 - "{files|json}"
35 """
36
37 def __init__(self, gen, values, makemap, joinfmt, keytype=None):
38 if gen is not None:
39 self.gen = gen # generator or function returning generator
40 self._values = values
41 self._makemap = makemap
42 self.joinfmt = joinfmt
43 self.keytype = keytype # hint for 'x in y' where type(x) is unresolved
44 def gen(self):
45 """Default generator to stringify this as {join(self, ' ')}"""
46 for i, x in enumerate(self._values):
47 if i > 0:
48 yield ' '
49 yield self.joinfmt(x)
50 def itermaps(self):
51 makemap = self._makemap
52 for x in self._values:
53 yield makemap(x)
54 def __contains__(self, x):
55 return x in self._values
56 def __getitem__(self, key):
57 return self._values[key]
58 def __len__(self):
59 return len(self._values)
60 def __iter__(self):
61 return iter(self._values)
62 def __getattr__(self, name):
63 if name not in (r'get', r'items', r'iteritems', r'iterkeys',
64 r'itervalues', r'keys', r'values'):
65 raise AttributeError(name)
66 return getattr(self._values, name)
67
68 class mappable(object):
69 """Wrapper for non-list/dict object to support map operation
70
71 This class allows us to handle both:
72 - "{manifest}"
73 - "{manifest % '{rev}:{node}'}"
74 - "{manifest.rev}"
75
76 Unlike a hybrid, this does not simulate the behavior of the underling
77 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
78 """
79
80 def __init__(self, gen, key, value, makemap):
81 if gen is not None:
82 self.gen = gen # generator or function returning generator
83 self._key = key
84 self._value = value # may be generator of strings
85 self._makemap = makemap
86
87 def gen(self):
88 yield pycompat.bytestr(self._value)
89
90 def tomap(self):
91 return self._makemap(self._key)
92
93 def itermaps(self):
94 yield self.tomap()
95
96 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
97 """Wrap data to support both dict-like and string-like operations"""
98 prefmt = pycompat.identity
99 if fmt is None:
100 fmt = '%s=%s'
101 prefmt = pycompat.bytestr
102 return hybrid(gen, data, lambda k: {key: k, value: data[k]},
103 lambda k: fmt % (prefmt(k), prefmt(data[k])))
104
105 def hybridlist(data, name, fmt=None, gen=None):
106 """Wrap data to support both list-like and string-like operations"""
107 prefmt = pycompat.identity
108 if fmt is None:
109 fmt = '%s'
110 prefmt = pycompat.bytestr
111 return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
112
113 def unwraphybrid(thing):
114 """Return an object which can be stringified possibly by using a legacy
115 template"""
116 gen = getattr(thing, 'gen', None)
117 if gen is None:
118 return thing
119 if callable(gen):
120 return gen()
121 return gen
122
123 def unwrapvalue(thing):
124 """Move the inner value object out of the wrapper"""
125 if not util.safehasattr(thing, '_value'):
126 return thing
127 return thing._value
128
129 def wraphybridvalue(container, key, value):
130 """Wrap an element of hybrid container to be mappable
131
132 The key is passed to the makemap function of the given container, which
133 should be an item generated by iter(container).
134 """
135 makemap = getattr(container, '_makemap', None)
136 if makemap is None:
137 return value
138 if util.safehasattr(value, '_makemap'):
139 # a nested hybrid list/dict, which has its own way of map operation
140 return value
141 return mappable(None, key, value, makemap)
142
143 def compatdict(context, mapping, name, data, key='key', value='value',
144 fmt=None, plural=None, separator=' '):
145 """Wrap data like hybriddict(), but also supports old-style list template
146
147 This exists for backward compatibility with the old-style template. Use
148 hybriddict() for new template keywords.
149 """
150 c = [{key: k, value: v} for k, v in data.iteritems()]
151 t = context.resource(mapping, 'templ')
152 f = _showlist(name, c, t, mapping, plural, separator)
153 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
154
155 def compatlist(context, mapping, name, data, element=None, fmt=None,
156 plural=None, separator=' '):
157 """Wrap data like hybridlist(), but also supports old-style list template
158
159 This exists for backward compatibility with the old-style template. Use
160 hybridlist() for new template keywords.
161 """
162 t = context.resource(mapping, 'templ')
163 f = _showlist(name, data, t, mapping, plural, separator)
164 return hybridlist(data, name=element or name, fmt=fmt, gen=f)
165
166 def _showlist(name, values, templ, mapping, plural=None, separator=' '):
167 '''expand set of values.
168 name is name of key in template map.
169 values is list of strings or dicts.
170 plural is plural of name, if not simply name + 's'.
171 separator is used to join values as a string
172
173 expansion works like this, given name 'foo'.
174
175 if values is empty, expand 'no_foos'.
176
177 if 'foo' not in template map, return values as a string,
178 joined by 'separator'.
179
180 expand 'start_foos'.
181
182 for each value, expand 'foo'. if 'last_foo' in template
183 map, expand it instead of 'foo' for last key.
184
185 expand 'end_foos'.
186 '''
187 strmapping = pycompat.strkwargs(mapping)
188 if not plural:
189 plural = name + 's'
190 if not values:
191 noname = 'no_' + plural
192 if noname in templ:
193 yield templ(noname, **strmapping)
194 return
195 if name not in templ:
196 if isinstance(values[0], bytes):
197 yield separator.join(values)
198 else:
199 for v in values:
200 r = dict(v)
201 r.update(mapping)
202 yield r
203 return
204 startname = 'start_' + plural
205 if startname in templ:
206 yield templ(startname, **strmapping)
207 vmapping = mapping.copy()
208 def one(v, tag=name):
209 try:
210 vmapping.update(v)
211 # Python 2 raises ValueError if the type of v is wrong. Python
212 # 3 raises TypeError.
213 except (AttributeError, TypeError, ValueError):
214 try:
215 # Python 2 raises ValueError trying to destructure an e.g.
216 # bytes. Python 3 raises TypeError.
217 for a, b in v:
218 vmapping[a] = b
219 except (TypeError, ValueError):
220 vmapping[name] = v
221 return templ(tag, **pycompat.strkwargs(vmapping))
222 lastname = 'last_' + name
223 if lastname in templ:
224 last = values.pop()
225 else:
226 last = None
227 for v in values:
228 yield one(v)
229 if last is not None:
230 yield one(last, tag=lastname)
231 endname = 'end_' + plural
232 if endname in templ:
233 yield templ(endname, **strmapping)
234
26 235 def stringify(thing):
27 236 """Turn values into bytes by converting into text and concatenating them"""
28 thing = templatekw.unwraphybrid(thing)
237 thing = unwraphybrid(thing)
29 238 if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
30 239 if isinstance(thing, str):
31 240 # This is only reachable on Python 3 (otherwise
@@ -59,7 +268,7 b' def evalrawexp(context, mapping, arg):'
59 268 def evalfuncarg(context, mapping, arg):
60 269 """Evaluate given argument as value type"""
61 270 thing = evalrawexp(context, mapping, arg)
62 thing = templatekw.unwrapvalue(thing)
271 thing = unwrapvalue(thing)
63 272 # evalrawexp() may return string, generator of strings or arbitrary object
64 273 # such as date tuple, but filter does not want generator.
65 274 if isinstance(thing, types.GeneratorType):
@@ -76,7 +285,7 b' def evalboolean(context, mapping, arg):'
76 285 thing = util.parsebool(data)
77 286 else:
78 287 thing = func(context, mapping, data)
79 thing = templatekw.unwrapvalue(thing)
288 thing = unwrapvalue(thing)
80 289 if isinstance(thing, bool):
81 290 return thing
82 291 # other objects are evaluated as strings, which means 0 is True, but
@@ -236,4 +445,4 b' def getdictitem(dictarg, key):'
236 445 val = dictarg.get(key)
237 446 if val is None:
238 447 return
239 return templatekw.wraphybridvalue(dictarg, key, val)
448 return wraphybridvalue(dictarg, key, val)
General Comments 0
You need to be logged in to leave comments. Login now