]> granicus.if.org Git - python/commitdiff
Issue #9232: Support trailing commas in function declarations.
authorRobert Collins <rbtcollins@hp.com>
Tue, 11 Aug 2015 20:00:06 +0000 (08:00 +1200)
committerRobert Collins <rbtcollins@hp.com>
Tue, 11 Aug 2015 20:00:06 +0000 (08:00 +1200)
For example, "def f(*, a = 3,): pass" is now legal.

Patch from Mark Dickinson.

Doc/reference/compound_stmts.rst
Grammar/Grammar
Lib/test/test_grammar.py
Misc/NEWS
Python/ast.c
Python/graminit.c

index 01cfd6dbd2564a15c0ca5da3ef9d9c1a424a8789..00e64763a0e7595dd52590481d0caee8ec2a2995 100644 (file)
@@ -471,10 +471,10 @@ A function definition defines a user-defined function object (see section
    decorators: `decorator`+
    decorator: "@" `dotted_name` ["(" [`parameter_list` [","]] ")"] NEWLINE
    dotted_name: `identifier` ("." `identifier`)*
-   parameter_list: (`defparameter` ",")*
-                 : | "*" [`parameter`] ("," `defparameter`)* ["," "**" `parameter`]
-                 : | "**" `parameter`
-                 : | `defparameter` [","] )
+   parameter_list: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]]
+                 : | `parameter_list_starargs`
+   parameter_list_starargs: "*" [`parameter`] ("," `defparameter`)* ["," ["**" `parameter` [","]]]
+                         : | "**" `parameter` [","]
    parameter: `identifier` [":" `expression`]
    defparameter: `parameter` ["=" `expression`]
    funcname: `identifier`
index 99fcea026a377df713b68c9ebbe2567a94c3504e..14bdecd0e25d506667eaab8aeee9e36ad99bfdda 100644 (file)
@@ -27,13 +27,18 @@ async_funcdef: ASYNC funcdef
 funcdef: 'def' NAME parameters ['->' test] ':' suite
 
 parameters: '(' [typedargslist] ')'
-typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [','
-       ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]]
-     |  '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef)
+typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
+        '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+      | '**' tfpdef [',']]]
+  | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+  | '**' tfpdef [','])
 tfpdef: NAME [':' test]
-varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [','
-       ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]]
-     |  '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef)
+varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+        '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+      | '**' vfpdef [',']]]
+  | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+  | '**' vfpdef [',']
+)
 vfpdef: NAME
 
 stmt: simple_stmt | compound_stmt
index ec3d7833f7ea5cedcc94b2248afb44e92fd5dc7b..8f8d71ce85e3e4a6e5f6121b4a8821a7e9cc1669 100644 (file)
@@ -295,6 +295,10 @@ class GrammarTests(unittest.TestCase):
         pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200)
         pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100)
 
+        self.assertRaises(SyntaxError, eval, "def f(*): pass")
+        self.assertRaises(SyntaxError, eval, "def f(*,): pass")
+        self.assertRaises(SyntaxError, eval, "def f(*, **kwds): pass")
+
         # keyword arguments after *arglist
         def f(*args, **kwargs):
             return args, kwargs
@@ -352,6 +356,23 @@ class GrammarTests(unittest.TestCase):
         check_syntax_error(self, "f(*g(1=2))")
         check_syntax_error(self, "f(**g(1=2))")
 
+        # Check trailing commas are permitted in funcdef argument list
+        def f(a,): pass
+        def f(*args,): pass
+        def f(**kwds,): pass
+        def f(a, *args,): pass
+        def f(a, **kwds,): pass
+        def f(*args, b,): pass
+        def f(*, b,): pass
+        def f(*args, **kwds,): pass
+        def f(a, *args, b,): pass
+        def f(a, *, b,): pass
+        def f(a, *args, **kwds,): pass
+        def f(*args, b, **kwds,): pass
+        def f(*, b, **kwds,): pass
+        def f(a, *args, b, **kwds,): pass
+        def f(a, *, b, **kwds,): pass
+
     def test_lambdef(self):
         ### lambdef: 'lambda' [varargslist] ':' test
         l1 = lambda : 0
@@ -370,6 +391,23 @@ class GrammarTests(unittest.TestCase):
         self.assertEqual(l6(1,2), 1+2+20)
         self.assertEqual(l6(1,2,k=10), 1+2+10)
 
+        # check that trailing commas are permitted
+        l10 = lambda a,: 0
+        l11 = lambda *args,: 0
+        l12 = lambda **kwds,: 0
+        l13 = lambda a, *args,: 0
+        l14 = lambda a, **kwds,: 0
+        l15 = lambda *args, b,: 0
+        l16 = lambda *, b,: 0
+        l17 = lambda *args, **kwds,: 0
+        l18 = lambda a, *args, b,: 0
+        l19 = lambda a, *, b,: 0
+        l20 = lambda a, *args, **kwds,: 0
+        l21 = lambda *args, b, **kwds,: 0
+        l22 = lambda *, b, **kwds,: 0
+        l23 = lambda a, *args, b, **kwds,: 0
+        l24 = lambda a, *, b, **kwds,: 0
+
 
     ### stmt: simple_stmt | compound_stmt
     # Tested below
index 1aa6b09b7658c408879d57b5bd5e2cb78e9e74d8..c2ea6d658db5ba8780afef105dafe43c6e6304ba 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,10 @@ Release date: XXXX-XX-XX
 Core and Builtins
 -----------------
 
+- Issue #9232: Modify Python's grammar to allow trailing commas in the
+  argument list of a function declaration.  For example, "def f(*, a =
+  3,): pass" is now legal. Patch from Mark Dickinson.
+
 - Issue #24667: Resize odict in all cases that the underlying dict resizes.
 
 Library
index b572088a46c9e84a9707edf0e9e57e4bfc82af91..c1c3907b91c101fd3f38a7b41d45aae72c9cec85 100644 (file)
@@ -1260,16 +1260,20 @@ ast_for_arguments(struct compiling *c, const node *n)
        and varargslist (lambda definition).
 
        parameters: '(' [typedargslist] ')'
-       typedargslist: ((tfpdef ['=' test] ',')*
-           ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef]
-           | '**' tfpdef)
-           | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
+       typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
+               '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+             | '**' tfpdef [',']]]
+         | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+         | '**' tfpdef [','])
        tfpdef: NAME [':' test]
-       varargslist: ((vfpdef ['=' test] ',')*
-           ('*' [vfpdef] (',' vfpdef ['=' test])*  [',' '**' vfpdef]
-           | '**' vfpdef)
-           | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
+       varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+               '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+             | '**' vfpdef [',']]]
+         | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+         | '**' vfpdef [',']
+       )
        vfpdef: NAME
+
     */
     int i, j, k, nposargs = 0, nkwonlyargs = 0;
     int nposdefaults = 0, found_default = 0;
@@ -1371,7 +1375,8 @@ ast_for_arguments(struct compiling *c, const node *n)
                 i += 2; /* the name and the comma */
                 break;
             case STAR:
-                if (i+1 >= NCH(n)) {
+                if (i+1 >= NCH(n) ||
+                    (i+2 == NCH(n) && TYPE(CHILD(n, i+1)) == COMMA)) {
                     ast_error(c, CHILD(n, i),
                         "named arguments must follow bare *");
                     return NULL;
index 8212b2a5844d4b1f803f4b4a9cb382494df693a3..354dc121b029887f666cd01b710e8d252fdccb75 100644 (file)
@@ -204,11 +204,13 @@ static arc arcs_9_6[2] = {
     {32, 7},
     {0, 6},
 };
-static arc arcs_9_7[2] = {
+static arc arcs_9_7[3] = {
     {30, 12},
     {34, 3},
+    {0, 7},
 };
-static arc arcs_9_8[1] = {
+static arc arcs_9_8[2] = {
+    {32, 13},
     {0, 8},
 };
 static arc arcs_9_9[2] = {
@@ -221,35 +223,39 @@ static arc arcs_9_10[3] = {
     {0, 10},
 };
 static arc arcs_9_11[3] = {
-    {30, 13},
-    {32, 14},
+    {30, 14},
+    {32, 15},
     {0, 11},
 };
 static arc arcs_9_12[3] = {
     {32, 7},
-    {31, 15},
+    {31, 16},
     {0, 12},
 };
-static arc arcs_9_13[2] = {
-    {32, 14},
+static arc arcs_9_13[1] = {
     {0, 13},
 };
 static arc arcs_9_14[2] = {
-    {30, 16},
+    {32, 15},
+    {0, 14},
+};
+static arc arcs_9_15[3] = {
+    {30, 17},
     {34, 3},
+    {0, 15},
 };
-static arc arcs_9_15[1] = {
+static arc arcs_9_16[1] = {
     {26, 6},
 };
-static arc arcs_9_16[3] = {
-    {32, 14},
-    {31, 17},
-    {0, 16},
+static arc arcs_9_17[3] = {
+    {32, 15},
+    {31, 18},
+    {0, 17},
 };
-static arc arcs_9_17[1] = {
-    {26, 13},
+static arc arcs_9_18[1] = {
+    {26, 14},
 };
-static state states_9[18] = {
+static state states_9[19] = {
     {3, arcs_9_0},
     {3, arcs_9_1},
     {3, arcs_9_2},
@@ -257,17 +263,18 @@ static state states_9[18] = {
     {1, arcs_9_4},
     {4, arcs_9_5},
     {2, arcs_9_6},
-    {2, arcs_9_7},
-    {1, arcs_9_8},
+    {3, arcs_9_7},
+    {2, arcs_9_8},
     {2, arcs_9_9},
     {3, arcs_9_10},
     {3, arcs_9_11},
     {3, arcs_9_12},
-    {2, arcs_9_13},
+    {1, arcs_9_13},
     {2, arcs_9_14},
-    {1, arcs_9_15},
-    {3, arcs_9_16},
-    {1, arcs_9_17},
+    {3, arcs_9_15},
+    {1, arcs_9_16},
+    {3, arcs_9_17},
+    {1, arcs_9_18},
 };
 static arc arcs_10_0[1] = {
     {23, 1},
@@ -319,11 +326,13 @@ static arc arcs_11_6[2] = {
     {32, 7},
     {0, 6},
 };
-static arc arcs_11_7[2] = {
+static arc arcs_11_7[3] = {
     {36, 12},
     {34, 3},
+    {0, 7},
 };
-static arc arcs_11_8[1] = {
+static arc arcs_11_8[2] = {
+    {32, 13},
     {0, 8},
 };
 static arc arcs_11_9[2] = {
@@ -336,35 +345,39 @@ static arc arcs_11_10[3] = {
     {0, 10},
 };
 static arc arcs_11_11[3] = {
-    {36, 13},
-    {32, 14},
+    {36, 14},
+    {32, 15},
     {0, 11},
 };
 static arc arcs_11_12[3] = {
     {32, 7},
-    {31, 15},
+    {31, 16},
     {0, 12},
 };
-static arc arcs_11_13[2] = {
-    {32, 14},
+static arc arcs_11_13[1] = {
     {0, 13},
 };
 static arc arcs_11_14[2] = {
-    {36, 16},
+    {32, 15},
+    {0, 14},
+};
+static arc arcs_11_15[3] = {
+    {36, 17},
     {34, 3},
+    {0, 15},
 };
-static arc arcs_11_15[1] = {
+static arc arcs_11_16[1] = {
     {26, 6},
 };
-static arc arcs_11_16[3] = {
-    {32, 14},
-    {31, 17},
-    {0, 16},
+static arc arcs_11_17[3] = {
+    {32, 15},
+    {31, 18},
+    {0, 17},
 };
-static arc arcs_11_17[1] = {
-    {26, 13},
+static arc arcs_11_18[1] = {
+    {26, 14},
 };
-static state states_11[18] = {
+static state states_11[19] = {
     {3, arcs_11_0},
     {3, arcs_11_1},
     {3, arcs_11_2},
@@ -372,17 +385,18 @@ static state states_11[18] = {
     {1, arcs_11_4},
     {4, arcs_11_5},
     {2, arcs_11_6},
-    {2, arcs_11_7},
-    {1, arcs_11_8},
+    {3, arcs_11_7},
+    {2, arcs_11_8},
     {2, arcs_11_9},
     {3, arcs_11_10},
     {3, arcs_11_11},
     {3, arcs_11_12},
-    {2, arcs_11_13},
+    {1, arcs_11_13},
     {2, arcs_11_14},
-    {1, arcs_11_15},
-    {3, arcs_11_16},
-    {1, arcs_11_17},
+    {3, arcs_11_15},
+    {1, arcs_11_16},
+    {3, arcs_11_17},
+    {1, arcs_11_18},
 };
 static arc arcs_12_0[1] = {
     {23, 1},
@@ -1879,11 +1893,11 @@ static dfa dfas[85] = {
      "\000\000\100\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
     {264, "parameters", 0, 4, states_8,
      "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
-    {265, "typedargslist", 0, 18, states_9,
+    {265, "typedargslist", 0, 19, states_9,
      "\000\000\200\000\006\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
     {266, "tfpdef", 0, 4, states_10,
      "\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
-    {267, "varargslist", 0, 18, states_11,
+    {267, "varargslist", 0, 19, states_11,
      "\000\000\200\000\006\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
     {268, "vfpdef", 0, 2, states_12,
      "\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},