##// END OF EJS Templates
templater: abstract min/max away...
Yuya Nishihara -
r38284:41ae9b3c default
parent child Browse files
Show More
@@ -717,6 +717,12 b' class sessionvars(templateutil.wrapped):'
717 key = templateutil.unwrapvalue(context, mapping, key)
717 key = templateutil.unwrapvalue(context, mapping, key)
718 return self._vars.get(key)
718 return self._vars.get(key)
719
719
720 def getmin(self, context, mapping):
721 raise error.ParseError(_('not comparable'))
722
723 def getmax(self, context, mapping):
724 raise error.ParseError(_('not comparable'))
725
720 def itermaps(self, context):
726 def itermaps(self, context):
721 separator = self._start
727 separator = self._start
722 for key, value in sorted(self._vars.iteritems()):
728 for key, value in sorted(self._vars.iteritems()):
@@ -20,7 +20,6 b' from . import ('
20 error,
20 error,
21 minirst,
21 minirst,
22 obsutil,
22 obsutil,
23 pycompat,
24 registrar,
23 registrar,
25 revset as revsetmod,
24 revset as revsetmod,
26 revsetlang,
25 revsetlang,
@@ -404,13 +403,13 b' def max_(context, mapping, args, **kwarg'
404 # i18n: "max" is a keyword
403 # i18n: "max" is a keyword
405 raise error.ParseError(_("max expects one argument"))
404 raise error.ParseError(_("max expects one argument"))
406
405
407 iterable = evalfuncarg(context, mapping, args[0])
406 iterable = evalwrapped(context, mapping, args[0])
408 try:
407 try:
409 x = max(pycompat.maybebytestr(iterable))
408 return iterable.getmax(context, mapping)
410 except (TypeError, ValueError):
409 except error.ParseError as err:
411 # i18n: "max" is a keyword
410 # i18n: "max" is a keyword
412 raise error.ParseError(_("max first argument should be an iterable"))
411 hint = _("max first argument should be an iterable")
413 return templateutil.wraphybridvalue(iterable, x, x)
412 raise error.ParseError(bytes(err), hint=hint)
414
413
415 @templatefunc('min(iterable)')
414 @templatefunc('min(iterable)')
416 def min_(context, mapping, args, **kwargs):
415 def min_(context, mapping, args, **kwargs):
@@ -419,13 +418,13 b' def min_(context, mapping, args, **kwarg'
419 # i18n: "min" is a keyword
418 # i18n: "min" is a keyword
420 raise error.ParseError(_("min expects one argument"))
419 raise error.ParseError(_("min expects one argument"))
421
420
422 iterable = evalfuncarg(context, mapping, args[0])
421 iterable = evalwrapped(context, mapping, args[0])
423 try:
422 try:
424 x = min(pycompat.maybebytestr(iterable))
423 return iterable.getmin(context, mapping)
425 except (TypeError, ValueError):
424 except error.ParseError as err:
426 # i18n: "min" is a keyword
425 # i18n: "min" is a keyword
427 raise error.ParseError(_("min first argument should be an iterable"))
426 hint = _("min first argument should be an iterable")
428 return templateutil.wraphybridvalue(iterable, x, x)
427 raise error.ParseError(bytes(err), hint=hint)
429
428
430 @templatefunc('mod(a, b)')
429 @templatefunc('mod(a, b)')
431 def mod(context, mapping, args):
430 def mod(context, mapping, args):
@@ -47,6 +47,16 b' class wrapped(object):'
47 """
47 """
48
48
49 @abc.abstractmethod
49 @abc.abstractmethod
50 def getmin(self, context, mapping):
51 """Return the smallest item, which may be either a wrapped or a pure
52 value depending on the self type"""
53
54 @abc.abstractmethod
55 def getmax(self, context, mapping):
56 """Return the largest item, which may be either a wrapped or a pure
57 value depending on the self type"""
58
59 @abc.abstractmethod
50 def itermaps(self, context):
60 def itermaps(self, context):
51 """Yield each template mapping"""
61 """Yield each template mapping"""
52
62
@@ -85,6 +95,17 b' class wrappedbytes(wrapped):'
85 raise error.ParseError(_('%r is not a dictionary')
95 raise error.ParseError(_('%r is not a dictionary')
86 % pycompat.bytestr(self._value))
96 % pycompat.bytestr(self._value))
87
97
98 def getmin(self, context, mapping):
99 return self._getby(context, mapping, min)
100
101 def getmax(self, context, mapping):
102 return self._getby(context, mapping, max)
103
104 def _getby(self, context, mapping, func):
105 if not self._value:
106 raise error.ParseError(_('empty string'))
107 return func(pycompat.iterbytestr(self._value))
108
88 def itermaps(self, context):
109 def itermaps(self, context):
89 raise error.ParseError(_('%r is not iterable of mappings')
110 raise error.ParseError(_('%r is not iterable of mappings')
90 % pycompat.bytestr(self._value))
111 % pycompat.bytestr(self._value))
@@ -107,6 +128,12 b' class wrappedvalue(wrapped):'
107 def getmember(self, context, mapping, key):
128 def getmember(self, context, mapping, key):
108 raise error.ParseError(_('%r is not a dictionary') % self._value)
129 raise error.ParseError(_('%r is not a dictionary') % self._value)
109
130
131 def getmin(self, context, mapping):
132 raise error.ParseError(_("%r is not iterable") % self._value)
133
134 def getmax(self, context, mapping):
135 raise error.ParseError(_("%r is not iterable") % self._value)
136
110 def itermaps(self, context):
137 def itermaps(self, context):
111 raise error.ParseError(_('%r is not iterable of mappings')
138 raise error.ParseError(_('%r is not iterable of mappings')
112 % self._value)
139 % self._value)
@@ -151,6 +178,18 b' class hybrid(wrapped):'
151 key = unwrapastype(context, mapping, key, self.keytype)
178 key = unwrapastype(context, mapping, key, self.keytype)
152 return self._wrapvalue(key, self._values.get(key))
179 return self._wrapvalue(key, self._values.get(key))
153
180
181 def getmin(self, context, mapping):
182 return self._getby(context, mapping, min)
183
184 def getmax(self, context, mapping):
185 return self._getby(context, mapping, max)
186
187 def _getby(self, context, mapping, func):
188 if not self._values:
189 raise error.ParseError(_('empty sequence'))
190 val = func(self._values)
191 return self._wrapvalue(val, val)
192
154 def _wrapvalue(self, key, val):
193 def _wrapvalue(self, key, val):
155 if val is None:
194 if val is None:
156 return
195 return
@@ -217,6 +256,14 b' class mappable(wrapped):'
217 w = makewrapped(context, mapping, self._value)
256 w = makewrapped(context, mapping, self._value)
218 return w.getmember(context, mapping, key)
257 return w.getmember(context, mapping, key)
219
258
259 def getmin(self, context, mapping):
260 w = makewrapped(context, mapping, self._value)
261 return w.getmin(context, mapping)
262
263 def getmax(self, context, mapping):
264 w = makewrapped(context, mapping, self._value)
265 return w.getmax(context, mapping)
266
220 def itermaps(self, context):
267 def itermaps(self, context):
221 yield self.tomap()
268 yield self.tomap()
222
269
@@ -255,6 +302,12 b' class _mappingsequence(wrapped):'
255 def getmember(self, context, mapping, key):
302 def getmember(self, context, mapping, key):
256 raise error.ParseError(_('not a dictionary'))
303 raise error.ParseError(_('not a dictionary'))
257
304
305 def getmin(self, context, mapping):
306 raise error.ParseError(_('not comparable'))
307
308 def getmax(self, context, mapping):
309 raise error.ParseError(_('not comparable'))
310
258 def join(self, context, mapping, sep):
311 def join(self, context, mapping, sep):
259 mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
312 mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
260 if self._name:
313 if self._name:
@@ -321,6 +374,18 b' class mappedgenerator(wrapped):'
321 def getmember(self, context, mapping, key):
374 def getmember(self, context, mapping, key):
322 raise error.ParseError(_('not a dictionary'))
375 raise error.ParseError(_('not a dictionary'))
323
376
377 def getmin(self, context, mapping):
378 return self._getby(context, mapping, min)
379
380 def getmax(self, context, mapping):
381 return self._getby(context, mapping, max)
382
383 def _getby(self, context, mapping, func):
384 xs = self.tovalue(context, mapping)
385 if not xs:
386 raise error.ParseError(_('empty sequence'))
387 return func(xs)
388
324 def itermaps(self, context):
389 def itermaps(self, context):
325 raise error.ParseError(_('list of strings is not mappable'))
390 raise error.ParseError(_('list of strings is not mappable'))
326
391
@@ -3274,6 +3274,51 b' Test min/max over map operation:'
3274 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3274 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3275 t3
3275 t3
3276
3276
3277 Test min/max of strings:
3278
3279 $ hg log -R latesttag -l1 -T '{min(desc)}\n'
3280 3
3281 $ hg log -R latesttag -l1 -T '{max(desc)}\n'
3282 t
3283
3284 Test min/max of non-iterable:
3285
3286 $ hg debugtemplate '{min(1)}'
3287 hg: parse error: 1 is not iterable
3288 (min first argument should be an iterable)
3289 [255]
3290 $ hg debugtemplate '{max(2)}'
3291 hg: parse error: 2 is not iterable
3292 (max first argument should be an iterable)
3293 [255]
3294
3295 Test min/max of empty sequence:
3296
3297 $ hg debugtemplate '{min("")}'
3298 hg: parse error: empty string
3299 (min first argument should be an iterable)
3300 [255]
3301 $ hg debugtemplate '{max("")}'
3302 hg: parse error: empty string
3303 (max first argument should be an iterable)
3304 [255]
3305 $ hg debugtemplate '{min(dict())}'
3306 hg: parse error: empty sequence
3307 (min first argument should be an iterable)
3308 [255]
3309 $ hg debugtemplate '{max(dict())}'
3310 hg: parse error: empty sequence
3311 (max first argument should be an iterable)
3312 [255]
3313 $ hg debugtemplate '{min(dict() % "")}'
3314 hg: parse error: empty sequence
3315 (min first argument should be an iterable)
3316 [255]
3317 $ hg debugtemplate '{max(dict() % "")}'
3318 hg: parse error: empty sequence
3319 (max first argument should be an iterable)
3320 [255]
3321
3277 Test min/max of if() result
3322 Test min/max of if() result
3278
3323
3279 $ cd latesttag
3324 $ cd latesttag
General Comments 0
You need to be logged in to leave comments. Login now