diff options
Diffstat (limited to 'Objects/genobject.c')
-rw-r--r-- | Objects/genobject.c | 1934 |
1 files changed, 156 insertions, 1778 deletions
diff --git a/Objects/genobject.c b/Objects/genobject.c index c5fe999..082e03c 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1,117 +1,18 @@ /* Generator object implementation */ #include "Python.h" -#include "pycore_ceval.h" /* _PyEval_EvalFrame() */ -#include "pycore_object.h" -#include "pycore_pystate.h" #include "frameobject.h" +#include "genobject.h" +#include "ceval.h" #include "structmember.h" #include "opcode.h" -static PyObject *gen_close(PyGenObject *, PyObject *); -static PyObject *async_gen_asend_new(PyAsyncGenObject *, PyObject *); -static PyObject *async_gen_athrow_new(PyAsyncGenObject *, PyObject *); - -static char *NON_INIT_CORO_MSG = "can't send non-None value to a " - "just-started coroutine"; - -static char *ASYNC_GEN_IGNORED_EXIT_MSG = - "async generator ignored GeneratorExit"; - -static inline int -exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) -{ - Py_VISIT(exc_state->exc_type); - Py_VISIT(exc_state->exc_value); - Py_VISIT(exc_state->exc_traceback); - return 0; -} - static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); - Py_VISIT(gen->gi_name); - Py_VISIT(gen->gi_qualname); - /* No need to visit cr_origin, because it's just tuples/str/int, so can't - participate in a reference cycle. */ - return exc_state_traverse(&gen->gi_exc_state, visit, arg); -} - -void -_PyGen_Finalize(PyObject *self) -{ - PyGenObject *gen = (PyGenObject *)self; - PyObject *res = NULL; - PyObject *error_type, *error_value, *error_traceback; - - if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) { - /* Generator isn't paused, so no need to close */ - return; - } - - if (PyAsyncGen_CheckExact(self)) { - PyAsyncGenObject *agen = (PyAsyncGenObject*)self; - PyObject *finalizer = agen->ag_finalizer; - if (finalizer && !agen->ag_closed) { - /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); - - res = _PyObject_CallOneArg(finalizer, self); - - if (res == NULL) { - PyErr_WriteUnraisable(self); - } else { - Py_DECREF(res); - } - /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); - return; - } - } - - /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); - - /* If `gen` is a coroutine, and if it was never awaited on, - issue a RuntimeWarning. */ - if (gen->gi_code != NULL && - ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && - gen->gi_frame->f_lasti == -1) - { - _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); - } - else { - res = gen_close(gen, NULL); - } - - if (res == NULL) { - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(self); - } - } - else { - Py_DECREF(res); - } - - /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); -} - -static inline void -exc_state_clear(_PyErr_StackItem *exc_state) -{ - PyObject *t, *v, *tb; - t = exc_state->exc_type; - v = exc_state->exc_value; - tb = exc_state->exc_traceback; - exc_state->exc_type = NULL; - exc_state->exc_value = NULL; - exc_state->exc_traceback = NULL; - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); + return 0; } static void @@ -126,82 +27,44 @@ gen_dealloc(PyGenObject *gen) _PyObject_GC_TRACK(self); - if (PyObject_CallFinalizerFromDealloc(self)) - return; /* resurrected. :( */ + if (gen->gi_frame != NULL && gen->gi_frame->f_stacktop != NULL) { + /* Generator is paused, so we need to close */ + Py_TYPE(gen)->tp_del(self); + if (self->ob_refcnt > 0) + return; /* resurrected. :( */ + } _PyObject_GC_UNTRACK(self); - if (PyAsyncGen_CheckExact(gen)) { - /* We have to handle this case for asynchronous generators - right here, because this code has to be between UNTRACK - and GC_Del. */ - Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer); - } - if (gen->gi_frame != NULL) { - gen->gi_frame->f_gen = NULL; - Py_CLEAR(gen->gi_frame); - } - if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { - Py_CLEAR(((PyCoroObject *)gen)->cr_origin); - } + Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); - Py_CLEAR(gen->gi_name); - Py_CLEAR(gen->gi_qualname); - exc_state_clear(&gen->gi_exc_state); PyObject_GC_Del(gen); } + static PyObject * -gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) +gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = PyThreadState_GET(); PyFrameObject *f = gen->gi_frame; PyObject *result; if (gen->gi_running) { - const char *msg = "generator already executing"; - if (PyCoro_CheckExact(gen)) { - msg = "coroutine already executing"; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "async generator already executing"; - } - PyErr_SetString(PyExc_ValueError, msg); + PyErr_SetString(PyExc_ValueError, + "generator already executing"); return NULL; } - if (f == NULL || f->f_stacktop == NULL) { - if (PyCoro_CheckExact(gen) && !closing) { - /* `gen` is an exhausted coroutine: raise an error, - except when called from gen_close(), which should - always be a silent method. */ - PyErr_SetString( - PyExc_RuntimeError, - "cannot reuse already awaited coroutine"); - } - else if (arg && !exc) { - /* `gen` is an exhausted generator: - only set exception if called from send(). */ - if (PyAsyncGen_CheckExact(gen)) { - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else { - PyErr_SetNone(PyExc_StopIteration); - } - } + if (f==NULL || f->f_stacktop == NULL) { + /* Only set exception if called from send() */ + if (arg && !exc) + PyErr_SetNone(PyExc_StopIteration); return NULL; } if (f->f_lasti == -1) { if (arg && arg != Py_None) { - const char *msg = "can't send non-None value to a " - "just-started generator"; - if (PyCoro_CheckExact(gen)) { - msg = NON_INIT_CORO_MSG; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "can't send non-None value to a " - "just-started async generator"; - } - PyErr_SetString(PyExc_TypeError, msg); + PyErr_SetString(PyExc_TypeError, + "can't send non-None value to a " + "just-started generator"); return NULL; } } else { @@ -213,16 +76,13 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) /* Generators always return to their most recent caller, not * necessarily their creator. */ + f->f_tstate = tstate; Py_XINCREF(tstate->frame); assert(f->f_back == NULL); f->f_back = tstate->frame; gen->gi_running = 1; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - result = _PyEval_EvalFrame(tstate, f, exc); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; + result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It @@ -230,54 +90,23 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) * cycle. */ assert(f->f_back == tstate->frame); Py_CLEAR(f->f_back); + /* Clear the borrowed reference to the thread state */ + f->f_tstate = NULL; /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ - if (result && f->f_stacktop == NULL) { - if (result == Py_None) { - /* Delay exception instantiation if we can */ - if (PyAsyncGen_CheckExact(gen)) { - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else { - PyErr_SetNone(PyExc_StopIteration); - } - } - else { - /* Async generators cannot return anything but None */ - assert(!PyAsyncGen_CheckExact(gen)); - _PyGen_SetStopIterationValue(result); - } - Py_CLEAR(result); - } - else if (!result && PyErr_ExceptionMatches(PyExc_StopIteration)) { - const char *msg = "generator raised StopIteration"; - if (PyCoro_CheckExact(gen)) { - msg = "coroutine raised StopIteration"; - } - else if PyAsyncGen_CheckExact(gen) { - msg = "async generator raised StopIteration"; - } - _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg); - - } - else if (!result && PyAsyncGen_CheckExact(gen) && - PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) - { - /* code in `gen` raised a StopAsyncIteration error: - raise a RuntimeError. - */ - const char *msg = "async generator raised StopAsyncIteration"; - _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg); + if (result == Py_None && f->f_stacktop == NULL) { + Py_DECREF(result); + result = NULL; + /* Set exception if not called by gen_iternext() */ + if (arg) + PyErr_SetNone(PyExc_StopIteration); } if (!result || f->f_stacktop == NULL) { /* generator can't be rerun, so release the frame */ - /* first clean reference cycle through stored exception traceback */ - exc_state_clear(&gen->gi_exc_state); - gen->gi_frame->f_gen = NULL; - gen->gi_frame = NULL; Py_DECREF(f); + gen->gi_frame = NULL; } return result; @@ -287,189 +116,118 @@ PyDoc_STRVAR(send_doc, "send(arg) -> send 'arg' into generator,\n\ return next yielded value or raise StopIteration."); -PyObject * -_PyGen_Send(PyGenObject *gen, PyObject *arg) +static PyObject * +gen_send(PyGenObject *gen, PyObject *arg) { - return gen_send_ex(gen, arg, 0, 0); + return gen_send_ex(gen, arg, 0); } PyDoc_STRVAR(close_doc, "close() -> raise GeneratorExit inside generator."); -/* - * This helper function is used by gen_close and gen_throw to - * close a subiterator being delegated to by yield-from. - */ - -static int -gen_close_iter(PyObject *yf) -{ - PyObject *retval = NULL; - _Py_IDENTIFIER(close); - - if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { - retval = gen_close((PyGenObject *)yf, NULL); - if (retval == NULL) - return -1; - } - else { - PyObject *meth; - if (_PyObject_LookupAttrId(yf, &PyId_close, &meth) < 0) { - PyErr_WriteUnraisable(yf); - } - if (meth) { - retval = _PyObject_CallNoArg(meth); - Py_DECREF(meth); - if (retval == NULL) - return -1; - } - } - Py_XDECREF(retval); - return 0; -} - -PyObject * -_PyGen_yf(PyGenObject *gen) -{ - PyObject *yf = NULL; - PyFrameObject *f = gen->gi_frame; - - if (f && f->f_stacktop) { - PyObject *bytecode = f->f_code->co_code; - unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - - if (f->f_lasti < 0) { - /* Return immediately if the frame didn't start yet. YIELD_FROM - always come after LOAD_CONST: a code object should not start - with YIELD_FROM */ - assert(code[0] != YIELD_FROM); - return NULL; - } - - if (code[f->f_lasti + sizeof(_Py_CODEUNIT)] != YIELD_FROM) - return NULL; - yf = f->f_stacktop[-1]; - Py_INCREF(yf); - } - - return yf; -} - static PyObject * gen_close(PyGenObject *gen, PyObject *args) { PyObject *retval; - PyObject *yf = _PyGen_yf(gen); - int err = 0; - - if (yf) { - gen->gi_running = 1; - err = gen_close_iter(yf); - gen->gi_running = 0; - Py_DECREF(yf); - } - if (err == 0) - PyErr_SetNone(PyExc_GeneratorExit); - retval = gen_send_ex(gen, Py_None, 1, 1); + PyErr_SetNone(PyExc_GeneratorExit); + retval = gen_send_ex(gen, Py_None, 1); if (retval) { - const char *msg = "generator ignored GeneratorExit"; - if (PyCoro_CheckExact(gen)) { - msg = "coroutine ignored GeneratorExit"; - } else if (PyAsyncGen_CheckExact(gen)) { - msg = ASYNC_GEN_IGNORED_EXIT_MSG; - } Py_DECREF(retval); - PyErr_SetString(PyExc_RuntimeError, msg); + PyErr_SetString(PyExc_RuntimeError, + "generator ignored GeneratorExit"); return NULL; } if (PyErr_ExceptionMatches(PyExc_StopIteration) - || PyErr_ExceptionMatches(PyExc_GeneratorExit)) { + || PyErr_ExceptionMatches(PyExc_GeneratorExit)) + { PyErr_Clear(); /* ignore these errors */ - Py_RETURN_NONE; + Py_INCREF(Py_None); + return Py_None; } return NULL; } +static void +gen_del(PyObject *self) +{ + PyObject *res; + PyObject *error_type, *error_value, *error_traceback; + PyGenObject *gen = (PyGenObject *)self; + + if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) + /* Generator isn't paused, so no need to close */ + return; + + /* Temporarily resurrect the object. */ + assert(self->ob_refcnt == 0); + self->ob_refcnt = 1; + + /* Save the current exception, if any. */ + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + res = gen_close(gen, NULL); + + if (res == NULL) + PyErr_WriteUnraisable(self); + else + Py_DECREF(res); + + /* Restore the saved exception. */ + PyErr_Restore(error_type, error_value, error_traceback); + + /* Undo the temporary resurrection; can't use DECREF here, it would + * cause a recursive call. + */ + assert(self->ob_refcnt > 0); + if (--self->ob_refcnt == 0) + return; /* this is the normal path out */ + + /* close() resurrected it! Make it look like the original Py_DECREF + * never happened. + */ + { + Py_ssize_t refcnt = self->ob_refcnt; + _Py_NewReference(self); + self->ob_refcnt = refcnt; + } + assert(PyType_IS_GC(self->ob_type) && + _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); + + /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so + * we need to undo that. */ + _Py_DEC_REFTOTAL; + /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object + * chain, so no more to do there. + * If COUNT_ALLOCS, the original decref bumped tp_frees, and + * _Py_NewReference bumped tp_allocs: both of those need to be + * undone. + */ +#ifdef COUNT_ALLOCS + --self->ob_type->tp_frees; + --self->ob_type->tp_allocs; +#endif +} + + PyDoc_STRVAR(throw_doc, "throw(typ[,val[,tb]]) -> raise exception in generator,\n\ return next yielded value or raise StopIteration."); static PyObject * -_gen_throw(PyGenObject *gen, int close_on_genexit, - PyObject *typ, PyObject *val, PyObject *tb) +gen_throw(PyGenObject *gen, PyObject *args) { - PyObject *yf = _PyGen_yf(gen); - _Py_IDENTIFIER(throw); + PyObject *typ; + PyObject *tb = NULL; + PyObject *val = NULL; - if (yf) { - PyObject *ret; - int err; - if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && - close_on_genexit - ) { - /* Asynchronous generators *should not* be closed right away. - We have to allow some awaits to work it through, hence the - `close_on_genexit` parameter here. - */ - gen->gi_running = 1; - err = gen_close_iter(yf); - gen->gi_running = 0; - Py_DECREF(yf); - if (err < 0) - return gen_send_ex(gen, Py_None, 1, 0); - goto throw_here; - } - if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { - /* `yf` is a generator or a coroutine. */ - gen->gi_running = 1; - /* Close the generator that we are currently iterating with - 'yield from' or awaiting on with 'await'. */ - ret = _gen_throw((PyGenObject *)yf, close_on_genexit, - typ, val, tb); - gen->gi_running = 0; - } else { - /* `yf` is an iterator or a coroutine-like object. */ - PyObject *meth; - if (_PyObject_LookupAttrId(yf, &PyId_throw, &meth) < 0) { - Py_DECREF(yf); - return NULL; - } - if (meth == NULL) { - Py_DECREF(yf); - goto throw_here; - } - gen->gi_running = 1; - ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_running = 0; - Py_DECREF(meth); - } - Py_DECREF(yf); - if (!ret) { - PyObject *val; - /* Pop subiterator from stack */ - ret = *(--gen->gi_frame->f_stacktop); - assert(ret == yf); - Py_DECREF(ret); - /* Termination repetition of YIELD_FROM */ - assert(gen->gi_frame->f_lasti >= 0); - gen->gi_frame->f_lasti += sizeof(_Py_CODEUNIT); - if (_PyGen_FetchStopIterationValue(&val) == 0) { - ret = gen_send_ex(gen, val, 0, 0); - Py_DECREF(val); - } else { - ret = gen_send_ex(gen, Py_None, 1, 0); - } - } - return ret; - } + if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) + return NULL; -throw_here: /* First, check the traceback argument, replacing None with NULL. */ - if (tb == Py_None) { + if (tb == Py_None) tb = NULL; - } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback object"); @@ -480,8 +238,9 @@ throw_here: Py_XINCREF(val); Py_XINCREF(tb); - if (PyExceptionClass_Check(typ)) + if (PyExceptionClass_Check(typ)) { PyErr_NormalizeException(&typ, &val, &tb); + } else if (PyExceptionInstance_Check(typ)) { /* Raising an instance. The value should be a dummy. */ @@ -496,23 +255,18 @@ throw_here: val = typ; typ = PyExceptionInstance_Class(typ); Py_INCREF(typ); - - if (tb == NULL) - /* Returns NULL if there's no traceback */ - tb = PyException_GetTraceback(val); } } else { /* Not something you can raise. throw() fails. */ PyErr_Format(PyExc_TypeError, - "exceptions must be classes or instances " - "deriving from BaseException, not %s", - Py_TYPE(typ)->tp_name); + "exceptions must be classes, or instances, not %s", + typ->ob_type->tp_name); goto failed_throw; } PyErr_Restore(typ, val, tb); - return gen_send_ex(gen, Py_None, 1, 0); + return gen_send_ex(gen, Py_None, 1); failed_throw: /* Didn't use our arguments, so restore their original refcounts */ @@ -524,199 +278,51 @@ failed_throw: static PyObject * -gen_throw(PyGenObject *gen, PyObject *args) -{ - PyObject *typ; - PyObject *tb = NULL; - PyObject *val = NULL; - - if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) { - return NULL; - } - - return _gen_throw(gen, 1, typ, val, tb); -} - - -static PyObject * gen_iternext(PyGenObject *gen) { - return gen_send_ex(gen, NULL, 0, 0); -} - -/* - * Set StopIteration with specified value. Value can be arbitrary object - * or NULL. - * - * Returns 0 if StopIteration is set and -1 if any other exception is set. - */ -int -_PyGen_SetStopIterationValue(PyObject *value) -{ - PyObject *e; - - if (value == NULL || - (!PyTuple_Check(value) && !PyExceptionInstance_Check(value))) - { - /* Delay exception instantiation if we can */ - PyErr_SetObject(PyExc_StopIteration, value); - return 0; - } - /* Construct an exception instance manually with - * _PyObject_CallOneArg and pass it to PyErr_SetObject. - * - * We do this to handle a situation when "value" is a tuple, in which - * case PyErr_SetObject would set the value of StopIteration to - * the first element of the tuple. - * - * (See PyErr_SetObject/_PyErr_CreateException code for details.) - */ - e = _PyObject_CallOneArg(PyExc_StopIteration, value); - if (e == NULL) { - return -1; - } - PyErr_SetObject(PyExc_StopIteration, e); - Py_DECREF(e); - return 0; + return gen_send_ex(gen, NULL, 0); } -/* - * If StopIteration exception is set, fetches its 'value' - * attribute if any, otherwise sets pvalue to None. - * - * Returns 0 if no exception or StopIteration is set. - * If any other exception is set, returns -1 and leaves - * pvalue unchanged. - */ - -int -_PyGen_FetchStopIterationValue(PyObject **pvalue) -{ - PyObject *et, *ev, *tb; - PyObject *value = NULL; - - if (PyErr_ExceptionMatches(PyExc_StopIteration)) { - PyErr_Fetch(&et, &ev, &tb); - if (ev) { - /* exception will usually be normalised already */ - if (PyObject_TypeCheck(ev, (PyTypeObject *) et)) { - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); - Py_DECREF(ev); - } else if (et == PyExc_StopIteration && !PyTuple_Check(ev)) { - /* Avoid normalisation and take ev as value. - * - * Normalization is required if the value is a tuple, in - * that case the value of StopIteration would be set to - * the first element of the tuple. - * - * (See _PyErr_CreateException code for details.) - */ - value = ev; - } else { - /* normalisation required */ - PyErr_NormalizeException(&et, &ev, &tb); - if (!PyObject_TypeCheck(ev, (PyTypeObject *)PyExc_StopIteration)) { - PyErr_Restore(et, ev, tb); - return -1; - } - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); - Py_DECREF(ev); - } - } - Py_XDECREF(et); - Py_XDECREF(tb); - } else if (PyErr_Occurred()) { - return -1; - } - if (value == NULL) { - value = Py_None; - Py_INCREF(value); - } - *pvalue = value; - return 0; -} static PyObject * gen_repr(PyGenObject *gen) { - return PyUnicode_FromFormat("<generator object %S at %p>", - gen->gi_qualname, gen); + char *code_name; + code_name = PyString_AsString(((PyCodeObject *)gen->gi_code)->co_name); + if (code_name == NULL) + return NULL; + return PyString_FromFormat("<generator object %.200s at %p>", + code_name, gen); } -static PyObject * -gen_get_name(PyGenObject *op, void *Py_UNUSED(ignored)) -{ - Py_INCREF(op->gi_name); - return op->gi_name; -} - -static int -gen_set_name(PyGenObject *op, PyObject *value, void *Py_UNUSED(ignored)) -{ - /* Not legal to del gen.gi_name or to set it to anything - * other than a string object. */ - if (value == NULL || !PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "__name__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - Py_XSETREF(op->gi_name, value); - return 0; -} static PyObject * -gen_get_qualname(PyGenObject *op, void *Py_UNUSED(ignored)) +gen_get_name(PyGenObject *gen) { - Py_INCREF(op->gi_qualname); - return op->gi_qualname; + PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(name); + return name; } -static int -gen_set_qualname(PyGenObject *op, PyObject *value, void *Py_UNUSED(ignored)) -{ - /* Not legal to del gen.__qualname__ or to set it to anything - * other than a string object. */ - if (value == NULL || !PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "__qualname__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - Py_XSETREF(op->gi_qualname, value); - return 0; -} -static PyObject * -gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored)) -{ - PyObject *yf = _PyGen_yf(gen); - if (yf == NULL) - Py_RETURN_NONE; - return yf; -} +PyDoc_STRVAR(gen__name__doc__, +"Return the name of the generator's associated code object."); static PyGetSetDef gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, (setter)gen_set_name, - PyDoc_STR("name of the generator")}, - {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, - PyDoc_STR("qualified name of the generator")}, - {"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, - PyDoc_STR("object being iterated by yield from, or None")}, - {NULL} /* Sentinel */ + {"__name__", (getter)gen_get_name, NULL, gen__name__doc__}, + {NULL} }; + static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), RO}, + {"gi_running", T_INT, offsetof(PyGenObject, gi_running), RO}, + {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), RO}, {NULL} /* Sentinel */ }; static PyMethodDef gen_methods[] = { - {"send",(PyCFunction)_PyGen_Send, METH_O, send_doc}, + {"send",(PyCFunction)gen_send, METH_O, send_doc}, {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc}, {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc}, {NULL, NULL} /* Sentinel */ @@ -729,10 +335,10 @@ PyTypeObject PyGen_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)gen_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ (reprfunc)gen_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -743,7 +349,7 @@ PyTypeObject PyGen_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)gen_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -770,1270 +376,42 @@ PyTypeObject PyGen_Type = { 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ + gen_del, /* tp_del */ }; -static PyObject * -gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, - PyObject *name, PyObject *qualname) +PyObject * +PyGen_New(PyFrameObject *f) { - PyGenObject *gen = PyObject_GC_New(PyGenObject, type); + PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { Py_DECREF(f); return NULL; } gen->gi_frame = f; - f->f_gen = (PyObject *) gen; Py_INCREF(f->f_code); gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; - gen->gi_exc_state.exc_type = NULL; - gen->gi_exc_state.exc_value = NULL; - gen->gi_exc_state.exc_traceback = NULL; - gen->gi_exc_state.previous_item = NULL; - if (name != NULL) - gen->gi_name = name; - else - gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name; - Py_INCREF(gen->gi_name); - if (qualname != NULL) - gen->gi_qualname = qualname; - else - gen->gi_qualname = gen->gi_name; - Py_INCREF(gen->gi_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } -PyObject * -PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname) -{ - return gen_new_with_qualname(&PyGen_Type, f, name, qualname); -} - -PyObject * -PyGen_New(PyFrameObject *f) -{ - return gen_new_with_qualname(&PyGen_Type, f, NULL, NULL); -} - -/* Coroutine Object */ - -typedef struct { - PyObject_HEAD - PyCoroObject *cw_coroutine; -} PyCoroWrapper; - -static int -gen_is_coroutine(PyObject *o) -{ - if (PyGen_CheckExact(o)) { - PyCodeObject *code = (PyCodeObject *)((PyGenObject*)o)->gi_code; - if (code->co_flags & CO_ITERABLE_COROUTINE) { - return 1; - } - } - return 0; -} - -/* - * This helper function returns an awaitable for `o`: - * - `o` if `o` is a coroutine-object; - * - `type(o)->tp_as_async->am_await(o)` - * - * Raises a TypeError if it's not possible to return - * an awaitable and returns NULL. - */ -PyObject * -_PyCoro_GetAwaitableIter(PyObject *o) -{ - unaryfunc getter = NULL; - PyTypeObject *ot; - - if (PyCoro_CheckExact(o) || gen_is_coroutine(o)) { - /* 'o' is a coroutine. */ - Py_INCREF(o); - return o; - } - - ot = Py_TYPE(o); - if (ot->tp_as_async != NULL) { - getter = ot->tp_as_async->am_await; - } - if (getter != NULL) { - PyObject *res = (*getter)(o); - if (res != NULL) { - if (PyCoro_CheckExact(res) || gen_is_coroutine(res)) { - /* __await__ must return an *iterator*, not - a coroutine or another awaitable (see PEP 492) */ - PyErr_SetString(PyExc_TypeError, - "__await__() returned a coroutine"); - Py_CLEAR(res); - } else if (!PyIter_Check(res)) { - PyErr_Format(PyExc_TypeError, - "__await__() returned non-iterator " - "of type '%.100s'", - Py_TYPE(res)->tp_name); - Py_CLEAR(res); - } - } - return res; - } - - PyErr_Format(PyExc_TypeError, - "object %.100s can't be used in 'await' expression", - ot->tp_name); - return NULL; -} - -static PyObject * -coro_repr(PyCoroObject *coro) -{ - return PyUnicode_FromFormat("<coroutine object %S at %p>", - coro->cr_qualname, coro); -} - -static PyObject * -coro_await(PyCoroObject *coro) -{ - PyCoroWrapper *cw = PyObject_GC_New(PyCoroWrapper, &_PyCoroWrapper_Type); - if (cw == NULL) { - return NULL; - } - Py_INCREF(coro); - cw->cw_coroutine = coro; - _PyObject_GC_TRACK(cw); - return (PyObject *)cw; -} - -static PyObject * -coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored)) -{ - PyObject *yf = _PyGen_yf((PyGenObject *) coro); - if (yf == NULL) - Py_RETURN_NONE; - return yf; -} - -static PyGetSetDef coro_getsetlist[] = { - {"__name__", (getter)gen_get_name, (setter)gen_set_name, - PyDoc_STR("name of the coroutine")}, - {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, - PyDoc_STR("qualified name of the coroutine")}, - {"cr_await", (getter)coro_get_cr_await, NULL, - PyDoc_STR("object being awaited on, or None")}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef coro_memberlist[] = { - {"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY}, - {"cr_running", T_BOOL, offsetof(PyCoroObject, cr_running), READONLY}, - {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY}, - {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY}, - {NULL} /* Sentinel */ -}; - -PyDoc_STRVAR(coro_send_doc, -"send(arg) -> send 'arg' into coroutine,\n\ -return next iterated value or raise StopIteration."); - -PyDoc_STRVAR(coro_throw_doc, -"throw(typ[,val[,tb]]) -> raise exception in coroutine,\n\ -return next iterated value or raise StopIteration."); - -PyDoc_STRVAR(coro_close_doc, -"close() -> raise GeneratorExit inside coroutine."); - -static PyMethodDef coro_methods[] = { - {"send",(PyCFunction)_PyGen_Send, METH_O, coro_send_doc}, - {"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc}, - {"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc}, - {NULL, NULL} /* Sentinel */ -}; - -static PyAsyncMethods coro_as_async = { - (unaryfunc)coro_await, /* am_await */ - 0, /* am_aiter */ - 0 /* am_anext */ -}; - -PyTypeObject PyCoro_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "coroutine", /* tp_name */ - sizeof(PyCoroObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)gen_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - &coro_as_async, /* tp_as_async */ - (reprfunc)coro_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)gen_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(PyCoroObject, cr_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - coro_methods, /* tp_methods */ - coro_memberlist, /* tp_members */ - coro_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ -}; - -static void -coro_wrapper_dealloc(PyCoroWrapper *cw) -{ - _PyObject_GC_UNTRACK((PyObject *)cw); - Py_CLEAR(cw->cw_coroutine); - PyObject_GC_Del(cw); -} - -static PyObject * -coro_wrapper_iternext(PyCoroWrapper *cw) -{ - return gen_send_ex((PyGenObject *)cw->cw_coroutine, NULL, 0, 0); -} - -static PyObject * -coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg) -{ - return gen_send_ex((PyGenObject *)cw->cw_coroutine, arg, 0, 0); -} - -static PyObject * -coro_wrapper_throw(PyCoroWrapper *cw, PyObject *args) -{ - return gen_throw((PyGenObject *)cw->cw_coroutine, args); -} - -static PyObject * -coro_wrapper_close(PyCoroWrapper *cw, PyObject *args) -{ - return gen_close((PyGenObject *)cw->cw_coroutine, args); -} - -static int -coro_wrapper_traverse(PyCoroWrapper *cw, visitproc visit, void *arg) -{ - Py_VISIT((PyObject *)cw->cw_coroutine); - return 0; -} - -static PyMethodDef coro_wrapper_methods[] = { - {"send",(PyCFunction)coro_wrapper_send, METH_O, coro_send_doc}, - {"throw",(PyCFunction)coro_wrapper_throw, METH_VARARGS, coro_throw_doc}, - {"close",(PyCFunction)coro_wrapper_close, METH_NOARGS, coro_close_doc}, - {NULL, NULL} /* Sentinel */ -}; - -PyTypeObject _PyCoroWrapper_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "coroutine_wrapper", - sizeof(PyCoroWrapper), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)coro_wrapper_dealloc, /* destructor tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "A wrapper object implementing __await__ for coroutines.", - (traverseproc)coro_wrapper_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)coro_wrapper_iternext, /* tp_iternext */ - coro_wrapper_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ -}; - -static PyObject * -compute_cr_origin(int origin_depth) -{ - PyFrameObject *frame = PyEval_GetFrame(); - /* First count how many frames we have */ - int frame_count = 0; - for (; frame && frame_count < origin_depth; ++frame_count) { - frame = frame->f_back; - } - - /* Now collect them */ - PyObject *cr_origin = PyTuple_New(frame_count); - if (cr_origin == NULL) { - return NULL; - } - frame = PyEval_GetFrame(); - for (int i = 0; i < frame_count; ++i) { - PyObject *frameinfo = Py_BuildValue( - "OiO", - frame->f_code->co_filename, - PyFrame_GetLineNumber(frame), - frame->f_code->co_name); - if (!frameinfo) { - Py_DECREF(cr_origin); - return NULL; - } - PyTuple_SET_ITEM(cr_origin, i, frameinfo); - frame = frame->f_back; - } - - return cr_origin; -} - -PyObject * -PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname) -{ - PyObject *coro = gen_new_with_qualname(&PyCoro_Type, f, name, qualname); - if (!coro) { - return NULL; - } - - PyThreadState *tstate = _PyThreadState_GET(); - int origin_depth = tstate->coroutine_origin_tracking_depth; - - if (origin_depth == 0) { - ((PyCoroObject *)coro)->cr_origin = NULL; - } else { - PyObject *cr_origin = compute_cr_origin(origin_depth); - ((PyCoroObject *)coro)->cr_origin = cr_origin; - if (!cr_origin) { - Py_DECREF(coro); - return NULL; - } - } - - return coro; -} - - -/* ========= Asynchronous Generators ========= */ - - -typedef enum { - AWAITABLE_STATE_INIT, /* new awaitable, has not yet been iterated */ - AWAITABLE_STATE_ITER, /* being iterated */ - AWAITABLE_STATE_CLOSED, /* closed */ -} AwaitableState; - - -typedef struct { - PyObject_HEAD - PyAsyncGenObject *ags_gen; - - /* Can be NULL, when in the __anext__() mode - (equivalent of "asend(None)") */ - PyObject *ags_sendval; - - AwaitableState ags_state; -} PyAsyncGenASend; - - -typedef struct { - PyObject_HEAD - PyAsyncGenObject *agt_gen; - - /* Can be NULL, when in the "aclose()" mode - (equivalent of "athrow(GeneratorExit)") */ - PyObject *agt_args; - - AwaitableState agt_state; -} PyAsyncGenAThrow; - - -typedef struct { - PyObject_HEAD - PyObject *agw_val; -} _PyAsyncGenWrappedValue; - - -#ifndef _PyAsyncGen_MAXFREELIST -#define _PyAsyncGen_MAXFREELIST 80 -#endif - -/* Freelists boost performance 6-10%; they also reduce memory - fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend - are short-living objects that are instantiated for every - __anext__ call. -*/ - -static _PyAsyncGenWrappedValue *ag_value_freelist[_PyAsyncGen_MAXFREELIST]; -static int ag_value_freelist_free = 0; - -static PyAsyncGenASend *ag_asend_freelist[_PyAsyncGen_MAXFREELIST]; -static int ag_asend_freelist_free = 0; - -#define _PyAsyncGenWrappedValue_CheckExact(o) \ - (Py_TYPE(o) == &_PyAsyncGenWrappedValue_Type) - -#define PyAsyncGenASend_CheckExact(o) \ - (Py_TYPE(o) == &_PyAsyncGenASend_Type) - - -static int -async_gen_traverse(PyAsyncGenObject *gen, visitproc visit, void *arg) -{ - Py_VISIT(gen->ag_finalizer); - return gen_traverse((PyGenObject*)gen, visit, arg); -} - - -static PyObject * -async_gen_repr(PyAsyncGenObject *o) -{ - return PyUnicode_FromFormat("<async_generator object %S at %p>", - o->ag_qualname, o); -} - - -static int -async_gen_init_hooks(PyAsyncGenObject *o) -{ - PyThreadState *tstate; - PyObject *finalizer; - PyObject *firstiter; - - if (o->ag_hooks_inited) { - return 0; - } - - o->ag_hooks_inited = 1; - - tstate = _PyThreadState_GET(); - - finalizer = tstate->async_gen_finalizer; - if (finalizer) { - Py_INCREF(finalizer); - o->ag_finalizer = finalizer; - } - - firstiter = tstate->async_gen_firstiter; - if (firstiter) { - PyObject *res; - - Py_INCREF(firstiter); - res = _PyObject_CallOneArg(firstiter, (PyObject *)o); - Py_DECREF(firstiter); - if (res == NULL) { - return 1; - } - Py_DECREF(res); - } - - return 0; -} - - -static PyObject * -async_gen_anext(PyAsyncGenObject *o) -{ - if (async_gen_init_hooks(o)) { - return NULL; - } - return async_gen_asend_new(o, NULL); -} - - -static PyObject * -async_gen_asend(PyAsyncGenObject *o, PyObject *arg) -{ - if (async_gen_init_hooks(o)) { - return NULL; - } - return async_gen_asend_new(o, arg); -} - - -static PyObject * -async_gen_aclose(PyAsyncGenObject *o, PyObject *arg) -{ - if (async_gen_init_hooks(o)) { - return NULL; - } - return async_gen_athrow_new(o, NULL); -} - -static PyObject * -async_gen_athrow(PyAsyncGenObject *o, PyObject *args) -{ - if (async_gen_init_hooks(o)) { - return NULL; - } - return async_gen_athrow_new(o, args); -} - - -static PyGetSetDef async_gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, (setter)gen_set_name, - PyDoc_STR("name of the async generator")}, - {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, - PyDoc_STR("qualified name of the async generator")}, - {"ag_await", (getter)coro_get_cr_await, NULL, - PyDoc_STR("object being awaited on, or None")}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef async_gen_memberlist[] = { - {"ag_frame", T_OBJECT, offsetof(PyAsyncGenObject, ag_frame), READONLY}, - {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async), - READONLY}, - {"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY}, - {NULL} /* Sentinel */ -}; - -PyDoc_STRVAR(async_aclose_doc, -"aclose() -> raise GeneratorExit inside generator."); - -PyDoc_STRVAR(async_asend_doc, -"asend(v) -> send 'v' in generator."); - -PyDoc_STRVAR(async_athrow_doc, -"athrow(typ[,val[,tb]]) -> raise exception in generator."); - -static PyMethodDef async_gen_methods[] = { - {"asend", (PyCFunction)async_gen_asend, METH_O, async_asend_doc}, - {"athrow",(PyCFunction)async_gen_athrow, METH_VARARGS, async_athrow_doc}, - {"aclose", (PyCFunction)async_gen_aclose, METH_NOARGS, async_aclose_doc}, - {NULL, NULL} /* Sentinel */ -}; - - -static PyAsyncMethods async_gen_as_async = { - 0, /* am_await */ - PyObject_SelfIter, /* am_aiter */ - (unaryfunc)async_gen_anext /* am_anext */ -}; - - -PyTypeObject PyAsyncGen_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "async_generator", /* tp_name */ - sizeof(PyAsyncGenObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)gen_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - &async_gen_as_async, /* tp_as_async */ - (reprfunc)async_gen_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)async_gen_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(PyAsyncGenObject, ag_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - async_gen_methods, /* tp_methods */ - async_gen_memberlist, /* tp_members */ - async_gen_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ -}; - - -PyObject * -PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname) -{ - PyAsyncGenObject *o; - o = (PyAsyncGenObject *)gen_new_with_qualname( - &PyAsyncGen_Type, f, name, qualname); - if (o == NULL) { - return NULL; - } - o->ag_finalizer = NULL; - o->ag_closed = 0; - o->ag_hooks_inited = 0; - o->ag_running_async = 0; - return (PyObject*)o; -} - - int -PyAsyncGen_ClearFreeLists(void) -{ - int ret = ag_value_freelist_free + ag_asend_freelist_free; - - while (ag_value_freelist_free) { - _PyAsyncGenWrappedValue *o; - o = ag_value_freelist[--ag_value_freelist_free]; - assert(_PyAsyncGenWrappedValue_CheckExact(o)); - PyObject_GC_Del(o); - } - - while (ag_asend_freelist_free) { - PyAsyncGenASend *o; - o = ag_asend_freelist[--ag_asend_freelist_free]; - assert(Py_TYPE(o) == &_PyAsyncGenASend_Type); - PyObject_GC_Del(o); - } - - return ret; -} - -void -_PyAsyncGen_Fini(void) -{ - PyAsyncGen_ClearFreeLists(); -} - - -static PyObject * -async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result) -{ - if (result == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetNone(PyExc_StopAsyncIteration); - } - - if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) - || PyErr_ExceptionMatches(PyExc_GeneratorExit) - ) { - gen->ag_closed = 1; - } - - gen->ag_running_async = 0; - return NULL; - } - - if (_PyAsyncGenWrappedValue_CheckExact(result)) { - /* async yield */ - _PyGen_SetStopIterationValue(((_PyAsyncGenWrappedValue*)result)->agw_val); - Py_DECREF(result); - gen->ag_running_async = 0; - return NULL; - } - - return result; -} - - -/* ---------- Async Generator ASend Awaitable ------------ */ - - -static void -async_gen_asend_dealloc(PyAsyncGenASend *o) -{ - _PyObject_GC_UNTRACK((PyObject *)o); - Py_CLEAR(o->ags_gen); - Py_CLEAR(o->ags_sendval); - if (ag_asend_freelist_free < _PyAsyncGen_MAXFREELIST) { - assert(PyAsyncGenASend_CheckExact(o)); - ag_asend_freelist[ag_asend_freelist_free++] = o; - } else { - PyObject_GC_Del(o); - } -} - -static int -async_gen_asend_traverse(PyAsyncGenASend *o, visitproc visit, void *arg) -{ - Py_VISIT(o->ags_gen); - Py_VISIT(o->ags_sendval); - return 0; -} - - -static PyObject * -async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg) -{ - PyObject *result; - - if (o->ags_state == AWAITABLE_STATE_CLOSED) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - - if (o->ags_state == AWAITABLE_STATE_INIT) { - if (o->ags_gen->ag_running_async) { - PyErr_SetString( - PyExc_RuntimeError, - "anext(): asynchronous generator is already running"); - return NULL; - } - - if (arg == NULL || arg == Py_None) { - arg = o->ags_sendval; - } - o->ags_state = AWAITABLE_STATE_ITER; - } - - o->ags_gen->ag_running_async = 1; - result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0); - result = async_gen_unwrap_value(o->ags_gen, result); - - if (result == NULL) { - o->ags_state = AWAITABLE_STATE_CLOSED; - } - - return result; -} - - -static PyObject * -async_gen_asend_iternext(PyAsyncGenASend *o) -{ - return async_gen_asend_send(o, NULL); -} - - -static PyObject * -async_gen_asend_throw(PyAsyncGenASend *o, PyObject *args) +PyGen_NeedsFinalizing(PyGenObject *gen) { - PyObject *result; - - if (o->ags_state == AWAITABLE_STATE_CLOSED) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - - result = gen_throw((PyGenObject*)o->ags_gen, args); - result = async_gen_unwrap_value(o->ags_gen, result); - - if (result == NULL) { - o->ags_state = AWAITABLE_STATE_CLOSED; - } - - return result; -} - - -static PyObject * -async_gen_asend_close(PyAsyncGenASend *o, PyObject *args) -{ - o->ags_state = AWAITABLE_STATE_CLOSED; - Py_RETURN_NONE; -} - - -static PyMethodDef async_gen_asend_methods[] = { - {"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc}, - {"throw", (PyCFunction)async_gen_asend_throw, METH_VARARGS, throw_doc}, - {"close", (PyCFunction)async_gen_asend_close, METH_NOARGS, close_doc}, - {NULL, NULL} /* Sentinel */ -}; - - -static PyAsyncMethods async_gen_asend_as_async = { - PyObject_SelfIter, /* am_await */ - 0, /* am_aiter */ - 0 /* am_anext */ -}; - - -PyTypeObject _PyAsyncGenASend_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "async_generator_asend", /* tp_name */ - sizeof(PyAsyncGenASend), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)async_gen_asend_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - &async_gen_asend_as_async, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)async_gen_asend_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)async_gen_asend_iternext, /* tp_iternext */ - async_gen_asend_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - - -static PyObject * -async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval) -{ - PyAsyncGenASend *o; - if (ag_asend_freelist_free) { - ag_asend_freelist_free--; - o = ag_asend_freelist[ag_asend_freelist_free]; - _Py_NewReference((PyObject *)o); - } else { - o = PyObject_GC_New(PyAsyncGenASend, &_PyAsyncGenASend_Type); - if (o == NULL) { - return NULL; - } - } - - Py_INCREF(gen); - o->ags_gen = gen; - - Py_XINCREF(sendval); - o->ags_sendval = sendval; - - o->ags_state = AWAITABLE_STATE_INIT; - - _PyObject_GC_TRACK((PyObject*)o); - return (PyObject*)o; -} - - -/* ---------- Async Generator Value Wrapper ------------ */ - - -static void -async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o) -{ - _PyObject_GC_UNTRACK((PyObject *)o); - Py_CLEAR(o->agw_val); - if (ag_value_freelist_free < _PyAsyncGen_MAXFREELIST) { - assert(_PyAsyncGenWrappedValue_CheckExact(o)); - ag_value_freelist[ag_value_freelist_free++] = o; - } else { - PyObject_GC_Del(o); - } -} - - -static int -async_gen_wrapped_val_traverse(_PyAsyncGenWrappedValue *o, - visitproc visit, void *arg) -{ - Py_VISIT(o->agw_val); - return 0; -} - - -PyTypeObject _PyAsyncGenWrappedValue_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "async_generator_wrapped_value", /* tp_name */ - sizeof(_PyAsyncGenWrappedValue), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)async_gen_wrapped_val_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)async_gen_wrapped_val_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - - -PyObject * -_PyAsyncGenValueWrapperNew(PyObject *val) -{ - _PyAsyncGenWrappedValue *o; - assert(val); - - if (ag_value_freelist_free) { - ag_value_freelist_free--; - o = ag_value_freelist[ag_value_freelist_free]; - assert(_PyAsyncGenWrappedValue_CheckExact(o)); - _Py_NewReference((PyObject*)o); - } else { - o = PyObject_GC_New(_PyAsyncGenWrappedValue, - &_PyAsyncGenWrappedValue_Type); - if (o == NULL) { - return NULL; - } - } - o->agw_val = val; - Py_INCREF(val); - _PyObject_GC_TRACK((PyObject*)o); - return (PyObject*)o; -} - - -/* ---------- Async Generator AThrow awaitable ------------ */ - - -static void -async_gen_athrow_dealloc(PyAsyncGenAThrow *o) -{ - _PyObject_GC_UNTRACK((PyObject *)o); - Py_CLEAR(o->agt_gen); - Py_CLEAR(o->agt_args); - PyObject_GC_Del(o); -} - - -static int -async_gen_athrow_traverse(PyAsyncGenAThrow *o, visitproc visit, void *arg) -{ - Py_VISIT(o->agt_gen); - Py_VISIT(o->agt_args); - return 0; -} - - -static PyObject * -async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) -{ - PyGenObject *gen = (PyGenObject*)o->agt_gen; + int i; PyFrameObject *f = gen->gi_frame; - PyObject *retval; - if (f == NULL || f->f_stacktop == NULL || - o->agt_state == AWAITABLE_STATE_CLOSED) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } + if (f == NULL || f->f_stacktop == NULL || f->f_iblock <= 0) + return 0; /* no frame or empty blockstack == no finalization */ - if (o->agt_state == AWAITABLE_STATE_INIT) { - if (o->agt_gen->ag_running_async) { - if (o->agt_args == NULL) { - PyErr_SetString( - PyExc_RuntimeError, - "aclose(): asynchronous generator is already running"); - } - else { - PyErr_SetString( - PyExc_RuntimeError, - "athrow(): asynchronous generator is already running"); - } - return NULL; - } - - if (o->agt_gen->ag_closed) { - o->agt_state = AWAITABLE_STATE_CLOSED; - PyErr_SetNone(PyExc_StopAsyncIteration); - return NULL; - } - - if (arg != Py_None) { - PyErr_SetString(PyExc_RuntimeError, NON_INIT_CORO_MSG); - return NULL; - } - - o->agt_state = AWAITABLE_STATE_ITER; - o->agt_gen->ag_running_async = 1; - - if (o->agt_args == NULL) { - /* aclose() mode */ - o->agt_gen->ag_closed = 1; - - retval = _gen_throw((PyGenObject *)gen, - 0, /* Do not close generator when - PyExc_GeneratorExit is passed */ - PyExc_GeneratorExit, NULL, NULL); - - if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) { - Py_DECREF(retval); - goto yield_close; - } - } else { - PyObject *typ; - PyObject *tb = NULL; - PyObject *val = NULL; - - if (!PyArg_UnpackTuple(o->agt_args, "athrow", 1, 3, - &typ, &val, &tb)) { - return NULL; - } - - retval = _gen_throw((PyGenObject *)gen, - 0, /* Do not close generator when - PyExc_GeneratorExit is passed */ - typ, val, tb); - retval = async_gen_unwrap_value(o->agt_gen, retval); - } - if (retval == NULL) { - goto check_error; - } - return retval; - } - - assert(o->agt_state == AWAITABLE_STATE_ITER); - - retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0); - if (o->agt_args) { - return async_gen_unwrap_value(o->agt_gen, retval); - } else { - /* aclose() mode */ - if (retval) { - if (_PyAsyncGenWrappedValue_CheckExact(retval)) { - o->agt_gen->ag_running_async = 0; - Py_DECREF(retval); - goto yield_close; - } - else { - return retval; - } - } - else { - goto check_error; - } - } - -yield_close: - o->agt_gen->ag_running_async = 0; - PyErr_SetString( - PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG); - return NULL; - -check_error: - o->agt_gen->ag_running_async = 0; - if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || - PyErr_ExceptionMatches(PyExc_GeneratorExit)) - { - o->agt_state = AWAITABLE_STATE_CLOSED; - if (o->agt_args == NULL) { - /* when aclose() is called we don't want to propagate - StopAsyncIteration or GeneratorExit; just raise - StopIteration, signalling that this 'aclose()' await - is done. - */ - PyErr_Clear(); - PyErr_SetNone(PyExc_StopIteration); - } - } - return NULL; -} - - -static PyObject * -async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *args) -{ - PyObject *retval; - - if (o->agt_state == AWAITABLE_STATE_CLOSED) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - - retval = gen_throw((PyGenObject*)o->agt_gen, args); - if (o->agt_args) { - return async_gen_unwrap_value(o->agt_gen, retval); - } else { - /* aclose() mode */ - if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) { - o->agt_gen->ag_running_async = 0; - Py_DECREF(retval); - PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG); - return NULL; - } - if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || - PyErr_ExceptionMatches(PyExc_GeneratorExit)) - { - /* when aclose() is called we don't want to propagate - StopAsyncIteration or GeneratorExit; just raise - StopIteration, signalling that this 'aclose()' await - is done. - */ - PyErr_Clear(); - PyErr_SetNone(PyExc_StopIteration); - } - return retval; + /* Any block type besides a loop requires cleanup. */ + i = f->f_iblock; + while (--i >= 0) { + if (f->f_blockstack[i].b_type != SETUP_LOOP) + return 1; } -} - -static PyObject * -async_gen_athrow_iternext(PyAsyncGenAThrow *o) -{ - return async_gen_athrow_send(o, Py_None); -} - - -static PyObject * -async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args) -{ - o->agt_state = AWAITABLE_STATE_CLOSED; - Py_RETURN_NONE; -} - - -static PyMethodDef async_gen_athrow_methods[] = { - {"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc}, - {"throw", (PyCFunction)async_gen_athrow_throw, METH_VARARGS, throw_doc}, - {"close", (PyCFunction)async_gen_athrow_close, METH_NOARGS, close_doc}, - {NULL, NULL} /* Sentinel */ -}; - - -static PyAsyncMethods async_gen_athrow_as_async = { - PyObject_SelfIter, /* am_await */ - 0, /* am_aiter */ - 0 /* am_anext */ -}; - - -PyTypeObject _PyAsyncGenAThrow_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "async_generator_athrow", /* tp_name */ - sizeof(PyAsyncGenAThrow), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)async_gen_athrow_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - &async_gen_athrow_as_async, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)async_gen_athrow_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)async_gen_athrow_iternext, /* tp_iternext */ - async_gen_athrow_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - - -static PyObject * -async_gen_athrow_new(PyAsyncGenObject *gen, PyObject *args) -{ - PyAsyncGenAThrow *o; - o = PyObject_GC_New(PyAsyncGenAThrow, &_PyAsyncGenAThrow_Type); - if (o == NULL) { - return NULL; - } - o->agt_gen = gen; - o->agt_args = args; - o->agt_state = AWAITABLE_STATE_INIT; - Py_INCREF(gen); - Py_XINCREF(args); - _PyObject_GC_TRACK((PyObject*)o); - return (PyObject*)o; + /* No blocks except loops, it's safe to skip finalization. */ + return 0; } |