--- a/mercurial/hgweb/webutil.py Wed Mar 21 01:39:44 2018 +0900
+++ b/mercurial/hgweb/webutil.py Wed Mar 21 11:30:21 2018 +0900
@@ -713,6 +713,9 @@
def __copy__(self):
return sessionvars(copy.copy(self._vars), self._start)
+ def getmember(self, context, mapping, key):
+ return self._vars.get(key)
+
def itermaps(self, context):
separator = self._start
for key, value in sorted(self._vars.iteritems()):
--- a/mercurial/templatefuncs.py Wed Mar 21 01:39:44 2018 +0900
+++ b/mercurial/templatefuncs.py Wed Mar 21 11:30:21 2018 +0900
@@ -262,12 +262,13 @@
raise error.ParseError(_("get() expects two arguments"))
dictarg = evalwrapped(context, mapping, args[0])
- if not util.safehasattr(dictarg, 'getmember'):
+ key = evalfuncarg(context, mapping, args[1])
+ try:
+ return dictarg.getmember(context, mapping, key)
+ except error.ParseError as err:
# i18n: "get" is a keyword
- raise error.ParseError(_("get() expects a dict as first argument"))
-
- key = evalfuncarg(context, mapping, args[1])
- return dictarg.getmember(context, mapping, key)
+ hint = _("get() expects a dict as first argument")
+ raise error.ParseError(bytes(err), hint=hint)
@templatefunc('if(expr, then[, else])')
def if_(context, mapping, args):
--- a/mercurial/templateutil.py Wed Mar 21 01:39:44 2018 +0900
+++ b/mercurial/templateutil.py Wed Mar 21 11:30:21 2018 +0900
@@ -38,6 +38,14 @@
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
+ def getmember(self, context, mapping, key):
+ """Return a member item for the specified key
+
+ A returned object may be either a wrapped object or a pure value
+ depending on the self type.
+ """
+
+ @abc.abstractmethod
def itermaps(self, context):
"""Yield each template mapping"""
@@ -72,6 +80,10 @@
def __init__(self, value):
self._value = value
+ def getmember(self, context, mapping, key):
+ raise error.ParseError(_('%r is not a dictionary')
+ % pycompat.bytestr(self._value))
+
def itermaps(self, context):
raise error.ParseError(_('%r is not iterable of mappings')
% pycompat.bytestr(self._value))
@@ -91,6 +103,9 @@
def __init__(self, value):
self._value = value
+ def getmember(self, context, mapping, key):
+ raise error.ParseError(_('%r is not a dictionary') % self._value)
+
def itermaps(self, context):
raise error.ParseError(_('%r is not iterable of mappings')
% self._value)
@@ -196,6 +211,10 @@
def tomap(self):
return self._makemap(self._key)
+ def getmember(self, context, mapping, key):
+ w = makewrapped(context, mapping, self._value)
+ return w.getmember(context, mapping, key)
+
def itermaps(self, context):
yield self.tomap()
@@ -231,6 +250,9 @@
self._tmpl = tmpl
self._defaultsep = sep
+ def getmember(self, context, mapping, key):
+ raise error.ParseError(_('not a dictionary'))
+
def join(self, context, mapping, sep):
mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
if self._name:
@@ -294,6 +316,9 @@
def _gen(self, context):
return self._make(context, *self._args)
+ def getmember(self, context, mapping, key):
+ raise error.ParseError(_('not a dictionary'))
+
def itermaps(self, context):
raise error.ParseError(_('list of strings is not mappable'))
@@ -678,15 +703,13 @@
lm = context.overlaymap(mapping, d.tomap())
return runsymbol(context, lm, memb)
try:
- if util.safehasattr(d, 'getmember'):
- return d.getmember(context, mapping, memb)
- raise error.ParseError
- except error.ParseError:
+ return d.getmember(context, mapping, memb)
+ except error.ParseError as err:
sym = findsymbolicname(darg)
- if sym:
- raise error.ParseError(_("keyword '%s' has no member") % sym)
- else:
- raise error.ParseError(_("%r has no member") % pycompat.bytestr(d))
+ if not sym:
+ raise
+ hint = _("keyword '%s' does not support member operation") % sym
+ raise error.ParseError(bytes(err), hint=hint)
def runnegate(context, mapping, data):
data = evalinteger(context, mapping, data,
--- a/tests/test-command-template.t Wed Mar 21 01:39:44 2018 +0900
+++ b/tests/test-command-template.t Wed Mar 21 11:30:21 2018 +0900
@@ -3345,10 +3345,11 @@
default
$ hg log -R latesttag -l1 -T '{author.invalid}\n'
- hg: parse error: keyword 'author' has no member
+ hg: parse error: 'test' is not a dictionary
+ (keyword 'author' does not support member operation)
[255]
$ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
- hg: parse error: 'a' has no member
+ hg: parse error: 'a' is not a dictionary
[255]
Test the sub function of templating for expansion:
@@ -3851,7 +3852,8 @@
$ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
default
$ hg log -r 0 --template '{get(files, "should_fail")}\n'
- hg: parse error: get() expects a dict as first argument
+ hg: parse error: not a dictionary
+ (get() expects a dict as first argument)
[255]
Test json filter applied to hybrid object: