Show More
@@ -717,6 +717,12 b' class sessionvars(templateutil.wrapped):' | |||
|
717 | 717 | key = templateutil.unwrapvalue(context, mapping, key) |
|
718 | 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 | 726 | def itermaps(self, context): |
|
721 | 727 | separator = self._start |
|
722 | 728 | for key, value in sorted(self._vars.iteritems()): |
@@ -20,7 +20,6 b' from . import (' | |||
|
20 | 20 | error, |
|
21 | 21 | minirst, |
|
22 | 22 | obsutil, |
|
23 | pycompat, | |
|
24 | 23 | registrar, |
|
25 | 24 | revset as revsetmod, |
|
26 | 25 | revsetlang, |
@@ -404,13 +403,13 b' def max_(context, mapping, args, **kwarg' | |||
|
404 | 403 | # i18n: "max" is a keyword |
|
405 | 404 | raise error.ParseError(_("max expects one argument")) |
|
406 | 405 | |
|
407 |
iterable = eval |
|
|
406 | iterable = evalwrapped(context, mapping, args[0]) | |
|
408 | 407 | try: |
|
409 | x = max(pycompat.maybebytestr(iterable)) | |
|
410 |
except |
|
|
408 | return iterable.getmax(context, mapping) | |
|
409 | except error.ParseError as err: | |
|
411 | 410 | # i18n: "max" is a keyword |
|
412 |
|
|
|
413 | return templateutil.wraphybridvalue(iterable, x, x) | |
|
411 | hint = _("max first argument should be an iterable") | |
|
412 | raise error.ParseError(bytes(err), hint=hint) | |
|
414 | 413 | |
|
415 | 414 | @templatefunc('min(iterable)') |
|
416 | 415 | def min_(context, mapping, args, **kwargs): |
@@ -419,13 +418,13 b' def min_(context, mapping, args, **kwarg' | |||
|
419 | 418 | # i18n: "min" is a keyword |
|
420 | 419 | raise error.ParseError(_("min expects one argument")) |
|
421 | 420 | |
|
422 |
iterable = eval |
|
|
421 | iterable = evalwrapped(context, mapping, args[0]) | |
|
423 | 422 | try: |
|
424 | x = min(pycompat.maybebytestr(iterable)) | |
|
425 |
except |
|
|
423 | return iterable.getmin(context, mapping) | |
|
424 | except error.ParseError as err: | |
|
426 | 425 | # i18n: "min" is a keyword |
|
427 |
|
|
|
428 | return templateutil.wraphybridvalue(iterable, x, x) | |
|
426 | hint = _("min first argument should be an iterable") | |
|
427 | raise error.ParseError(bytes(err), hint=hint) | |
|
429 | 428 | |
|
430 | 429 | @templatefunc('mod(a, b)') |
|
431 | 430 | def mod(context, mapping, args): |
@@ -47,6 +47,16 b' class wrapped(object):' | |||
|
47 | 47 | """ |
|
48 | 48 | |
|
49 | 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 | 60 | def itermaps(self, context): |
|
51 | 61 | """Yield each template mapping""" |
|
52 | 62 | |
@@ -85,6 +95,17 b' class wrappedbytes(wrapped):' | |||
|
85 | 95 | raise error.ParseError(_('%r is not a dictionary') |
|
86 | 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 | 109 | def itermaps(self, context): |
|
89 | 110 | raise error.ParseError(_('%r is not iterable of mappings') |
|
90 | 111 | % pycompat.bytestr(self._value)) |
@@ -107,6 +128,12 b' class wrappedvalue(wrapped):' | |||
|
107 | 128 | def getmember(self, context, mapping, key): |
|
108 | 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 | 137 | def itermaps(self, context): |
|
111 | 138 | raise error.ParseError(_('%r is not iterable of mappings') |
|
112 | 139 | % self._value) |
@@ -151,6 +178,18 b' class hybrid(wrapped):' | |||
|
151 | 178 | key = unwrapastype(context, mapping, key, self.keytype) |
|
152 | 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 | 193 | def _wrapvalue(self, key, val): |
|
155 | 194 | if val is None: |
|
156 | 195 | return |
@@ -217,6 +256,14 b' class mappable(wrapped):' | |||
|
217 | 256 | w = makewrapped(context, mapping, self._value) |
|
218 | 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 | 267 | def itermaps(self, context): |
|
221 | 268 | yield self.tomap() |
|
222 | 269 | |
@@ -255,6 +302,12 b' class _mappingsequence(wrapped):' | |||
|
255 | 302 | def getmember(self, context, mapping, key): |
|
256 | 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 | 311 | def join(self, context, mapping, sep): |
|
259 | 312 | mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context)) |
|
260 | 313 | if self._name: |
@@ -321,6 +374,18 b' class mappedgenerator(wrapped):' | |||
|
321 | 374 | def getmember(self, context, mapping, key): |
|
322 | 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 | 389 | def itermaps(self, context): |
|
325 | 390 | raise error.ParseError(_('list of strings is not mappable')) |
|
326 | 391 |
@@ -3274,6 +3274,51 b' Test min/max over map operation:' | |||
|
3274 | 3274 | $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n' |
|
3275 | 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 | 3322 | Test min/max of if() result |
|
3278 | 3323 | |
|
3279 | 3324 | $ cd latesttag |
General Comments 0
You need to be logged in to leave comments.
Login now