# HG changeset patch # User Yuya Nishihara # Date 1521386600 -32400 # Node ID fb874fc1d9b4fbc56fe9d6699e249e43681e0afe # Parent 80f423a14c90fef3090d564dac8db4949bfaae55 templater: abstract ifcontains() over wrapped types This allows us to make .keytype private. There's a minor BC that a hybrid dict/list of keytype=None now strictly checks the type of the needle. For example, {ifcontains(rev, files)} no longer matches a file named "1" at the rev=1. I made this change for consistency with the get(dict, key) function. We can restore the old behavior by making keytype=bytes the default if desired. diff -r 80f423a14c90 -r fb874fc1d9b4 mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py Wed Mar 21 12:06:18 2018 +0900 +++ b/mercurial/hgweb/webutil.py Mon Mar 19 00:23:20 2018 +0900 @@ -713,6 +713,10 @@ def __copy__(self): return sessionvars(copy.copy(self._vars), self._start) + def contains(self, context, mapping, item): + item = templateutil.unwrapvalue(context, mapping, item) + return item in self._vars + def getmember(self, context, mapping, key): key = templateutil.unwrapvalue(context, mapping, key) return self._vars.get(key) diff -r 80f423a14c90 -r fb874fc1d9b4 mercurial/templatefuncs.py --- a/mercurial/templatefuncs.py Wed Mar 21 12:06:18 2018 +0900 +++ b/mercurial/templatefuncs.py Mon Mar 19 00:23:20 2018 +0900 @@ -291,13 +291,10 @@ # i18n: "ifcontains" is a keyword raise error.ParseError(_("ifcontains expects three or four arguments")) - haystack = evalfuncarg(context, mapping, args[1]) - keytype = getattr(haystack, 'keytype', None) + haystack = evalwrapped(context, mapping, args[1]) try: needle = evalrawexp(context, mapping, args[0]) - needle = templateutil.unwrapastype(context, mapping, needle, - keytype or bytes) - found = (needle in haystack) + found = haystack.contains(context, mapping, needle) except error.ParseError: found = False diff -r 80f423a14c90 -r fb874fc1d9b4 mercurial/templateutil.py --- a/mercurial/templateutil.py Wed Mar 21 12:06:18 2018 +0900 +++ b/mercurial/templateutil.py Mon Mar 19 00:23:20 2018 +0900 @@ -38,6 +38,13 @@ __metaclass__ = abc.ABCMeta @abc.abstractmethod + def contains(self, context, mapping, item): + """Test if the specified item is in self + + The item argument may be a wrapped object. + """ + + @abc.abstractmethod def getmember(self, context, mapping, key): """Return a member item for the specified key @@ -91,6 +98,10 @@ def __init__(self, value): self._value = value + def contains(self, context, mapping, item): + item = stringify(context, mapping, item) + return item in self._value + def getmember(self, context, mapping, key): raise error.ParseError(_('%r is not a dictionary') % pycompat.bytestr(self._value)) @@ -125,6 +136,9 @@ def __init__(self, value): self._value = value + def contains(self, context, mapping, item): + raise error.ParseError(_("%r is not iterable") % self._value) + def getmember(self, context, mapping, key): raise error.ParseError(_('%r is not a dictionary') % self._value) @@ -171,6 +185,10 @@ self._joinfmt = joinfmt self.keytype = keytype # hint for 'x in y' where type(x) is unresolved + def contains(self, context, mapping, item): + item = unwrapastype(context, mapping, item, self.keytype) + return item in self._values + def getmember(self, context, mapping, key): # TODO: maybe split hybrid list/dict types? if not util.safehasattr(self._values, 'get'): @@ -255,6 +273,10 @@ def tomap(self): return self._makemap(self._key) + def contains(self, context, mapping, item): + w = makewrapped(context, mapping, self._value) + return w.contains(context, mapping, item) + def getmember(self, context, mapping, key): w = makewrapped(context, mapping, self._value) return w.getmember(context, mapping, key) @@ -302,6 +324,9 @@ self._tmpl = tmpl self._defaultsep = sep + def contains(self, context, mapping, item): + raise error.ParseError(_('not comparable')) + def getmember(self, context, mapping, key): raise error.ParseError(_('not a dictionary')) @@ -371,6 +396,10 @@ self._make = make self._args = args + def contains(self, context, mapping, item): + item = stringify(context, mapping, item) + return item in self.tovalue(context, mapping) + def _gen(self, context): return self._make(context, *self._args) diff -r 80f423a14c90 -r fb874fc1d9b4 tests/test-command-template.t --- a/tests/test-command-template.t Wed Mar 21 12:06:18 2018 +0900 +++ b/tests/test-command-template.t Mon Mar 19 00:23:20 2018 +0900 @@ -4166,6 +4166,15 @@ 1 0 + $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n' + t + $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n' + t + $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n' + f + $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n' + t + Test revset function $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'