From 5bb8b9134b0bb35a73c76657f41cafa3e4361fcd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 16 Dec 2016 19:19:02 +0200 Subject: [PATCH] Issue #18896: Python function can now have more than 255 parameters. collections.namedtuple() now supports tuples with more than 255 elements. --- Doc/whatsnew/3.7.rst | 5 +++-- Include/code.h | 7 +++---- Lib/test/test_collections.py | 3 +-- Lib/test/test_compile.py | 11 ++--------- Lib/test/test_keywordonlyarg.py | 24 ++++++------------------ Lib/test/test_sys.py | 2 +- Misc/NEWS | 3 +++ Objects/codeobject.c | 20 +++++++++++++------- Python/ast.c | 5 ----- Python/ceval.c | 2 +- 10 files changed, 33 insertions(+), 49 deletions(-) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 90ef759c4fa..ac3a151638d 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -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 diff --git a/Include/code.h b/Include/code.h index c5fce3c9632..385258f93ce 100644 --- a/Include/code.h +++ b/Include/code.h @@ -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. diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 87454cc6704..76c71392dd3 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -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) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 409ec86c75c..4a7230f6711 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -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): diff --git a/Lib/test/test_keywordonlyarg.py b/Lib/test/test_keywordonlyarg.py index d82e33d9733..2cf8a89a078 100644 --- a/Lib/test/test_keywordonlyarg.py +++ b/Lib/test/test_keywordonlyarg.py @@ -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, "", "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, "", "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, "", "single") def testTooManyPositionalErrorMessage(self): def f(a, b=None, *, c=None): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 828421c23bc..e6d8e5082ff 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -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) diff --git a/Misc/NEWS b/Misc/NEWS index cbdef27ba5c..9d1ccfd07c0 100644 --- 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. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 788818d303d..0857554adc3 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -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); } diff --git a/Python/ast.c b/Python/ast.c index f07bb1685cb..5c5738f6ed5 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -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 */ diff --git a/Python/ceval.c b/Python/ceval.c index fc11117109c..f7ee0418641 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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) {