parser: extend buildargsdict() to support arbitrary number of **kwargs
Prepares for adding dict(key1=value1, ...) template function. More tests
will be added later.
--- a/mercurial/parser.py Sat Apr 08 20:07:37 2017 +0900
+++ b/mercurial/parser.py Mon Apr 03 22:07:09 2017 +0900
@@ -94,41 +94,55 @@
return t
def splitargspec(spec):
- """Parse spec of function arguments into (poskeys, varkey, keys)
+ """Parse spec of function arguments into (poskeys, varkey, keys, optkey)
>>> splitargspec('')
- ([], None, [])
+ ([], None, [], None)
>>> splitargspec('foo bar')
- ([], None, ['foo', 'bar'])
- >>> splitargspec('foo *bar baz')
- (['foo'], 'bar', ['baz'])
+ ([], None, ['foo', 'bar'], None)
+ >>> splitargspec('foo *bar baz **qux')
+ (['foo'], 'bar', ['baz'], 'qux')
>>> splitargspec('*foo')
- ([], 'foo', [])
+ ([], 'foo', [], None)
+ >>> splitargspec('**foo')
+ ([], None, [], 'foo')
"""
- pre, sep, post = spec.partition('*')
+ optkey = None
+ pre, sep, post = spec.partition('**')
+ if sep:
+ posts = post.split()
+ if not posts:
+ raise error.ProgrammingError('no **optkey name provided')
+ if len(posts) > 1:
+ raise error.ProgrammingError('excessive **optkey names provided')
+ optkey = posts[0]
+
+ pre, sep, post = pre.partition('*')
pres = pre.split()
posts = post.split()
if sep:
if not posts:
raise error.ProgrammingError('no *varkey name provided')
- return pres, posts[0], posts[1:]
- return [], None, pres
+ return pres, posts[0], posts[1:], optkey
+ return [], None, pres, optkey
def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode):
"""Build dict from list containing positional and keyword arguments
- Arguments are specified by a tuple of ``(poskeys, varkey, keys)`` where
+ Arguments are specified by a tuple of ``(poskeys, varkey, keys, optkey)``
+ where
- ``poskeys``: list of names of positional arguments
- ``varkey``: optional argument name that takes up remainder
- ``keys``: list of names that can be either positional or keyword arguments
+ - ``optkey``: optional argument name that takes up excess keyword arguments
If ``varkey`` specified, all ``keys`` must be given as keyword arguments.
Invalid keywords, too few positional arguments, or too many positional
arguments are rejected, but missing keyword arguments are just omitted.
"""
- poskeys, varkey, keys = argspec
+ poskeys, varkey, keys, optkey = argspec
kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode),
len(trees))
if kwstart < len(poskeys):
@@ -150,20 +164,26 @@
for k, x in zip(keys, trees[len(args):kwstart]):
args[k] = x
# remainder should be keyword arguments
+ if optkey:
+ args[optkey] = {}
for x in trees[kwstart:]:
if x[0] != keyvaluenode or x[1][0] != keynode:
raise error.ParseError(_("%(func)s got an invalid argument")
% {'func': funcname})
k = x[1][1]
- if k not in keys:
+ if k in keys:
+ d = args
+ elif not optkey:
raise error.ParseError(_("%(func)s got an unexpected keyword "
"argument '%(key)s'")
% {'func': funcname, 'key': k})
- if k in args:
+ else:
+ d = args[optkey]
+ if k in d:
raise error.ParseError(_("%(func)s got multiple values for keyword "
"argument '%(key)s'")
% {'func': funcname, 'key': k})
- args[k] = x[2]
+ d[k] = x[2]
return args
def unescapestr(s):
--- a/mercurial/templater.py Sat Apr 08 20:07:37 2017 +0900
+++ b/mercurial/templater.py Mon Apr 03 22:07:09 2017 +0900
@@ -467,7 +467,19 @@
def _buildfuncargs(exp, context, curmethods, funcname, argspec):
"""Compile parsed tree of function arguments into list or dict of
- (func, data) pairs"""
+ (func, data) pairs
+
+ >>> context = engine(lambda t: (runsymbol, t))
+ >>> def fargs(expr, argspec):
+ ... x = _parseexpr(expr)
+ ... n = getsymbol(x[1])
+ ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
+ >>> sorted(fargs('a(l=1, k=2)', 'k l m').keys())
+ ['k', 'l']
+ >>> args = fargs('a(opts=1, k=2)', '**opts')
+ >>> args.keys(), sorted(args['opts'].keys())
+ (['opts'], ['k', 'opts'])
+ """
def compiledict(xs):
return dict((k, compileexp(x, context, curmethods))
for k, x in xs.iteritems())
@@ -479,12 +491,14 @@
return compilelist(getlist(exp))
# function with argspec: return dict of named args
- _poskeys, varkey, _keys = argspec = parser.splitargspec(argspec)
+ _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
keyvaluenode='keyvalue', keynode='symbol')
compargs = {}
if varkey:
compargs[varkey] = compilelist(treeargs.pop(varkey))
+ if optkey:
+ compargs[optkey] = compiledict(treeargs.pop(optkey))
compargs.update(compiledict(treeargs))
return compargs