# HG changeset patch # User Yuya Nishihara # Date 1528518887 -32400 # Node ID f9c426385853657be0082f63567a1e78a3d5474b # Parent a9de1d28681cf6719e314029743de40d8900459c templater: abstract truth testing to fix {if(list_of_empty_strings)} Non-empty list should always be True even if it's stringified to ''. Spotted by Martin von Zweigbergk. diff -r a9de1d28681c -r f9c426385853 mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py Tue Jun 12 23:17:38 2018 +0900 +++ b/mercurial/hgweb/webutil.py Sat Jun 09 13:34:47 2018 +0900 @@ -743,6 +743,9 @@ def show(self, context, mapping): return self.join(context, '') + def tobool(self, context, mapping): + return bool(self._vars) + def tovalue(self, context, mapping): return self._vars diff -r a9de1d28681c -r f9c426385853 mercurial/templateutil.py --- a/mercurial/templateutil.py Tue Jun 12 23:17:38 2018 +0900 +++ b/mercurial/templateutil.py Sat Jun 09 13:34:47 2018 +0900 @@ -85,6 +85,10 @@ """ @abc.abstractmethod + def tobool(self, context, mapping): + """Return a boolean representation of the inner value""" + + @abc.abstractmethod def tovalue(self, context, mapping): """Move the inner value object out or create a value representation @@ -136,6 +140,9 @@ def show(self, context, mapping): return self._value + def tobool(self, context, mapping): + return bool(self._value) + def tovalue(self, context, mapping): return self._value @@ -169,6 +176,14 @@ return b'' return pycompat.bytestr(self._value) + def tobool(self, context, mapping): + if self._value is None: + return False + if isinstance(self._value, bool): + return self._value + # otherwise evaluate as string, which means 0 is True + return bool(pycompat.bytestr(self._value)) + def tovalue(self, context, mapping): return self._value @@ -201,6 +216,9 @@ def tomap(self, context): return {'unixtime': self._unixtime, 'tzoffset': self._tzoffset} + def tobool(self, context, mapping): + return True + def tovalue(self, context, mapping): return (self._unixtime, self._tzoffset) @@ -272,6 +290,9 @@ return gen() return gen + def tobool(self, context, mapping): + return bool(self._values) + def tovalue(self, context, mapping): # TODO: make it non-recursive for trivial lists/dicts xs = self._values @@ -327,6 +348,9 @@ return gen() return gen + def tobool(self, context, mapping): + return bool(self.tovalue(context, mapping)) + def tovalue(self, context, mapping): return _unthunk(context, mapping, self._value) @@ -396,6 +420,9 @@ def itermaps(self, context): return self._make(context, *self._args) + def tobool(self, context, mapping): + return _nonempty(self.itermaps(context)) + class mappinglist(_mappingsequence): """Wrapper for list of template mappings""" @@ -406,6 +433,9 @@ def itermaps(self, context): return iter(self._mappings) + def tobool(self, context, mapping): + return bool(self._mappings) + class mappedgenerator(wrapped): """Wrapper for generator of strings which acts as a list @@ -449,6 +479,9 @@ def show(self, context, mapping): return self.join(context, mapping, '') + def tobool(self, context, mapping): + return _nonempty(self._gen(context)) + def tovalue(self, context, mapping): return [stringify(context, mapping, x) for x in self._gen(context)] @@ -607,6 +640,13 @@ else: return None +def _nonempty(xiter): + try: + next(xiter) + return True + except StopIteration: + return False + def _unthunk(context, mapping, thing): """Evaluate a lazy byte string into value""" if not isinstance(thing, types.GeneratorType): @@ -655,13 +695,7 @@ thing = stringutil.parsebool(data) else: thing = func(context, mapping, data) - if isinstance(thing, wrapped): - thing = thing.tovalue(context, mapping) - if isinstance(thing, bool): - return thing - # other objects are evaluated as strings, which means 0 is True, but - # empty dict/list should be False as they are expected to be '' - return bool(stringify(context, mapping, thing)) + return makewrapped(context, mapping, thing).tobool(context, mapping) def evaldate(context, mapping, arg, err=None): """Evaluate given argument as a date tuple or a date string; returns diff -r a9de1d28681c -r f9c426385853 tests/test-command-template.t --- a/tests/test-command-template.t Tue Jun 12 23:17:38 2018 +0900 +++ b/tests/test-command-template.t Sat Jun 09 13:34:47 2018 +0900 @@ -4151,6 +4151,10 @@ empty string is False $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n' empty list is False + $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n' + non-empty list is True + $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n' + list of empty strings is True $ hg log -r 0 -T '{if(true, "true is True")}\n' true is True $ hg log -r 0 -T '{if(false, "", "false is False")}\n'