Patchwork [2,of,3,RFC] revset: add function to build dict of positional and keyword arguments

login
register
mail settings
Submitter Yuya Nishihara
Date June 29, 2015, 2:51 p.m.
Message ID <acaa4d55a0524e607e35.1435589490@mimosa>
Download mbox | patch
Permalink /patch/9813/
State Accepted
Headers show

Comments

Yuya Nishihara - June 29, 2015, 2:51 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1435393501 -32400
#      Sat Jun 27 17:25:01 2015 +0900
# Node ID acaa4d55a0524e607e354cc873b4008f4fe48f70
# Parent  164e1587bcf1164dad21f7f01b2ba160a011898d
revset: add function to build dict of positional and keyword arguments

Keyword arguments will be convenient for functions that will take more than
one optional or boolean flags. For example,

  file(pattern[, subrepos=false])
  subrepo([[pattern], status])

Because I don't think all functions should accept key=value syntax, getkwargs()
does not support variadic functions such as 'ancestor(*changeset)'.

The core logic is placed in the parser module because keyword arguments will
be more useful in the templater, where functions take more options. Test cases
will be added by the next patch.
Yuya Nishihara - June 30, 2015, 12:52 p.m.
On Mon, 29 Jun 2015 23:51:30 +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1435393501 -32400
> #      Sat Jun 27 17:25:01 2015 +0900
> # Node ID acaa4d55a0524e607e354cc873b4008f4fe48f70
> # Parent  164e1587bcf1164dad21f7f01b2ba160a011898d
> revset: add function to build dict of positional and keyword arguments
> 
> Keyword arguments will be convenient for functions that will take more than
> one optional or boolean flags. For example,
> 
>   file(pattern[, subrepos=false])
>   subrepo([[pattern], status])
> 
> Because I don't think all functions should accept key=value syntax, getkwargs()
> does not support variadic functions such as 'ancestor(*changeset)'.
> 
> The core logic is placed in the parser module because keyword arguments will
> be more useful in the templater, where functions take more options. Test cases
> will be added by the next patch.

> +def getkwargs(x, funcname, keys):
> +    return parser.buildargsdict(getlist(x), funcname, keys.split(),
> +                                keyvaluenode='keyvalue', keynode='symbol')

Perhaps I should call it as getargsdict(). It packs both positional and
keyword arguments.

Patch

diff --git a/mercurial/parser.py b/mercurial/parser.py
--- a/mercurial/parser.py
+++ b/mercurial/parser.py
@@ -91,6 +91,38 @@  class parser(object):
             return self.eval(t)
         return t
 
+def buildargsdict(trees, funcname, keys, 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.
+    """
+    if len(trees) > len(keys):
+        raise error.ParseError(_("%(func)s takes at most %(nargs)d arguments")
+                               % {'func': funcname, 'nargs': len(keys)})
+    args = {}
+    # consume positional arguments
+    for k, x in zip(keys, trees):
+        if x[0] == keyvaluenode:
+            break
+        args[k] = x
+    # remainder should be keyword arguments
+    for x in trees[len(args):]:
+        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:
+            raise error.ParseError(_("%(func)s got an unexpected keyword "
+                                     "argument '%(key)s'")
+                                   % {'func': funcname, 'key': k})
+        if k in args:
+            raise error.ParseError(_("%(func)s got multiple values for keyword "
+                                     "argument '%(key)s'")
+                                   % {'func': funcname, 'key': k})
+        args[k] = x[2]
+    return args
+
 def _prettyformat(tree, leafnodes, level, lines):
     if not isinstance(tree, tuple) or tree[0] in leafnodes:
         lines.append((level, str(tree)))
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -282,6 +282,10 @@  def getargs(x, min, max, err):
         raise error.ParseError(err)
     return l
 
+def getkwargs(x, funcname, keys):
+    return parser.buildargsdict(getlist(x), funcname, keys.split(),
+                                keyvaluenode='keyvalue', keynode='symbol')
+
 def isvalidsymbol(tree):
     """Examine whether specified ``tree`` is valid ``symbol`` or not
     """