Issue #18896: Python function can now have more than 255 parameters.
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 16 Dec 2016 17:19:02 +0000 (19:19 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Fri, 16 Dec 2016 17:19:02 +0000 (19:19 +0200)
collections.namedtuple() now supports tuples with more than 255 elements.

Doc/whatsnew/3.7.rst
Include/code.h
Lib/test/test_collections.py
Lib/test/test_compile.py
Lib/test/test_keywordonlyarg.py
Lib/test/test_sys.py
Misc/NEWS
Objects/codeobject.c
Python/ast.c
Python/ceval.c

index 90ef759c4fa0f534b6ac67f4937cbf361396c396..ac3a151638ddd243d257780ac6f93ff272bcc6e5 100644 (file)
@@ -75,8 +75,9 @@ New Features
 Other Language Changes
 ======================
 
-* More than 255 arguments can now be passed to a function.
-  (Contributed by Serhiy Storchaka in :issue:`12844`.)
+* More than 255 arguments can now be passed to a function, and a function can
+  now have more than 255 parameters.
+  (Contributed by Serhiy Storchaka in :issue:`12844` and :issue:`18896`.)
 
 
 New Modules
index c5fce3c963269b9f30e8fa0e2ff9888905e36850..385258f93ce49110977892bf405e56ddfb86fc34 100644 (file)
@@ -37,7 +37,7 @@ typedef struct {
        for tracebacks and debuggers; otherwise, constant de-duplication
        would collapse identical functions/lambdas defined on different lines.
     */
-    unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
+    Py_ssize_t *co_cell2arg;    /* Maps cell vars which are arguments. */
     PyObject *co_filename;     /* unicode (where it was loaded from) */
     PyObject *co_name;         /* unicode (name, for reference) */
     PyObject *co_lnotab;       /* string (encoding addr<->lineno mapping) See
@@ -84,9 +84,8 @@ typedef struct {
 #define CO_FUTURE_GENERATOR_STOP  0x80000
 
 /* This value is found in the co_cell2arg array when the associated cell
-   variable does not correspond to an argument. The maximum number of
-   arguments is 255 (indexed up to 254), so 255 work as a special flag.*/
-#define CO_CELL_NOT_AN_ARG 255
+   variable does not correspond to an argument. */
+#define CO_CELL_NOT_AN_ARG (-1)
 
 /* This should be defined if a future statement modifies the syntax.
    For example, when a keyword is added.
index 87454cc6704cb0725a8a4ee5d507dc0c2f573ab0..76c71392dd372af154fbba3acb9870be9f19fb8b 100644 (file)
@@ -319,8 +319,7 @@ class TestNamedTuple(unittest.TestCase):
         self.assertEqual(Dot(1)._replace(d=999), (999,))
         self.assertEqual(Dot(1)._fields, ('d',))
 
-        # n = 5000
-        n = 254 # SyntaxError: more than 255 arguments:
+        n = 5000
         names = list(set(''.join([choice(string.ascii_letters)
                                   for j in range(10)]) for i in range(n)))
         n = len(names)
index 409ec86c75cd34d1be07230c6910b6f3066c365e..4a7230f6711298c0779255b26e083da294de2116 100644 (file)
@@ -401,16 +401,9 @@ if 1:
         self.assertNotIn((Ellipsis, Ellipsis), d)
 
     def test_annotation_limit(self):
-        # 16 bits are available for # of annotations, but only 8 bits are
-        # available for the parameter count, hence 255
-        # is the max. Ensure the result of too many annotations is a
-        # SyntaxError.
+        # more than 255 annotations, should compile ok
         s = "def f(%s): pass"
-        s %= ', '.join('a%d:%d' % (i,i) for i in range(256))
-        self.assertRaises(SyntaxError, compile, s, '?', 'exec')
-        # Test that the max # of annotations compiles.
-        s = "def f(%s): pass"
-        s %= ', '.join('a%d:%d' % (i,i) for i in range(255))
+        s %= ', '.join('a%d:%d' % (i,i) for i in range(300))
         compile(s, '?', 'exec')
 
     def test_mangling(self):
index d82e33d9733068ea4f6e17d58128d115a4eac135..2cf8a89a078e81d4c5f98fc5ee84ff814006aaf0 100644 (file)
@@ -51,24 +51,12 @@ class KeywordOnlyArgTestCase(unittest.TestCase):
         self.assertRaisesSyntaxError("def f(p, *, (k1, k2), **kw):\n  pass\n")
 
     def testSyntaxForManyArguments(self):
-        fundef = "def f("
-        for i in range(255):
-            fundef += "i%d, "%i
-        fundef += "*, key=100):\n pass\n"
-        self.assertRaisesSyntaxError(fundef)
-
-        fundef2 = "def foo(i,*,"
-        for i in range(255):
-            fundef2 += "i%d, "%i
-        fundef2 += "lastarg):\n  pass\n"
-        self.assertRaisesSyntaxError(fundef2)
-
-        # exactly 255 arguments, should compile ok
-        fundef3 = "def f(i,*,"
-        for i in range(253):
-            fundef3 += "i%d, "%i
-        fundef3 += "lastarg):\n  pass\n"
-        compile(fundef3, "<test>", "single")
+        # more than 255 positional arguments, should compile ok
+        fundef = "def f(%s):\n  pass\n" % ', '.join('i%d' % i for i in range(300))
+        compile(fundef, "<test>", "single")
+        # more than 255 keyword-only arguments, should compile ok
+        fundef = "def f(*, %s):\n  pass\n" % ', '.join('i%d' % i for i in range(300))
+        compile(fundef, "<test>", "single")
 
     def testTooManyPositionalErrorMessage(self):
         def f(a, b=None, *, c=None):
index 828421c23bc94eeff2c4b46e1e5651537f095b24..e6d8e5082ffc044424823bb5413c329152a2e86a 100644 (file)
@@ -926,7 +926,7 @@ class SizeofTest(unittest.TestCase):
             def inner():
                 return x
             return inner
-        check(get_cell2.__code__, size('6i13P') + 1)
+        check(get_cell2.__code__, size('6i13P') + calcsize('n'))
         # complex
         check(complex(0,1), size('2d'))
         # method_descriptor (descriptor object)
index cbdef27ba5c511ee8993c9a4cc69761c6ce659f5..9d1ccfd07c038e88cee9965422eed1016a445e8f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1
 Core and Builtins
 -----------------
 
+- Issue #18896: Python function can now have more than 255 parameters.
+  collections.namedtuple() now supports tuples with more than 255 elements.
+
 - Issue #26919: On Android, operating system data is now always encoded/decoded
   to/from UTF-8, instead of the locale encoding to avoid inconsistencies with
   os.fsencode() and os.fsdecode() which are already using UTF-8.
index 788818d303d95b33382309bc5ac9ddb81f88a880..0857554adc3bae3c83d86a9339cb4d74f5308672 100644 (file)
@@ -110,7 +110,7 @@ PyCode_New(int argcount, int kwonlyargcount,
            PyObject *lnotab)
 {
     PyCodeObject *co;
-    unsigned char *cell2arg = NULL;
+    Py_ssize_t *cell2arg = NULL;
     Py_ssize_t i, n_cellvars;
 
     /* Check argument types */
@@ -142,19 +142,25 @@ PyCode_New(int argcount, int kwonlyargcount,
     if (n_cellvars) {
         Py_ssize_t total_args = argcount + kwonlyargcount +
             ((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
-        Py_ssize_t alloc_size = sizeof(unsigned char) * n_cellvars;
         bool used_cell2arg = false;
-        cell2arg = PyMem_MALLOC(alloc_size);
-        if (cell2arg == NULL)
+        cell2arg = PyMem_NEW(Py_ssize_t, n_cellvars);
+        if (cell2arg == NULL) {
+            PyErr_NoMemory();
             return NULL;
-        memset(cell2arg, CO_CELL_NOT_AN_ARG, alloc_size);
+        }
         /* Find cells which are also arguments. */
         for (i = 0; i < n_cellvars; i++) {
             Py_ssize_t j;
             PyObject *cell = PyTuple_GET_ITEM(cellvars, i);
+            cell2arg[i] = CO_CELL_NOT_AN_ARG;
             for (j = 0; j < total_args; j++) {
                 PyObject *arg = PyTuple_GET_ITEM(varnames, j);
-                if (!PyUnicode_Compare(cell, arg)) {
+                int cmp = PyUnicode_Compare(cell, arg);
+                if (cmp == -1 && PyErr_Occurred()) {
+                    PyMem_FREE(cell2arg);
+                    return NULL;
+                }
+                if (cmp == 0) {
                     cell2arg[i] = j;
                     used_cell2arg = true;
                     break;
@@ -449,7 +455,7 @@ code_sizeof(PyCodeObject *co, void *unused)
 
     res = _PyObject_SIZE(Py_TYPE(co));
     if (co->co_cell2arg != NULL && co->co_cellvars != NULL)
-        res += PyTuple_GET_SIZE(co->co_cellvars) * sizeof(unsigned char);
+        res += PyTuple_GET_SIZE(co->co_cellvars) * sizeof(Py_ssize_t);
     return PyLong_FromSsize_t(res);
 }
 
index f07bb1685cb4b1b509a555374afb50ffd5c8edc0..5c5738f6ed5e06854b9555ebaccaff8926a82678 100644 (file)
@@ -1411,11 +1411,6 @@ ast_for_arguments(struct compiling *c, const node *n)
     if (!kwdefaults && nkwonlyargs)
         return NULL;
 
-    if (nposargs + nkwonlyargs > 255) {
-        ast_error(c, n, "more than 255 arguments");
-        return NULL;
-    }
-
     /* tfpdef: NAME [':' test]
        vfpdef: NAME
     */
index fc11117109cebb6cdcbc6e383cd0e8b8692440b5..f7ee04186417dc9174acaff9d1234fe0af2420c6 100644 (file)
@@ -4100,7 +4100,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
        vars into frame. */
     for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
         PyObject *c;
-        int arg;
+        Py_ssize_t arg;
         /* Possibly account for the cell variable being an argument. */
         if (co->co_cell2arg != NULL &&
             (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {