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.
--- 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)
--- 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
--- 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)
--- 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'