[Python-checkins] cpython: Issue #9232: Support trailing commas in function declarations.

robert.collins python-checkins at python.org
Tue Aug 11 22:00:17 CEST 2015


https://hg.python.org/cpython/rev/419ceb531bab
changeset:   97360:419ceb531bab
user:        Robert Collins <rbtcollins at hp.com>
date:        Wed Aug 12 08:00:06 2015 +1200
summary:
  Issue #9232: Support trailing commas in function declarations.

For example, "def f(*, a = 3,): pass" is now legal.

Patch from Mark Dickinson.

files:
  Doc/reference/compound_stmts.rst |    8 +-
  Grammar/Grammar                  |   17 +-
  Lib/test/test_grammar.py         |   38 +++++
  Misc/NEWS                        |    4 +
  Python/ast.c                     |   23 ++-
  Python/graminit.c                |  122 ++++++++++--------
  6 files changed, 139 insertions(+), 73 deletions(-)


diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -471,10 +471,10 @@
    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`
diff --git a/Grammar/Grammar b/Grammar/Grammar
--- a/Grammar/Grammar
+++ b/Grammar/Grammar
@@ -27,13 +27,18 @@
 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
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -295,6 +295,10 @@
         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 @@
         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 @@
         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
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,10 @@
 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
diff --git a/Python/ast.c b/Python/ast.c
--- a/Python/ast.c
+++ b/Python/ast.c
@@ -1260,16 +1260,20 @@
        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 @@
                 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;
diff --git a/Python/graminit.c b/Python/graminit.c
--- a/Python/graminit.c
+++ b/Python/graminit.c
@@ -204,11 +204,13 @@
     {32, 7},
     {0, 6},
 };
-static arc arcs_9_7[2] = {
+static arc arcs_9_7[3] = {
     {30, 12},
     {34, 3},
-};
-static arc arcs_9_8[1] = {
+    {0, 7},
+};
+static arc arcs_9_8[2] = {
+    {32, 13},
     {0, 8},
 };
 static arc arcs_9_9[2] = {
@@ -221,35 +223,39 @@
     {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},
-};
-static arc arcs_9_15[1] = {
+    {0, 15},
+};
+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[1] = {
-    {26, 13},
-};
-static state states_9[18] = {
+static arc arcs_9_17[3] = {
+    {32, 15},
+    {31, 18},
+    {0, 17},
+};
+static arc arcs_9_18[1] = {
+    {26, 14},
+};
+static state states_9[19] = {
     {3, arcs_9_0},
     {3, arcs_9_1},
     {3, arcs_9_2},
@@ -257,17 +263,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 @@
     {32, 7},
     {0, 6},
 };
-static arc arcs_11_7[2] = {
+static arc arcs_11_7[3] = {
     {36, 12},
     {34, 3},
-};
-static arc arcs_11_8[1] = {
+    {0, 7},
+};
+static arc arcs_11_8[2] = {
+    {32, 13},
     {0, 8},
 };
 static arc arcs_11_9[2] = {
@@ -336,35 +345,39 @@
     {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},
-};
-static arc arcs_11_15[1] = {
+    {0, 15},
+};
+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[1] = {
-    {26, 13},
-};
-static state states_11[18] = {
+static arc arcs_11_17[3] = {
+    {32, 15},
+    {31, 18},
+    {0, 17},
+};
+static arc arcs_11_18[1] = {
+    {26, 14},
+};
+static state states_11[19] = {
     {3, arcs_11_0},
     {3, arcs_11_1},
     {3, arcs_11_2},
@@ -372,17 +385,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 @@
      "\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"},

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list