]> granicus.if.org Git - python/commitdiff
allow keyword args after *args in a function call
authorBenjamin Peterson <benjamin@python.org>
Tue, 19 Aug 2008 20:57:10 +0000 (20:57 +0000)
committerBenjamin Peterson <benjamin@python.org>
Tue, 19 Aug 2008 20:57:10 +0000 (20:57 +0000)
Doc/reference/expressions.rst
Grammar/Grammar
Lib/test/test_grammar.py
Lib/test/test_keywordonlyarg.py
Python/ast.c
Python/graminit.c

index 1e94d4079e3e8b1574277c823ce2c33dd6a51b18..945df61f16ac13a32d26b626a3363056a9333096 100644 (file)
@@ -612,11 +612,11 @@ of arguments:
    call: `primary` "(" [`argument_list` [","]
        : | `expression` `genexpr_for`] ")"
    argument_list: `positional_arguments` ["," `keyword_arguments`]
-                : ["," "*" `expression`]
-                : ["," "**" `expression`]
+                :   ["," "*" `expression`] ["," `keyword_arguments`]
+                :   ["," "**" `expression`]
                 : | `keyword_arguments` ["," "*" `expression`]
-                : ["," "**" `expression`]
-                : | "*" `expression` ["," "**" `expression`]
+                :   ["," `keyword_arguments`] ["," "**" `expression`]
+                : | "*" `expression` ["," `keyword_arguments`] ["," "**" `expression`]
                 : | "**" `expression`
    positional_arguments: `expression` ("," `expression`)*
    keyword_arguments: `keyword_item` ("," `keyword_item`)*
@@ -674,12 +674,13 @@ there were no excess keyword arguments.
 
 If the syntax ``*expression`` appears in the function call, ``expression`` must
 evaluate to a sequence.  Elements from this sequence are treated as if they were
-additional positional arguments; if there are positional arguments *x1*,...,*xN*
-, and ``expression`` evaluates to a sequence *y1*,...,*yM*, this is equivalent
-to a call with M+N positional arguments *x1*,...,*xN*,*y1*,...,*yM*.
+additional positional arguments; if there are positional arguments *x1*,...,
+*xN*, and ``expression`` evaluates to a sequence *y1*, ..., *yM*, this is
+equivalent to a call with M+N positional arguments *x1*, ..., *xN*, *y1*, ...,
+*yM*.
 
-A consequence of this is that although the ``*expression`` syntax appears
-*after* any keyword arguments, it is processed *before* the keyword arguments
+A consequence of this is that although the ``*expression`` syntax may appear
+*after* some keyword arguments, it is processed *before* the keyword arguments
 (and the ``**expression`` argument, if any -- see below).  So::
 
    >>> def f(a, b):
index 6111fae79b80e72e6af8d7c522b272c20bf57772..64816d9b2ce565cbcb6151286ab11a72f041d484 100644 (file)
@@ -113,7 +113,9 @@ dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
 
 classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
 
-arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
+arglist: (argument ',')* (argument [',']
+                         |'*' test (',' argument)* [',' '**' test] 
+                         |'**' test)
 argument: test [comp_for] | test '=' test  # Really [keyword '='] test
 
 comp_iter: comp_for | comp_if
index acfe1f1573378beae5453ae9ba891e1a0a20c84c..9a8846307ecb4fd6839f5eda6f21236024f14e08 100644 (file)
@@ -284,6 +284,14 @@ class GrammarTests(unittest.TestCase):
         pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200)
         pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100)
 
+        # keyword arguments after *arglist
+        def f(*args, **kwargs):
+            return args, kwargs
+        self.assertEquals(f(1, x=2, *[3, 4], y=5), ((1, 3, 4),
+                                                    {'x':2, 'y':5}))
+        self.assertRaises(SyntaxError, eval, "f(1, *(2,3), 4)")
+        self.assertRaises(SyntaxError, eval, "f(1, x=2, *(3,4), x=5)")
+
         # argument annotation tests
         def f(x) -> list: pass
         self.assertEquals(f.__annotations__, {'return': list})
index aa7d3db8946365ccbaee7a9c5ac25a68e9b27639..b771a6c0221fe57be053fb110ac2913f3f29be45 100644 (file)
@@ -75,7 +75,7 @@ class KeywordOnlyArgTestCase(unittest.TestCase):
 
     def testSyntaxErrorForFunctionCall(self):
         self.assertRaisesSyntaxError("f(p, k=1, p2)")
-        self.assertRaisesSyntaxError("f(p, *(1,2), k1=100)")
+        self.assertRaisesSyntaxError("f(p, k1=50, *(1,2), k1=100)")
 
     def testRaiseErrorFuncallWithUnexpectedKeywordArgument(self):
         self.assertRaises(TypeError, keywordonly_sum, ())
index 46bfd4e8034a81f8e9939433f9b2eb288861a1c3..6d2fa09b17dd34e8fbed4cdb35cc58ef8a716330 100644 (file)
@@ -1961,6 +1961,11 @@ ast_for_call(struct compiling *c, const node *n, expr_ty func)
                               "non-keyword arg after keyword arg");
                     return NULL;
                 }
+                if (vararg) {
+                    ast_error(CHILD(ch, 0),
+                              "only named arguments may follow *expression");
+                    return NULL;
+                }
                 e = ast_for_expr(c, CHILD(ch, 0));
                 if (!e)
                     return NULL;
index e26bcc5e26104c3d13c9f66b83ce162b5c470520..6f3787ac57a1d8109f08f1a6ef89fa2e4278ab34 100644 (file)
@@ -1598,7 +1598,8 @@ static arc arcs_73_5[2] = {
 static arc arcs_73_6[1] = {
        {0, 6},
 };
-static arc arcs_73_7[1] = {
+static arc arcs_73_7[2] = {
+       {161, 5},
        {32, 3},
 };
 static state states_73[8] = {
@@ -1609,7 +1610,7 @@ static state states_73[8] = {
        {4, arcs_73_4},
        {2, arcs_73_5},
        {1, arcs_73_6},
-       {1, arcs_73_7},
+       {2, arcs_73_7},
 };
 static arc arcs_74_0[1] = {
        {24, 1},