# HG changeset patch # User Yuya Nishihara # Date 1483943152 -32400 # Node ID c3a3896a9fa8b0e0681f7ca4fcce622244323559 # Parent ffd324eaf9944032a4e3670856b356dced76712f parser: extend buildargsdict() to support variable-length positional args This can simplify the argument parsing of followlines(). Tests are added by the next patch. diff -r ffd324eaf994 -r c3a3896a9fa8 mercurial/parser.py --- a/mercurial/parser.py Mon Jan 09 15:15:21 2017 +0900 +++ b/mercurial/parser.py Mon Jan 09 15:25:52 2017 +0900 @@ -90,22 +90,61 @@ return self.eval(t) return t -def buildargsdict(trees, funcname, keys, keyvaluenode, keynode): +def splitargspec(spec): + """Parse spec of function arguments into (poskeys, varkey, keys) + + >>> splitargspec('') + ([], None, []) + >>> splitargspec('foo bar') + ([], None, ['foo', 'bar']) + >>> splitargspec('foo *bar baz') + (['foo'], 'bar', ['baz']) + >>> splitargspec('*foo') + ([], 'foo', []) + """ + pre, sep, post = spec.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 + +def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode): """Build dict from list containing positional and keyword arguments - Invalid keywords or too many positional arguments are rejected, but - missing arguments are just omitted. + Arguments are specified by a tuple of ``(poskeys, varkey, keys)`` 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 + + 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 kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode), len(trees)) - if len(trees) > len(keys): + if kwstart < len(poskeys): + raise error.ParseError(_("%(func)s takes at least %(nargs)d positional " + "arguments") + % {'func': funcname, 'nargs': len(poskeys)}) + if not varkey and len(trees) > len(poskeys) + len(keys): raise error.ParseError(_("%(func)s takes at most %(nargs)d arguments") - % {'func': funcname, 'nargs': len(keys)}) + % {'func': funcname, + 'nargs': len(poskeys) + len(keys)}) args = {} # consume positional arguments - for k, x in zip(keys, trees[:kwstart]): + for k, x in zip(poskeys, trees[:kwstart]): args[k] = x - assert len(args) == kwstart + if varkey: + args[varkey] = trees[len(args):kwstart] + else: + for k, x in zip(keys, trees[len(args):kwstart]): + args[k] = x # remainder should be keyword arguments for x in trees[kwstart:]: if x[0] != keyvaluenode or x[1][0] != keynode: diff -r ffd324eaf994 -r c3a3896a9fa8 mercurial/revset.py --- a/mercurial/revset.py Mon Jan 09 15:15:21 2017 +0900 +++ b/mercurial/revset.py Mon Jan 09 15:25:52 2017 +0900 @@ -326,7 +326,7 @@ return l def getargsdict(x, funcname, keys): - return parser.buildargsdict(getlist(x), funcname, keys.split(), + return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys), keyvaluenode='keyvalue', keynode='symbol') def getset(repo, subset, x):