diff options
author | Pablo Galindo <Pablogsal@gmail.com> | 2019-04-29 12:36:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-29 12:36:57 (GMT) |
commit | 8c77b8cb9188165a123f2512026e3629bf03dc9b (patch) | |
tree | 863ea19f5f2c8ec179c32b3d06dc8366859ae26e /Python/ceval.c | |
parent | 99fcc616d400cd31af0733c3f8cc93bcc1d32a44 (diff) | |
download | cpython-8c77b8cb9188165a123f2512026e3629bf03dc9b.zip cpython-8c77b8cb9188165a123f2512026e3629bf03dc9b.tar.gz cpython-8c77b8cb9188165a123f2512026e3629bf03dc9b.tar.bz2 |
bpo-36540: PEP 570 -- Implementation (GH-12701)
This commit contains the implementation of PEP570: Python positional-only parameters.
* Update Grammar/Grammar with new typedarglist and varargslist
* Regenerate grammar files
* Update and regenerate AST related files
* Update code object
* Update marshal.c
* Update compiler and symtable
* Regenerate importlib files
* Update callable objects
* Implement positional-only args logic in ceval.c
* Regenerate frozen data
* Update standard library to account for positional-only args
* Add test file for positional-only args
* Update other test files to account for positional-only args
* Add News entry
* Update inspect module and related tests
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 118 |
1 files changed, 100 insertions, 18 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index ccd0427..8ae273e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3694,10 +3694,10 @@ missing_arguments(PyCodeObject *co, Py_ssize_t missing, Py_ssize_t defcount, return; if (positional) { start = 0; - end = co->co_argcount - defcount; + end = co->co_posonlyargcount + co->co_argcount - defcount; } else { - start = co->co_argcount; + start = co->co_posonlyargcount + co->co_argcount; end = start + co->co_kwonlyargcount; } for (i = start; i < end; i++) { @@ -3724,23 +3724,25 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, Py_ssize_t kwonly_given = 0; Py_ssize_t i; PyObject *sig, *kwonly_sig; + Py_ssize_t co_posonlyargcount = co->co_posonlyargcount; Py_ssize_t co_argcount = co->co_argcount; + Py_ssize_t total_positional = co_argcount + co_posonlyargcount; assert((co->co_flags & CO_VARARGS) == 0); /* Count missing keyword-only args. */ - for (i = co_argcount; i < co_argcount + co->co_kwonlyargcount; i++) { + for (i = total_positional; i < total_positional + co->co_kwonlyargcount; i++) { if (GETLOCAL(i) != NULL) { kwonly_given++; } } if (defcount) { - Py_ssize_t atleast = co_argcount - defcount; + Py_ssize_t atleast = total_positional - defcount; plural = 1; - sig = PyUnicode_FromFormat("from %zd to %zd", atleast, co_argcount); + sig = PyUnicode_FromFormat("from %zd to %zd", atleast, total_positional); } else { - plural = (co_argcount != 1); - sig = PyUnicode_FromFormat("%zd", co_argcount); + plural = (total_positional != 1); + sig = PyUnicode_FromFormat("%zd", total_positional); } if (sig == NULL) return; @@ -3772,6 +3774,67 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, Py_DECREF(kwonly_sig); } +static int +positional_only_passed_as_keyword(PyCodeObject *co, Py_ssize_t kwcount, + PyObject* const* kwnames) +{ + int posonly_conflicts = 0; + PyObject* posonly_names = PyList_New(0); + + for(int k=0; k < co->co_posonlyargcount; k++){ + PyObject* posonly_name = PyTuple_GET_ITEM(co->co_varnames, k); + + for (int k2=0; k2<kwcount; k2++){ + /* Compare the pointers first and fallback to PyObject_RichCompareBool*/ + PyObject* kwname = kwnames[k2]; + if (kwname == posonly_name){ + if(PyList_Append(posonly_names, kwname) != 0) { + goto fail; + } + posonly_conflicts++; + continue; + } + + int cmp = PyObject_RichCompareBool(posonly_name, kwname, Py_EQ); + + if ( cmp > 0) { + if(PyList_Append(posonly_names, kwname) != 0) { + goto fail; + } + posonly_conflicts++; + } else if (cmp < 0) { + goto fail; + } + + } + } + if (posonly_conflicts) { + PyObject* comma = PyUnicode_FromString(", "); + if (comma == NULL) { + goto fail; + } + PyObject* error_names = PyUnicode_Join(comma, posonly_names); + Py_DECREF(comma); + if (error_names == NULL) { + goto fail; + } + PyErr_Format(PyExc_TypeError, + "%U() got some positional-only arguments passed" + " as keyword arguments: '%U'", + co->co_name, error_names); + Py_DECREF(error_names); + goto fail; + } + + Py_DECREF(posonly_names); + return 0; + +fail: + Py_XDECREF(posonly_names); + return 1; + +} + /* This is gonna seem *real weird*, but if you put some other code between PyEval_EvalFrame() and _PyEval_EvalFrameDefault() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ @@ -3791,8 +3854,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **fastlocals, **freevars; PyThreadState *tstate; PyObject *x, *u; - const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; - Py_ssize_t i, n; + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount + co->co_posonlyargcount; + Py_ssize_t i, j, n; PyObject *kwdict; if (globals == NULL) { @@ -3826,14 +3889,28 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, kwdict = NULL; } + /* Copy positional only arguments into local variables */ + if (argcount > co->co_argcount + co->co_posonlyargcount) { + n = co->co_posonlyargcount; + } + else { + n = argcount; + } + for (j = 0; j < n; j++) { + x = args[j]; + Py_INCREF(x); + SETLOCAL(j, x); + } + + /* Copy positional arguments into local variables */ - if (argcount > co->co_argcount) { - n = co->co_argcount; + if (argcount > co->co_argcount + co->co_posonlyargcount) { + n += co->co_argcount; } else { n = argcount; } - for (i = 0; i < n; i++) { + for (i = j; i < n; i++) { x = args[i]; Py_INCREF(x); SETLOCAL(i, x); @@ -3866,7 +3943,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Speed hack: do raw pointer compares. As names are normally interned this should almost always hit. */ co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item; - for (j = 0; j < total_args; j++) { + for (j = co->co_posonlyargcount; j < total_args; j++) { PyObject *name = co_varnames[j]; if (name == keyword) { goto kw_found; @@ -3874,7 +3951,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } /* Slow fallback, just in case */ - for (j = 0; j < total_args; j++) { + for (j = co->co_posonlyargcount; j < total_args; j++) { PyObject *name = co_varnames[j]; int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ); if (cmp > 0) { @@ -3887,6 +3964,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, assert(j >= total_args); if (kwdict == NULL) { + + if (co->co_posonlyargcount && positional_only_passed_as_keyword(co, kwcount, kwnames)) { + goto fail; + } + PyErr_Format(PyExc_TypeError, "%U() got an unexpected keyword argument '%S'", co->co_name, keyword); @@ -3910,14 +3992,14 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } /* Check the number of positional arguments */ - if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { + if ((argcount > co->co_argcount + co->co_posonlyargcount) && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); goto fail; } /* Add missing positional arguments (copy default values from defs) */ - if (argcount < co->co_argcount) { - Py_ssize_t m = co->co_argcount - defcount; + if (argcount < co->co_posonlyargcount + co->co_argcount) { + Py_ssize_t m = co->co_posonlyargcount + co->co_argcount - defcount; Py_ssize_t missing = 0; for (i = argcount; i < m; i++) { if (GETLOCAL(i) == NULL) { @@ -3944,7 +4026,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Add missing keyword arguments (copy default values from kwdefs) */ if (co->co_kwonlyargcount > 0) { Py_ssize_t missing = 0; - for (i = co->co_argcount; i < total_args; i++) { + for (i = co->co_posonlyargcount + co->co_argcount; i < total_args; i++) { PyObject *name; if (GETLOCAL(i) != NULL) continue; |