Mercurial > hg
comparison mercurial/templater.py @ 34581:ee0d74083a22
templater: store revisions as ints so min/max won't compare them as strings
Because a template value has no explicit type (like ancient PHP), ifcontains()
has to coerce the type of the needle. Before, it was always converted to a
string, which meant any container type should be a list/dict of strings.
This no longer works since we've introduced min/max functions.
In order to work around the untyped nature of templater, this patch adds
a type specifier to hybrid dict/list. It isn't named as "valuetype" since
the _hybrid class can also wrap a dict.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Tue, 19 Sep 2017 23:13:46 +0900 |
parents | 4c1cfe54c08d |
children | 3edfd472f3cb |
comparison
equal
deleted
inserted
replaced
34580:7259f0ddfc0f | 34581:ee0d74083a22 |
---|---|
331 return thing | 331 return thing |
332 # other objects are evaluated as strings, which means 0 is True, but | 332 # other objects are evaluated as strings, which means 0 is True, but |
333 # empty dict/list should be False as they are expected to be '' | 333 # empty dict/list should be False as they are expected to be '' |
334 return bool(stringify(thing)) | 334 return bool(stringify(thing)) |
335 | 335 |
336 def evalinteger(context, mapping, arg, err): | 336 def evalinteger(context, mapping, arg, err=None): |
337 v = evalfuncarg(context, mapping, arg) | 337 v = evalfuncarg(context, mapping, arg) |
338 try: | 338 try: |
339 return int(v) | 339 return int(v) |
340 except (TypeError, ValueError): | 340 except (TypeError, ValueError): |
341 raise error.ParseError(err) | 341 raise error.ParseError(err or _('not an integer')) |
342 | 342 |
343 def evalstring(context, mapping, arg): | 343 def evalstring(context, mapping, arg): |
344 return stringify(evalrawexp(context, mapping, arg)) | 344 return stringify(evalrawexp(context, mapping, arg)) |
345 | 345 |
346 def evalstringliteral(context, mapping, arg): | 346 def evalstringliteral(context, mapping, arg): |
350 if func is runsymbol: | 350 if func is runsymbol: |
351 thing = func(context, mapping, data, default=data) | 351 thing = func(context, mapping, data, default=data) |
352 else: | 352 else: |
353 thing = func(context, mapping, data) | 353 thing = func(context, mapping, data) |
354 return stringify(thing) | 354 return stringify(thing) |
355 | |
356 _evalfuncbytype = { | |
357 bool: evalboolean, | |
358 bytes: evalstring, | |
359 int: evalinteger, | |
360 } | |
361 | |
362 def evalastype(context, mapping, arg, typ): | |
363 """Evaluate given argument and coerce its type""" | |
364 try: | |
365 f = _evalfuncbytype[typ] | |
366 except KeyError: | |
367 raise error.ProgrammingError('invalid type specified: %r' % typ) | |
368 return f(context, mapping, arg) | |
355 | 369 |
356 def runinteger(context, mapping, data): | 370 def runinteger(context, mapping, data): |
357 return int(data) | 371 return int(data) |
358 | 372 |
359 def runstring(context, mapping, data): | 373 def runstring(context, mapping, data): |
780 on whether the item "needle" is in "haystack".""" | 794 on whether the item "needle" is in "haystack".""" |
781 if not (3 <= len(args) <= 4): | 795 if not (3 <= len(args) <= 4): |
782 # i18n: "ifcontains" is a keyword | 796 # i18n: "ifcontains" is a keyword |
783 raise error.ParseError(_("ifcontains expects three or four arguments")) | 797 raise error.ParseError(_("ifcontains expects three or four arguments")) |
784 | 798 |
785 needle = evalstring(context, mapping, args[0]) | |
786 haystack = evalfuncarg(context, mapping, args[1]) | 799 haystack = evalfuncarg(context, mapping, args[1]) |
800 needle = evalastype(context, mapping, args[0], | |
801 getattr(haystack, 'keytype', None) or bytes) | |
787 | 802 |
788 if needle in haystack: | 803 if needle in haystack: |
789 yield evalrawexp(context, mapping, args[2]) | 804 yield evalrawexp(context, mapping, args[2]) |
790 elif len(args) == 4: | 805 elif len(args) == 4: |
791 yield evalrawexp(context, mapping, args[3]) | 806 yield evalrawexp(context, mapping, args[3]) |