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])