From 3f16b927b6791867124ec969049fde4d61df0157 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 9 Apr 2014 23:55:56 -0400 Subject: PEP 465: a dedicated infix operator for matrix multiplication (closes #21176) --- Python/ceval.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5db88be620..ab7afa90d5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1495,6 +1495,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BINARY_MATRIX_MULTIPLY) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = PyNumber_MatrixMultiply(left, right); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) + goto error; + DISPATCH(); + } + TARGET(BINARY_TRUE_DIVIDE) { PyObject *divisor = POP(); PyObject *dividend = TOP(); @@ -1685,6 +1697,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(INPLACE_MATRIX_MULTIPLY) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = PyNumber_InPlaceMatrixMultiply(left, right); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) + goto error; + DISPATCH(); + } + TARGET(INPLACE_TRUE_DIVIDE) { PyObject *divisor = POP(); PyObject *dividend = TOP(); -- cgit v1.2.1 From cb7bff4fcfe8f736d1095a0be0c363dfec14a564 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Jun 2014 15:59:28 +0200 Subject: Issue #21205: Add a new ``__qualname__`` attribute to generator, the qualified name, and use it in the representation of a generator (``repr(gen)``). The default name of the generator (``__name__`` attribute) is now get from the function instead of the code. Use ``gen.gi_code.co_name`` to get the name of the code. --- Python/ceval.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e14e77270c..3ede055070 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3401,10 +3401,11 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +static PyObject * +_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3597,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3616,16 @@ fail: /* Jump here from prelude on failure */ return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) +{ + return _PyEval_EvalCodeWithName(_co, globals, locals, + args, argcount, kws, kwcount, + defs, defcount, kwdefs, closure, + NULL, NULL); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4324,8 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); + PyObject *name = ((PyFunctionObject *)func) -> func_name; + PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; PyObject **d = NULL; int nd = 0; @@ -4355,10 +4368,11 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + return _PyEval_EvalCodeWithName((PyObject*)co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject * -- cgit v1.2.1 From c54b6df1ddcec5fa1d13efe50cf7b2a12388d8bc Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 13 Oct 2014 20:19:45 +0200 Subject: Issue #17636: Circular imports involving relative imports are now supported. --- Python/ceval.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2dbf591d45..4b1d6ca969 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4693,11 +4693,29 @@ static PyObject * import_from(PyObject *v, PyObject *name) { PyObject *x; + _Py_IDENTIFIER(__name__); + PyObject *fullmodname, *pkgname; x = PyObject_GetAttr(v, name); - if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) + return x; + /* Issue #17636: in case this failed because of a circular relative + import, try to fallback on reading the module directly from + sys.modules. */ + PyErr_Clear(); + pkgname = _PyObject_GetAttrId(v, &PyId___name__); + if (pkgname == NULL) + return NULL; + fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); + Py_DECREF(pkgname); + if (fullmodname == NULL) + return NULL; + x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); + if (x == NULL) PyErr_Format(PyExc_ImportError, "cannot import name %R", name); - } + else + Py_INCREF(x); + Py_DECREF(fullmodname); return x; } -- cgit v1.2.1 From b0a85d474df68ed8e72a01506fa74ceca3587aef Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 17 Feb 2015 10:14:30 +0200 Subject: Issue #22883: Got rid of outdated references to PyInt and PyString in comments. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e09ff3423c..5cefdcf088 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4606,7 +4606,7 @@ ext_call_fail: return result; } -/* Extract a slice index from a PyInt or PyLong or an object with the +/* Extract a slice index from a PyLong or an object with the nb_index slot defined, and store in *pi. Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX, and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1. -- cgit v1.2.1 From ef35d11c6986437b4eac74a6657377607c34841a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Mar 2015 23:35:27 +0100 Subject: Issue #23571: PyObject_Call(), PyCFunction_Call() and call_function() now raise a SystemError if a function returns a result and raises an exception. The SystemError is chained to the previous exception. Refactor also PyObject_Call() and PyCFunction_Call() to make them more readable. Remove some checks which became useless (duplicate checks). Change reviewed by Serhiy Storchaka. --- Python/ceval.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5cefdcf088..1c6089d5c0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3192,8 +3192,7 @@ fast_block_end: if (why != WHY_RETURN) retval = NULL; - assert((retval != NULL && !PyErr_Occurred()) - || (retval == NULL && PyErr_Occurred())); + assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: if (co->co_flags & CO_GENERATOR) { @@ -3254,7 +3253,7 @@ exit_eval_frame: f->f_executing = 0; tstate->frame = f->f_back; - return retval; + return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx"); } static void @@ -4119,13 +4118,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; -#ifdef Py_DEBUG - /* PyEval_CallObjectWithKeywords() must not be called with an exception - set, because it may clear it (directly or indirectly) - and so the caller looses its exception */ - assert(!PyErr_Occurred()); -#endif - if (arg == NULL) { arg = PyTuple_New(0); if (arg == NULL) @@ -4149,8 +4141,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) result = PyObject_Call(func, arg, kw); Py_DECREF(arg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } @@ -4253,11 +4243,15 @@ call_function(PyObject ***pp_stack, int oparg PyObject *self = PyCFunction_GET_SELF(func); if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); + + x = _Py_CheckFunctionResult(x, "call_function"); } else if (flags & METH_O && na == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); + + x = _Py_CheckFunctionResult(x, "call_function"); } else { err_args(func, flags, na); @@ -4277,7 +4271,8 @@ call_function(PyObject ***pp_stack, int oparg x = NULL; } } - } else { + } + else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); @@ -4299,9 +4294,9 @@ call_function(PyObject ***pp_stack, int oparg x = do_call(func, pp_stack, na, nk); READ_TIMESTAMP(*pintr1); Py_DECREF(func); + + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); /* Clear the stack of the function object. Also removes the arguments in case they weren't consumed already @@ -4313,8 +4308,7 @@ call_function(PyObject ***pp_stack, int oparg PCALL(PCALL_POP); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); return x; } @@ -4601,8 +4595,6 @@ ext_call_fail: Py_XDECREF(callargs); Py_XDECREF(kwdict); Py_XDECREF(stararg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } -- cgit v1.2.1 From e2e154b313a94ae72f8e0c05f7698d07c2ba1999 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Mar 2015 11:52:32 +0100 Subject: Issue #23571: Restore removed assert(!PyErr_Occurred()); in PyEval_CallObjectWithKeywords() Sorry Serhiy, I missed your explanation because the review email was moved to my Spam folder :-( --- Python/ceval.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 1c6089d5c0..25fbc0fc79 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4118,6 +4118,13 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; +#ifdef Py_DEBUG + /* PyEval_CallObjectWithKeywords() must not be called with an exception + set. It raises a new exception if parameters are invalid or if + PyTuple_New() fails, and so the original exception is lost. */ + assert(!PyErr_Occurred()); +#endif + if (arg == NULL) { arg = PyTuple_New(0); if (arg == NULL) -- cgit v1.2.1 From 814337d3955510ed32321e0c38239415d224b57b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 21 Mar 2015 15:04:43 +0100 Subject: Issue #23571: _Py_CheckFunctionResult() now gives the name of the function which returned an invalid result (result+error or no result without error) in the exception message. Add also unit test to check that the exception contains the name of the function. Special case: the final _PyEval_EvalFrameEx() check doesn't mention the function since it didn't execute a single function but a whole frame. --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 25fbc0fc79..d68cdc6292 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3253,7 +3253,7 @@ exit_eval_frame: f->f_executing = 0; tstate->frame = f->f_back; - return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx"); + return _Py_CheckFunctionResult(NULL, retval, "PyEval_EvalFrameEx"); } static void @@ -4251,14 +4251,14 @@ call_function(PyObject ***pp_stack, int oparg if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); - x = _Py_CheckFunctionResult(x, "call_function"); + x = _Py_CheckFunctionResult(func, x, NULL); } else if (flags & METH_O && na == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); - x = _Py_CheckFunctionResult(x, "call_function"); + x = _Py_CheckFunctionResult(func, x, NULL); } else { err_args(func, flags, na); -- cgit v1.2.1 From 9d6c9533eba49f69a10f9fba83b98bb46d07f2d3 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 15 Apr 2015 17:08:45 -0400 Subject: #23949: Improve tuple unpacking error messages. Patch by Arnon Yaari. --- Python/ceval.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d68cdc6292..2f3d3ad8b8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3825,9 +3825,17 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) if (w == NULL) { /* Iterator done, via error or exhaustion. */ if (!PyErr_Occurred()) { - PyErr_Format(PyExc_ValueError, - "need more than %d value%s to unpack", - i, i == 1 ? "" : "s"); + if (argcntafter == -1) { + PyErr_Format(PyExc_ValueError, + "not enough values to unpack (expected %d, got %d)", + argcnt, i); + } + else { + PyErr_Format(PyExc_ValueError, + "not enough values to unpack " + "(expected at least %d, got %d)", + argcnt + argcntafter, i); + } } goto Error; } @@ -3844,8 +3852,9 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) return 1; } Py_DECREF(w); - PyErr_Format(PyExc_ValueError, "too many values to unpack " - "(expected %d)", argcnt); + PyErr_Format(PyExc_ValueError, + "too many values to unpack (expected %d)", + argcnt); goto Error; } @@ -3857,8 +3866,9 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) ll = PyList_GET_SIZE(l); if (ll < argcntafter) { - PyErr_Format(PyExc_ValueError, "need more than %zd values to unpack", - argcnt + ll); + PyErr_Format(PyExc_ValueError, + "not enough values to unpack (expected at least %d, got %zd)", + argcnt + argcntafter, argcnt + ll); goto Error; } -- cgit v1.2.1 From 2571248c875b79a134b3a6fd4010c2a2e6e2c7aa Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 5 May 2015 20:16:41 -0400 Subject: PEP 448: additional unpacking generalizations (closes #2292) Patch by Neil Girdhar. --- Python/ceval.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 5 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2f3d3ad8b8..5ee9077332 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -12,8 +12,10 @@ #include "Python.h" #include "code.h" +#include "dictobject.h" #include "frameobject.h" #include "opcode.h" +#include "setobject.h" #include "structmember.h" #include @@ -2379,6 +2381,43 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET_WITH_IMPL(BUILD_TUPLE_UNPACK, _build_list_unpack) + TARGET(BUILD_LIST_UNPACK) + _build_list_unpack: { + int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; + int i; + PyObject *sum = PyList_New(0); + PyObject *return_value; + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + PyObject *none_val; + + none_val = _PyList_Extend((PyListObject *)sum, PEEK(i)); + if (none_val == NULL) { + Py_DECREF(sum); + goto error; + } + Py_DECREF(none_val); + } + + if (convert_to_tuple) { + return_value = PyList_AsTuple(sum); + Py_DECREF(sum); + if (return_value == NULL) + goto error; + } + else { + return_value = sum; + } + + while (oparg--) + Py_DECREF(POP()); + PUSH(return_value); + DISPATCH(); + } + TARGET(BUILD_SET) { PyObject *set = PySet_New(NULL); int err = 0; @@ -2398,14 +2437,127 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BUILD_SET_UNPACK) { + int i; + PyObject *sum = PySet_New(NULL); + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + if (_PySet_Update(sum, PEEK(i)) < 0) { + Py_DECREF(sum); + goto error; + } + } + + while (oparg--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + TARGET(BUILD_MAP) { PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; + while (--oparg >= 0) { + int err; + PyObject *key = TOP(); + PyObject *value = SECOND(); + STACKADJ(-2); + err = PyDict_SetItem(map, key, value); + Py_DECREF(value); + Py_DECREF(key); + if (err != 0) { + Py_DECREF(map); + goto error; + } + } PUSH(map); DISPATCH(); } + TARGET_WITH_IMPL(BUILD_MAP_UNPACK_WITH_CALL, _build_map_unpack) + TARGET(BUILD_MAP_UNPACK) + _build_map_unpack: { + int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; + int num_maps; + int function_location; + int i; + PyObject *sum = PyDict_New(); + if (sum == NULL) + goto error; + if (with_call) { + num_maps = oparg & 0xff; + function_location = (oparg>>8) & 0xff; + } + else { + num_maps = oparg; + } + + for (i = num_maps; i > 0; i--) { + PyObject *arg = PEEK(i); + if (with_call) { + PyObject *intersection = _PyDictView_Intersect(sum, arg); + + if (intersection == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyObject *func = ( + PEEK(function_location + num_maps)); + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + arg->ob_type->tp_name); + } + Py_DECREF(sum); + goto error; + } + + if (PySet_GET_SIZE(intersection)) { + Py_ssize_t idx = 0; + PyObject *key; + PyObject *func = PEEK(function_location + num_maps); + Py_hash_t hash; + _PySet_NextEntry(intersection, &idx, &key, &hash); + if (!PyUnicode_Check(key)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s keywords must be strings", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + } else { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s got multiple " + "values for keyword argument '%U'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + key); + } + Py_DECREF(intersection); + Py_DECREF(sum); + goto error; + } + Py_DECREF(intersection); + } + + if (PyDict_Update(sum, arg) < 0) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not a mapping", + arg->ob_type->tp_name); + } + Py_DECREF(sum); + goto error; + } + } + + while (num_maps--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + TARGET(STORE_MAP) { PyObject *key = TOP(); PyObject *value = SECOND(); @@ -3050,6 +3202,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto dispatch_opcode; } + #if USE_COMPUTED_GOTOS _unknown_opcode: #endif @@ -4557,6 +4710,12 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) kwdict = d; } } + if (nk > 0) { + kwdict = update_keyword_args(kwdict, nk, pp_stack, func); + if (kwdict == NULL) + goto ext_call_fail; + } + if (flags & CALL_FLAG_VAR) { stararg = EXT_POP(*pp_stack); if (!PyTuple_Check(stararg)) { @@ -4578,11 +4737,6 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) } nstar = PyTuple_GET_SIZE(stararg); } - if (nk > 0) { - kwdict = update_keyword_args(kwdict, nk, pp_stack, func); - if (kwdict == NULL) - goto ext_call_fail; - } callargs = update_star_args(na, nstar, stararg, pp_stack); if (callargs == NULL) goto ext_call_fail; -- cgit v1.2.1 From ea639832c36905ecb07caba111f614c4bbf9bdd6 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 11 May 2015 22:57:16 -0400 Subject: PEP 0492 -- Coroutines with async and await syntax. Issue #24017. --- Python/ceval.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 223 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5ee9077332..8acd08201e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1926,11 +1926,133 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto fast_block_end; } + TARGET(GET_AITER) { + getaiterfunc getter = NULL; + PyObject *iter = NULL; + PyObject *awaitable = NULL; + PyObject *obj = TOP(); + PyTypeObject *type = Py_TYPE(obj); + + if (type->tp_as_async != NULL) + getter = type->tp_as_async->am_aiter; + + if (getter != NULL) { + iter = (*getter)(obj); + Py_DECREF(obj); + if (iter == NULL) { + SET_TOP(NULL); + goto error; + } + } + else { + SET_TOP(NULL); + PyErr_Format( + PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + Py_DECREF(obj); + goto error; + } + + awaitable = _PyGen_GetAwaitableIter(iter); + if (awaitable == NULL) { + SET_TOP(NULL); + PyErr_Format( + PyExc_TypeError, + "'async for' received an invalid object " + "from __aiter__: %.100s", + Py_TYPE(iter)->tp_name); + + Py_DECREF(iter); + goto error; + } else + Py_DECREF(iter); + + SET_TOP(awaitable); + DISPATCH(); + } + + TARGET(GET_ANEXT) { + aiternextfunc getter = NULL; + PyObject *next_iter = NULL; + PyObject *awaitable = NULL; + PyObject *aiter = TOP(); + PyTypeObject *type = Py_TYPE(aiter); + + if (type->tp_as_async != NULL) + getter = type->tp_as_async->am_anext; + + if (getter != NULL) { + next_iter = (*getter)(aiter); + if (next_iter == NULL) { + goto error; + } + } + else { + PyErr_Format( + PyExc_TypeError, + "'async for' requires an iterator with " + "__anext__ method, got %.100s", + type->tp_name); + goto error; + } + + awaitable = _PyGen_GetAwaitableIter(next_iter); + if (awaitable == NULL) { + PyErr_Format( + PyExc_TypeError, + "'async for' received an invalid object " + "from __anext__: %.100s", + Py_TYPE(next_iter)->tp_name); + + Py_DECREF(next_iter); + goto error; + } else + Py_DECREF(next_iter); + + PUSH(awaitable); + DISPATCH(); + } + + TARGET(GET_AWAITABLE) { + PyObject *iterable = TOP(); + PyObject *iter = _PyGen_GetAwaitableIter(iterable); + + Py_DECREF(iterable); + + SET_TOP(iter); /* Even if it's NULL */ + + if (iter == NULL) { + goto error; + } + + DISPATCH(); + } + TARGET(YIELD_FROM) { PyObject *v = POP(); PyObject *reciever = TOP(); int err; if (PyGen_CheckExact(reciever)) { + if ( + (((PyCodeObject*) \ + ((PyGenObject*)reciever)->gi_code)->co_flags & + CO_COROUTINE) + && !(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) + { + /* If we're yielding-from a coroutine object from a regular + generator object - raise an error. */ + + Py_CLEAR(v); + Py_CLEAR(reciever); + SET_TOP(NULL); + + PyErr_SetString(PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "from a generator"); + goto error; + } retval = _PyGen_Send((PyGenObject *)reciever, v); } else { _Py_IDENTIFIER(send); @@ -2822,11 +2944,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_ITER) { /* before: [obj]; after [getiter(obj)] */ PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + PyObject *iter; + /* If we have a generator object on top -- keep it there, + it's already an iterator. + + This is needed to allow use of 'async def' coroutines + in 'yield from' expression from generator-based coroutines + (decorated with types.coroutine()). + + 'yield from' is compiled to GET_ITER..YIELD_FROM combination, + but since coroutines raise TypeError in their 'tp_iter' we + need a way for them to "pass through" the GET_ITER. + */ + if (!PyGen_CheckExact(iterable)) { + /* `iterable` is not a generator. */ + iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + SET_TOP(iter); + if (iter == NULL) + goto error; + } PREDICT(FOR_ITER); DISPATCH(); } @@ -2883,6 +3020,39 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BEFORE_ASYNC_WITH) { + _Py_IDENTIFIER(__aexit__); + _Py_IDENTIFIER(__aenter__); + + PyObject *mgr = TOP(); + PyObject *exit = special_lookup(mgr, &PyId___aexit__), + *enter; + PyObject *res; + if (exit == NULL) + goto error; + SET_TOP(exit); + enter = special_lookup(mgr, &PyId___aenter__); + Py_DECREF(mgr); + if (enter == NULL) + goto error; + res = PyObject_CallFunctionObjArgs(enter, NULL); + Py_DECREF(enter); + if (res == NULL) + goto error; + PUSH(res); + DISPATCH(); + } + + TARGET(SETUP_ASYNC_WITH) { + PyObject *res = POP(); + /* Setup the finally block before pushing the result + of __aenter__ on the stack. */ + PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg, + STACK_LEVEL()); + PUSH(res); + DISPATCH(); + } + TARGET(SETUP_WITH) { _Py_IDENTIFIER(__exit__); _Py_IDENTIFIER(__enter__); @@ -2909,7 +3079,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(WITH_CLEANUP) { + TARGET(WITH_CLEANUP_START) { /* At the top of the stack are 1-6 values indicating how/why we entered the finally clause: - TOP = None @@ -2937,7 +3107,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *exit_func; PyObject *exc = TOP(), *val = Py_None, *tb = Py_None, *res; - int err; if (exc == Py_None) { (void)POP(); exit_func = TOP(); @@ -2987,10 +3156,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (res == NULL) goto error; + PUSH(exc); + PUSH(res); + PREDICT(WITH_CLEANUP_FINISH); + DISPATCH(); + } + + PREDICTED(WITH_CLEANUP_FINISH); + TARGET(WITH_CLEANUP_FINISH) { + PyObject *res = POP(); + PyObject *exc = POP(); + int err; + if (exc != Py_None) err = PyObject_IsTrue(res); else err = 0; + Py_DECREF(res); if (err < 0) @@ -3751,6 +3933,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } if (co->co_flags & CO_GENERATOR) { + PyObject *gen; + PyObject *coroutine_wrapper; + /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ Py_CLEAR(f->f_back); @@ -3759,7 +3944,19 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_NewWithQualName(f, name, qualname); + gen = PyGen_NewWithQualName(f, name, qualname); + if (gen == NULL) + return NULL; + + if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { + coroutine_wrapper = PyEval_GetCoroutineWrapper(); + if (coroutine_wrapper != NULL) { + PyObject *wrapped = + PyObject_CallFunction(coroutine_wrapper, "N", gen); + gen = wrapped; + } + } + return gen; } retval = PyEval_EvalFrameEx(f,0); @@ -4205,6 +4402,24 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) || (tstate->c_profilefunc != NULL)); } +void +PyEval_SetCoroutineWrapper(PyObject *wrapper) +{ + PyThreadState *tstate = PyThreadState_GET(); + + Py_CLEAR(tstate->coroutine_wrapper); + + Py_XINCREF(wrapper); + tstate->coroutine_wrapper = wrapper; +} + +PyObject * +PyEval_GetCoroutineWrapper() +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->coroutine_wrapper; +} + PyObject * PyEval_GetBuiltins(void) { -- cgit v1.2.1 From eae221194a86e192c96afab97b373220997a4db8 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 11 May 2015 23:19:34 -0400 Subject: Fix warnings for PyEval_GetCoroutineWrapper --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8acd08201e..77085c25a0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4414,7 +4414,7 @@ PyEval_SetCoroutineWrapper(PyObject *wrapper) } PyObject * -PyEval_GetCoroutineWrapper() +PyEval_GetCoroutineWrapper(void) { PyThreadState *tstate = PyThreadState_GET(); return tstate->coroutine_wrapper; -- cgit v1.2.1 From ba830e670e1b0ab5ff88eb735312220e399d400c Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Wed, 13 May 2015 15:54:02 +1000 Subject: Issue 24017: fix for "async with" refcounting * adds missing INCREF in WITH_CLEANUP_START * adds missing DECREF in WITH_CLEANUP_FINISH * adds several new tests Yury created while investigating this --- Python/ceval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 77085c25a0..afb0f89aa8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3156,6 +3156,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (res == NULL) goto error; + Py_INCREF(exc); /* Duplicating the exception on the stack */ PUSH(exc); PUSH(res); PREDICT(WITH_CLEANUP_FINISH); @@ -3174,6 +3175,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) err = 0; Py_DECREF(res); + Py_DECREF(exc); if (err < 0) goto error; -- cgit v1.2.1 From eef24c3a5da25a828bc79c777e85ae604167a977 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 28 May 2015 11:21:31 -0400 Subject: Issue 24017: Drop getawaitablefunc and friends in favor of unaryfunc. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index afb0f89aa8..7d39d9bfea 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1927,7 +1927,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(GET_AITER) { - getaiterfunc getter = NULL; + unaryfunc getter = NULL; PyObject *iter = NULL; PyObject *awaitable = NULL; PyObject *obj = TOP(); @@ -1974,7 +1974,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(GET_ANEXT) { - aiternextfunc getter = NULL; + unaryfunc getter = NULL; PyObject *next_iter = NULL; PyObject *awaitable = NULL; PyObject *aiter = TOP(); -- cgit v1.2.1 From ffe5c8f2c45e238c70ac87fecf23aeef676a65f6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 May 2015 14:30:26 -0500 Subject: in dict displays, evaluate the key before the value (closes #11205) Patch partially by Steve Dougherty. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 7d39d9bfea..eb36975012 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2584,8 +2584,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; while (--oparg >= 0) { int err; - PyObject *key = TOP(); - PyObject *value = SECOND(); + PyObject *value = TOP(); + PyObject *key = SECOND(); STACKADJ(-2); err = PyDict_SetItem(map, key, value); Py_DECREF(value); -- cgit v1.2.1 From 2a8855656d526cce898303bb03c66274f06e7b51 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 May 2015 14:40:08 -0500 Subject: remove STORE_MAP, since it's unused --- Python/ceval.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index eb36975012..06772b1bec 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2680,21 +2680,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(STORE_MAP) { - PyObject *key = TOP(); - PyObject *value = SECOND(); - PyObject *map = THIRD(); - int err; - STACKADJ(-2); - assert(PyDict_CheckExact(map)); - err = PyDict_SetItem(map, key, value); - Py_DECREF(value); - Py_DECREF(key); - if (err != 0) - goto error; - DISPATCH(); - } - TARGET(MAP_ADD) { PyObject *key = TOP(); PyObject *value = SECOND(); -- cgit v1.2.1 From dab9b839d0e4eb315c219433c19c999729a39a95 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 1 Jun 2015 12:15:23 -0400 Subject: Issue 24017: Make PyEval_(Set|Get)CoroutineWrapper private --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 06772b1bec..bb2c0b96a5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3936,7 +3936,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return NULL; if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { - coroutine_wrapper = PyEval_GetCoroutineWrapper(); + coroutine_wrapper = _PyEval_GetCoroutineWrapper(); if (coroutine_wrapper != NULL) { PyObject *wrapped = PyObject_CallFunction(coroutine_wrapper, "N", gen); @@ -4390,7 +4390,7 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) } void -PyEval_SetCoroutineWrapper(PyObject *wrapper) +_PyEval_SetCoroutineWrapper(PyObject *wrapper) { PyThreadState *tstate = PyThreadState_GET(); @@ -4401,7 +4401,7 @@ PyEval_SetCoroutineWrapper(PyObject *wrapper) } PyObject * -PyEval_GetCoroutineWrapper(void) +_PyEval_GetCoroutineWrapper(void) { PyThreadState *tstate = PyThreadState_GET(); return tstate->coroutine_wrapper; -- cgit v1.2.1 From f28b7448cd4c7d325fee3c4bf4584f655eafccdb Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 2 Jun 2015 18:43:51 -0400 Subject: Issue 24342: Let wrapper set by sys.set_coroutine_wrapper fail gracefully --- Python/ceval.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index bb2c0b96a5..2a1db17b45 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3921,7 +3921,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (co->co_flags & CO_GENERATOR) { PyObject *gen; - PyObject *coroutine_wrapper; /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ @@ -3935,14 +3934,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (gen == NULL) return NULL; - if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { - coroutine_wrapper = _PyEval_GetCoroutineWrapper(); - if (coroutine_wrapper != NULL) { - PyObject *wrapped = - PyObject_CallFunction(coroutine_wrapper, "N", gen); - gen = wrapped; - } - } + if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) + return _PyEval_ApplyCoroutineWrapper(gen); + return gen; } @@ -4407,6 +4401,33 @@ _PyEval_GetCoroutineWrapper(void) return tstate->coroutine_wrapper; } +PyObject * +_PyEval_ApplyCoroutineWrapper(PyObject *gen) +{ + PyObject *wrapped; + PyThreadState *tstate = PyThreadState_GET(); + PyObject *wrapper = tstate->coroutine_wrapper; + + if (tstate->in_coroutine_wrapper) { + assert(wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.150R attempted " + "to recursively wrap %.150R", + wrapper, + gen); + return NULL; + } + + if (wrapper == NULL) { + return gen; + } + + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; +} + PyObject * PyEval_GetBuiltins(void) { -- cgit v1.2.1 From 95f436fe00004830fd5551208337c8945d8bd1ba Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 2 Jun 2015 22:30:31 -0400 Subject: Issue 24342: No need to use PyAPI_FUNC for _PyEval_ApplyCoroutineWrapper --- Python/ceval.c | 58 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2a1db17b45..96ed6ed4ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -146,6 +146,8 @@ static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); +static PyObject * apply_coroutine_wrapper(PyObject *); + #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -3935,7 +3937,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return NULL; if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) - return _PyEval_ApplyCoroutineWrapper(gen); + return apply_coroutine_wrapper(gen); return gen; } @@ -4401,33 +4403,6 @@ _PyEval_GetCoroutineWrapper(void) return tstate->coroutine_wrapper; } -PyObject * -_PyEval_ApplyCoroutineWrapper(PyObject *gen) -{ - PyObject *wrapped; - PyThreadState *tstate = PyThreadState_GET(); - PyObject *wrapper = tstate->coroutine_wrapper; - - if (tstate->in_coroutine_wrapper) { - assert(wrapper != NULL); - PyErr_Format(PyExc_RuntimeError, - "coroutine wrapper %.150R attempted " - "to recursively wrap %.150R", - wrapper, - gen); - return NULL; - } - - if (wrapper == NULL) { - return gen; - } - - tstate->in_coroutine_wrapper = 1; - wrapped = PyObject_CallFunction(wrapper, "N", gen); - tstate->in_coroutine_wrapper = 0; - return wrapped; -} - PyObject * PyEval_GetBuiltins(void) { @@ -5257,6 +5232,33 @@ unicode_concatenate(PyObject *v, PyObject *w, return res; } +static PyObject * +apply_coroutine_wrapper(PyObject *gen) +{ + PyObject *wrapped; + PyThreadState *tstate = PyThreadState_GET(); + PyObject *wrapper = tstate->coroutine_wrapper; + + if (tstate->in_coroutine_wrapper) { + assert(wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.200R attempted " + "to recursively wrap %.200R", + wrapper, + gen); + return NULL; + } + + if (wrapper == NULL) { + return gen; + } + + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; +} + #ifdef DYNAMIC_EXECUTION_PROFILE static PyObject * -- cgit v1.2.1 From 2848485266c7a82fd88138ca05b11e61015b2827 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 4 Jun 2015 10:16:51 -0400 Subject: Issue 24374: Plug refleak in set_coroutine_wrapper --- Python/ceval.c | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 96ed6ed4ee..641f9db317 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -146,8 +146,6 @@ static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); -static PyObject * apply_coroutine_wrapper(PyObject *); - #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -3923,6 +3921,18 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (co->co_flags & CO_GENERATOR) { PyObject *gen; + PyObject *coro_wrapper = tstate->coroutine_wrapper; + int is_coro = co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE); + + if (is_coro && tstate->in_coroutine_wrapper) { + assert(coro_wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.200R attempted " + "to recursively wrap %.200R", + coro_wrapper, + co); + goto fail; + } /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ @@ -3936,8 +3946,13 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (gen == NULL) return NULL; - if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) - return apply_coroutine_wrapper(gen); + if (is_coro && coro_wrapper != NULL) { + PyObject *wrapped; + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(coro_wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; + } return gen; } @@ -5232,33 +5247,6 @@ unicode_concatenate(PyObject *v, PyObject *w, return res; } -static PyObject * -apply_coroutine_wrapper(PyObject *gen) -{ - PyObject *wrapped; - PyThreadState *tstate = PyThreadState_GET(); - PyObject *wrapper = tstate->coroutine_wrapper; - - if (tstate->in_coroutine_wrapper) { - assert(wrapper != NULL); - PyErr_Format(PyExc_RuntimeError, - "coroutine wrapper %.200R attempted " - "to recursively wrap %.200R", - wrapper, - gen); - return NULL; - } - - if (wrapper == NULL) { - return gen; - } - - tstate->in_coroutine_wrapper = 1; - wrapped = PyObject_CallFunction(wrapper, "N", gen); - tstate->in_coroutine_wrapper = 0; - return wrapped; -} - #ifdef DYNAMIC_EXECUTION_PROFILE static PyObject * -- cgit v1.2.1 From 15be527fca539925b6ba49e0d0089f13f1e210ee Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 22 Jun 2015 12:19:30 -0400 Subject: Issue #24400: Introduce a distinct type for 'async def' coroutines. Summary of changes: 1. Coroutines now have a distinct, separate from generators type at the C level: PyGen_Type, and a new typedef PyCoroObject. PyCoroObject shares the initial segment of struct layout with PyGenObject, making it possible to reuse existing generators machinery. The new type is exposed as 'types.CoroutineType'. As a consequence of having a new type, CO_GENERATOR flag is no longer applied to coroutines. 2. Having a separate type for coroutines made it possible to add an __await__ method to the type. Although it is not used by the interpreter (see details on that below), it makes coroutines naturally (without using __instancecheck__) conform to collections.abc.Coroutine and collections.abc.Awaitable ABCs. [The __instancecheck__ is still used for generator-based coroutines, as we don't want to add __await__ for generators.] 3. Add new opcode: GET_YIELD_FROM_ITER. The opcode is needed to allow passing native coroutines to the YIELD_FROM opcode. Before this change, 'yield from o' expression was compiled to: (o) GET_ITER LOAD_CONST YIELD_FROM Now, we use GET_YIELD_FROM_ITER instead of GET_ITER. The reason for adding a new opcode is that GET_ITER is used in some contexts (such as 'for .. in' loops) where passing a coroutine object is invalid. 4. Add two new introspection functions to the inspec module: getcoroutinestate(c) and getcoroutinelocals(c). 5. inspect.iscoroutine(o) is updated to test if 'o' is a native coroutine object. Before this commit it used abc.Coroutine, and it was requested to update inspect.isgenerator(o) to use abc.Generator; it was decided, however, that inspect functions should really be tailored for checking for native types. 6. sys.set_coroutine_wrapper(w) API is updated to work with only native coroutines. Since types.coroutine decorator supports any type of callables now, it would be confusing that it does not work for all types of coroutines. 7. Exceptions logic in generators C implementation was updated to raise clearer messages for coroutines: Before: TypeError("generator raised StopIteration") After: TypeError("coroutine raised StopIteration") --- Python/ceval.c | 79 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 40 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e127a731fd..12df0fe3fc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1191,7 +1191,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_executing = 1; - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { if (!throwflag && f->f_exc_type != NULL && f->f_exc_type != Py_None) { /* We were in an except handler when we left, restore the exception state which was put aside @@ -1955,7 +1955,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } - awaitable = _PyGen_GetAwaitableIter(iter); + awaitable = _PyCoro_GetAwaitableIter(iter); if (awaitable == NULL) { SET_TOP(NULL); PyErr_Format( @@ -1998,7 +1998,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } - awaitable = _PyGen_GetAwaitableIter(next_iter); + awaitable = _PyCoro_GetAwaitableIter(next_iter); if (awaitable == NULL) { PyErr_Format( PyExc_TypeError, @@ -2017,7 +2017,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_AWAITABLE) { PyObject *iterable = TOP(); - PyObject *iter = _PyGen_GetAwaitableIter(iterable); + PyObject *iter = _PyCoro_GetAwaitableIter(iterable); Py_DECREF(iterable); @@ -2034,25 +2034,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *v = POP(); PyObject *reciever = TOP(); int err; - if (PyGen_CheckExact(reciever)) { - if ( - (((PyCodeObject*) \ - ((PyGenObject*)reciever)->gi_code)->co_flags & - CO_COROUTINE) - && !(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) - { - /* If we're yielding-from a coroutine object from a regular - generator object - raise an error. */ - - Py_CLEAR(v); - Py_CLEAR(reciever); - SET_TOP(NULL); - - PyErr_SetString(PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "from a generator"); - goto error; - } + if (PyGen_CheckExact(reciever) || PyCoro_CheckExact(reciever)) { retval = _PyGen_Send((PyGenObject *)reciever, v); } else { _Py_IDENTIFIER(send); @@ -2929,19 +2911,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_ITER) { /* before: [obj]; after [getiter(obj)] */ PyObject *iterable = TOP(); - PyObject *iter; - /* If we have a generator object on top -- keep it there, - it's already an iterator. - - This is needed to allow use of 'async def' coroutines - in 'yield from' expression from generator-based coroutines - (decorated with types.coroutine()). + PyObject *iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + SET_TOP(iter); + if (iter == NULL) + goto error; + PREDICT(FOR_ITER); + DISPATCH(); + } - 'yield from' is compiled to GET_ITER..YIELD_FROM combination, - but since coroutines raise TypeError in their 'tp_iter' we - need a way for them to "pass through" the GET_ITER. - */ - if (!PyGen_CheckExact(iterable)) { + TARGET(GET_YIELD_FROM_ITER) { + /* before: [obj]; after [getiter(obj)] */ + PyObject *iterable = TOP(); + PyObject *iter; + if (PyCoro_CheckExact(iterable)) { + /* `iterable` is a coroutine */ + if (!(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { + /* and it is used in a 'yield from' expression of a + regular generator. */ + Py_DECREF(iterable); + SET_TOP(NULL); + PyErr_SetString(PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "in a non-coroutine generator"); + goto error; + } + } + else if (!PyGen_CheckExact(iterable)) { /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); Py_DECREF(iterable); @@ -2949,7 +2945,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (iter == NULL) goto error; } - PREDICT(FOR_ITER); DISPATCH(); } @@ -3517,7 +3512,7 @@ fast_block_end: assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { /* The purpose of this block is to put aside the generator's exception state and restore that of the calling frame. If the current @@ -3919,10 +3914,10 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o; } - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { PyObject *gen; PyObject *coro_wrapper = tstate->coroutine_wrapper; - int is_coro = co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE); + int is_coro = co->co_flags & CO_COROUTINE; if (is_coro && tstate->in_coroutine_wrapper) { assert(coro_wrapper != NULL); @@ -3942,7 +3937,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - gen = PyGen_NewWithQualName(f, name, qualname); + if (is_coro) { + gen = PyCoro_New(f, name, qualname); + } else { + gen = PyGen_NewWithQualName(f, name, qualname); + } if (gen == NULL) return NULL; -- cgit v1.2.1 From 1f0eea05f78b032be72ee5b5787421acef61588d Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 3 Jul 2015 01:04:23 -0400 Subject: Issue #19235: Add new RecursionError exception. Patch by Georg Brandl. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 12df0fe3fc..e68ae33bfd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -737,7 +737,7 @@ _Py_CheckRecursiveCall(const char *where) if (tstate->recursion_depth > recursion_limit) { --tstate->recursion_depth; tstate->overflowed = 1; - PyErr_Format(PyExc_RuntimeError, + PyErr_Format(PyExc_RecursionError, "maximum recursion depth exceeded%s", where); return -1; -- cgit v1.2.1 From f0604e5a21390a16c9cc1ee32cbbad313599345c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 5 Jul 2015 10:37:25 -0500 Subject: set items in dict displays from left to right (closes #24569) --- Python/ceval.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e68ae33bfd..ac52ad91e2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2561,22 +2561,25 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(BUILD_MAP) { + int i; PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; - while (--oparg >= 0) { + for (i = oparg; i > 0; i--) { int err; - PyObject *value = TOP(); - PyObject *key = SECOND(); - STACKADJ(-2); + PyObject *key = PEEK(2*i); + PyObject *value = PEEK(2*i - 1); err = PyDict_SetItem(map, key, value); - Py_DECREF(value); - Py_DECREF(key); if (err != 0) { Py_DECREF(map); goto error; } } + + while (oparg--) { + Py_DECREF(POP()); + Py_DECREF(POP()); + } PUSH(map); DISPATCH(); } -- cgit v1.2.1 From f6c8d9988e5a4deaa45f6b7e6436674f0d6013e4 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 11 Aug 2015 18:01:31 -0700 Subject: Issue #24492: make sure that ``from ... import ...` raises an ImportError if __name__ is not defined on a package. Thanks to Armin Rigo for the bug report and diagnosing the cause. --- Python/ceval.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ac52ad91e2..8d2cdc2eda 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5085,19 +5085,24 @@ import_from(PyObject *v, PyObject *name) sys.modules. */ PyErr_Clear(); pkgname = _PyObject_GetAttrId(v, &PyId___name__); - if (pkgname == NULL) - return NULL; + if (pkgname == NULL) { + goto error; + } fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); Py_DECREF(pkgname); - if (fullmodname == NULL) + if (fullmodname == NULL) { return NULL; + } x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); - if (x == NULL) - PyErr_Format(PyExc_ImportError, "cannot import name %R", name); - else - Py_INCREF(x); Py_DECREF(fullmodname); + if (x == NULL) { + goto error; + } + Py_INCREF(x); return x; + error: + PyErr_Format(PyExc_ImportError, "cannot import name %R", name); + return NULL; } static int -- cgit v1.2.1 From 8bd2d0b8bd744a976d163daee2bd7fdb5f8dd184 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 5 Nov 2015 13:55:20 +0100 Subject: Issue #25556: Fix LOAD_GLOBAL bytecode when globals type is not dict and the requested name doesn't exist in globals: clear the KeyError exception before calling PyObject_GetItem(). Fail also if the raised exception is not a KeyError. --- Python/ceval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f9e8ef8859..beabfebc1c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2363,6 +2363,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Slow-path if globals or builtins is not a dict */ v = PyObject_GetItem(f->f_globals, name); if (v == NULL) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + goto error; + PyErr_Clear(); + v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) -- cgit v1.2.1 From 585a1eed45faea53f836b1db2ac211d7f14b44c8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 24 Dec 2015 10:35:59 +0200 Subject: Issue #20440: Massive replacing unsafe attribute setting code with special macro Py_SETREF. --- Python/ceval.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index beabfebc1c..5fa555ed43 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3214,8 +3214,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_DECREF(*pfunc); - *pfunc = self; + Py_SETREF(*pfunc, self); na++; /* n++; */ } else @@ -4670,8 +4669,7 @@ call_function(PyObject ***pp_stack, int oparg Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_DECREF(*pfunc); - *pfunc = self; + Py_SETREF(*pfunc, self); na++; n++; } else -- cgit v1.2.1 From 49b4fe42c04e1eec009aac77da7f44d985b1abbb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Dec 2015 12:36:18 +0200 Subject: Issue #20440: Applied yet one patch for using Py_SETREF. The patch is automatically generated, it replaces the code that uses Py_CLEAR. --- Python/ceval.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5fa555ed43..786adbf7e1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4410,10 +4410,8 @@ _PyEval_SetCoroutineWrapper(PyObject *wrapper) { PyThreadState *tstate = PyThreadState_GET(); - Py_CLEAR(tstate->coroutine_wrapper); - Py_XINCREF(wrapper); - tstate->coroutine_wrapper = wrapper; + Py_SETREF(tstate->coroutine_wrapper, wrapper); } PyObject * -- cgit v1.2.1 From 70b5333c6e61084ae63938c49748d084d4ac8f76 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sun, 31 Jan 2016 06:30:56 +0000 Subject: Issue #4806: Avoid masking original TypeError in call with * unpacking Based on patch by Hagen F?rstenau and Daniel Urban. --- Python/ceval.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 786adbf7e1..aee0e6bc85 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4935,16 +4935,18 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) stararg = EXT_POP(*pp_stack); if (!PyTuple_Check(stararg)) { PyObject *t = NULL; + if (Py_TYPE(stararg)->tp_iter == NULL && + !PySequence_Check(stararg)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after * " + "must be an iterable, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + stararg->ob_type->tp_name); + goto ext_call_fail; + } t = PySequence_Tuple(stararg); if (t == NULL) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after * " - "must be a sequence, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - stararg->ob_type->tp_name); - } goto ext_call_fail; } Py_DECREF(stararg); -- cgit v1.2.1 From 09bfcfc935da1ed8c753055ae1f317dc59764bc3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 2 Mar 2016 11:30:46 -0500 Subject: coroutines: Error when awaiting on coroutine that's being awaited Issue #25888 --- Python/ceval.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index aee0e6bc85..f9ecad46cf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2021,6 +2021,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(iterable); + if (iter != NULL && PyCoro_CheckExact(iter)) { + PyObject *yf = _PyGen_yf((PyGenObject*)iter); + if (yf != NULL) { + /* `iter` is a coroutine object that is being + awaited, `yf` is a pointer to the current awaitable + being awaited on. */ + Py_DECREF(yf); + Py_CLEAR(iter); + PyErr_SetString( + PyExc_RuntimeError, + "coroutine is being awaited already"); + /* The code below jumps to `error` if `iter` is NULL. */ + } + } + SET_TOP(iter); /* Even if it's NULL */ if (iter == NULL) { -- cgit v1.2.1 From 58455ad4ed5e6f1eda17ceb0dd7f5612a980e48d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Apr 2016 09:45:48 +0300 Subject: Issue #22570: Renamed Py_SETREF to Py_XSETREF. --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f9ecad46cf..1811210931 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3229,7 +3229,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_SETREF(*pfunc, self); + Py_XSETREF(*pfunc, self); na++; /* n++; */ } else @@ -4426,7 +4426,7 @@ _PyEval_SetCoroutineWrapper(PyObject *wrapper) PyThreadState *tstate = PyThreadState_GET(); Py_XINCREF(wrapper); - Py_SETREF(tstate->coroutine_wrapper, wrapper); + Py_XSETREF(tstate->coroutine_wrapper, wrapper); } PyObject * @@ -4682,7 +4682,7 @@ call_function(PyObject ***pp_stack, int oparg Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_SETREF(*pfunc, self); + Py_XSETREF(*pfunc, self); na++; n++; } else -- cgit v1.2.1 From 3099587c099191eccc737760a8d15c7fedcd8a40 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 10 Apr 2016 18:05:40 +0300 Subject: Issue #26200: Added Py_SETREF and replaced Py_XSETREF with Py_SETREF in places where Py_DECREF was used. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 1811210931..ee79c21955 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3229,7 +3229,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_XSETREF(*pfunc, self); + Py_SETREF(*pfunc, self); na++; /* n++; */ } else @@ -4682,7 +4682,7 @@ call_function(PyObject ***pp_stack, int oparg Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_XSETREF(*pfunc, self); + Py_SETREF(*pfunc, self); na++; n++; } else -- cgit v1.2.1 From 52e7ca603736baa91d9a3f5b5305bde76e01321b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 16 May 2016 22:52:40 -0700 Subject: fix possible refleak in MAKE_FUNCTION (closes #26991) Patch by Xiang Zhang. --- Python/ceval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ee79c21955..3758b0936a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3284,6 +3284,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *anns = PyDict_New(); if (anns == NULL) { Py_DECREF(func); + Py_DECREF(names); goto error; } name_ix = PyTuple_Size(names); @@ -3299,9 +3300,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (err != 0) { Py_DECREF(anns); Py_DECREF(func); + Py_DECREF(names); goto error; } } + Py_DECREF(names); if (PyFunction_SetAnnotations(func, anns) != 0) { /* Can't happen unless @@ -3311,7 +3314,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } Py_DECREF(anns); - Py_DECREF(names); } /* XXX Maybe this should be a separate opcode? */ -- cgit v1.2.1 From d938c575732c46c86c25db9bb3a322cfdeeaf5eb Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 9 Jun 2016 15:08:31 -0400 Subject: Issue #27243: Fix __aiter__ protocol --- Python/ceval.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 3758b0936a..3d69038444 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1933,8 +1933,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *obj = TOP(); PyTypeObject *type = Py_TYPE(obj); - if (type->tp_as_async != NULL) + if (type->tp_as_async != NULL) { getter = type->tp_as_async->am_aiter; + } if (getter != NULL) { iter = (*getter)(obj); @@ -1955,6 +1956,27 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } + if (Py_TYPE(iter)->tp_as_async != NULL && + Py_TYPE(iter)->tp_as_async->am_anext != NULL) { + + /* Starting with CPython 3.5.2 __aiter__ should return + asynchronous iterators directly (not awaitables that + resolve to asynchronous iterators.) + + Therefore, we check if the object that was returned + from __aiter__ has an __anext__ method. If it does, + we wrap it in an awaitable that resolves to `iter`. + + See http://bugs.python.org/issue27243 for more + details. + */ + + PyObject *wrapper = _PyAIterWrapper_New(iter); + Py_DECREF(iter); + SET_TOP(wrapper); + DISPATCH(); + } + awaitable = _PyCoro_GetAwaitableIter(iter); if (awaitable == NULL) { SET_TOP(NULL); @@ -1966,9 +1988,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(iter); goto error; - } else + } else { Py_DECREF(iter); + if (PyErr_WarnFormat( + PyExc_PendingDeprecationWarning, 1, + "'%.100s' implements legacy __aiter__ protocol; " + "__aiter__ should return an asynchronous " + "iterator, not awaitable", + type->tp_name)) + { + /* Warning was converted to an error. */ + Py_DECREF(awaitable); + SET_TOP(NULL); + goto error; + } + } + SET_TOP(awaitable); DISPATCH(); } -- cgit v1.2.1 From 1d44d85503dba0ebd06608002a5a271a4f407c1d Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 18 Jul 2016 08:23:26 +0000 Subject: Issue #16191: Fix up references to renamed variables --- Python/ceval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 3d69038444..c632488ec2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1563,7 +1563,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { sum = unicode_concatenate(left, right, f, next_instr); - /* unicode_concatenate consumed the ref to v */ + /* unicode_concatenate consumed the ref to left */ } else { sum = PyNumber_Add(left, right); @@ -1762,7 +1762,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *sum; if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { sum = unicode_concatenate(left, right, f, next_instr); - /* unicode_concatenate consumed the ref to v */ + /* unicode_concatenate consumed the ref to left */ } else { sum = PyNumber_InPlaceAdd(left, right); @@ -1853,7 +1853,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *v = THIRD(); int err; STACKADJ(-3); - /* v[w] = u */ + /* container[sub] = v */ err = PyObject_SetItem(container, sub, v); Py_DECREF(v); Py_DECREF(container); @@ -1868,7 +1868,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *container = SECOND(); int err; STACKADJ(-2); - /* del v[w] */ + /* del container[sub] */ err = PyObject_DelItem(container, sub); Py_DECREF(container); Py_DECREF(sub); @@ -2107,7 +2107,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) SET_TOP(val); DISPATCH(); } - /* x remains on stack, retval is value to be yielded */ + /* receiver remains on stack, retval is value to be yielded */ f->f_stacktop = stack_pointer; why = WHY_YIELD; /* and repeat... */ @@ -2728,7 +2728,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) STACKADJ(-2); map = stack_pointer[-oparg]; /* dict */ assert(PyDict_CheckExact(map)); - err = PyDict_SetItem(map, key, value); /* v[w] = u */ + err = PyDict_SetItem(map, key, value); /* map[key] = value */ Py_DECREF(value); Py_DECREF(key); if (err != 0) -- cgit v1.2.1 From 99a19976dd8e777ffb8d498e22ceb9814ce677f3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 18 Aug 2016 18:13:10 +0200 Subject: Fix SystemError in "raise" statement Issue #27558: Fix a SystemError in the implementation of "raise" statement. In a brand new thread, raise a RuntimeError since there is no active exception to reraise. Patch written by Xiang Zhang. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c632488ec2..8e396fd2bc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4123,7 +4123,7 @@ do_raise(PyObject *exc, PyObject *cause) type = tstate->exc_type; value = tstate->exc_value; tb = tstate->exc_traceback; - if (type == Py_None) { + if (type == Py_None || type == NULL) { PyErr_SetString(PyExc_RuntimeError, "No active exception to reraise"); return 0; -- cgit v1.2.1 From 4c6227476c079eee9491fd2c6369805f84e8852a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 8 Sep 2016 14:40:36 -0700 Subject: Issue #26020: Fix evaluation order for set literals --- Python/ceval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8e396fd2bc..451791c8b4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2580,14 +2580,16 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(BUILD_SET) { PyObject *set = PySet_New(NULL); int err = 0; + int i; if (set == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); + for (i = oparg; i > 0; i--) { + PyObject *item = PEEK(i); if (err == 0) err = PySet_Add(set, item); Py_DECREF(item); } + STACKADJ(-oparg); if (err != 0) { Py_DECREF(set); goto error; -- cgit v1.2.1 From d32881ffdcdc493b0ca7e20a63d5644e20fedc29 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 22 Sep 2016 19:59:46 +0300 Subject: Silence GCC warning. The code was correct, but GCC is not enough clever. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 451791c8b4..9f1af788aa 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2646,14 +2646,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) _build_map_unpack: { int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; int num_maps; - int function_location; int i; PyObject *sum = PyDict_New(); if (sum == NULL) goto error; if (with_call) { num_maps = oparg & 0xff; - function_location = (oparg>>8) & 0xff; } else { num_maps = oparg; @@ -2666,6 +2664,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (intersection == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + int function_location = (oparg>>8) & 0xff; PyObject *func = ( PEEK(function_location + num_maps)); PyErr_Format(PyExc_TypeError, @@ -2682,6 +2681,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (PySet_GET_SIZE(intersection)) { Py_ssize_t idx = 0; PyObject *key; + int function_location = (oparg>>8) & 0xff; PyObject *func = PEEK(function_location + num_maps); Py_hash_t hash; _PySet_NextEntry(intersection, &idx, &key, &hash); -- cgit v1.2.1 From 8ef0213a85057e89ab26d8271850cb645d96d24e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 7 Oct 2016 23:32:41 +0300 Subject: Issue #28257: Improved error message when pass a non-mapping as a var-keyword argument. --- Python/ceval.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9f1af788aa..e9d0cbb976 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2663,7 +2663,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *intersection = _PyDictView_Intersect(sum, arg); if (intersection == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + if (PyErr_ExceptionMatches(PyExc_AttributeError) || + !PyMapping_Check(arg)) { int function_location = (oparg>>8) & 0xff; PyObject *func = ( PEEK(function_location + num_maps)); @@ -2707,9 +2708,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (PyDict_Update(sum, arg) < 0) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Format(PyExc_TypeError, - "'%.200s' object is not a mapping", - arg->ob_type->tp_name); + if (with_call) { + int function_location = (oparg>>8) & 0xff; + PyObject *func = PEEK(function_location + num_maps); + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + arg->ob_type->tp_name); + } + else { + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not a mapping", + arg->ob_type->tp_name); + } } Py_DECREF(sum); goto error; -- cgit v1.2.1 From 0ad3de98236bf2992531d0d8f172636b21ccadff Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 8 Nov 2016 23:12:46 +0200 Subject: Fixed possible abort in ceval loop if _PyUnicode_FromId() fails. Every opcode should end with DISPATCH() or goto error. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e9d0cbb976..7b405188d3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2196,7 +2196,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) else { PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__); if (build_class_str == NULL) - break; + goto error; bc = PyObject_GetItem(f->f_builtins, build_class_str); if (bc == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) -- cgit v1.2.1