summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2019-04-29 12:36:57 (GMT)
committerGitHub <noreply@github.com>2019-04-29 12:36:57 (GMT)
commit8c77b8cb9188165a123f2512026e3629bf03dc9b (patch)
tree863ea19f5f2c8ec179c32b3d06dc8366859ae26e /Python/ceval.c
parent99fcc616d400cd31af0733c3f8cc93bcc1d32a44 (diff)
downloadcpython-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.c118
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;