static int gen_close_iter(PyObject *yf) { PyObject *retval = NULL; _Py_IDENTIFIER(close); if (PyGen_CheckExact(yf)) { retval = gen_close((PyGenObject *)yf, NULL); if (retval == NULL) return -1; } else { PyObject *meth = _PyObject_GetAttrId(yf, &PyId_close); if (meth == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_WriteUnraisable(yf); PyErr_Clear(); } else { retval = PyObject_CallFunction(meth, ""); Py_DECREF(meth); if (retval == NULL) return -1; } } Py_XDECREF(retval); return 0; }
/* Return true if object has a finalization method. */ static int has_finalizer(PyObject *op) { /* first, dynamic decision per object */ traverseproc traverse; int collectable; traverse = Py_TYPE(op)->tp_traverse; collectable = -1; (void) traverse(op, (visitproc)visit_has_finalizer, (void *)&collectable); if (collectable >= 0) return collectable == 0; if ( PyType_Check(op)) { assert(delstr != NULL); return _PyType_Lookup(Py_TYPE(op), delstr) != NULL; } else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) return op->ob_type->tp_del != NULL; else if (PyGen_CheckExact(op)) return PyGen_NeedsFinalizing((PyGenObject *)op); else return op->ob_type->tp_del != NULL; }
/* Return true if object has a finalization method. */ static int has_finalizer(PyObject *op) { if (PyGen_CheckExact(op)) return PyGen_NeedsFinalizing((PyGenObject *)op); else return op->ob_type->tp_del != NULL; }
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; }
/* Return true if object has a finalization method. * CAUTION: An instance of an old-style class has to be checked for a *__del__ method, and earlier versions of this used to call PyObject_HasAttr, * which in turn could call the class's __getattr__ hook (if any). That * could invoke arbitrary Python code, mutating the object graph in arbitrary * ways, and that was the source of some excruciatingly subtle bugs. */ static int has_finalizer(PyObject *op) { if (PyInstance_Check(op)) { assert(delstr != NULL); return _PyInstance_Lookup(op, delstr) != NULL; } else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) return op->ob_type->tp_del != NULL; else if (PyGen_CheckExact(op)) return PyGen_NeedsFinalizing((PyGenObject *)op); else return 0; }
static PyObject * gen_throw(PyGenObject *gen, PyObject *args) { PyObject *typ; PyObject *tb = NULL; PyObject *val = NULL; PyObject *yf = _PyGen_yf(gen); _Py_IDENTIFIER(throw); if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) return NULL; if (yf) { PyObject *ret; int err; if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) { 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)) { gen->gi_running = 1; ret = gen_throw((PyGenObject *)yf, args); gen->gi_running = 0; } else { PyObject *meth = _PyObject_GetAttrId(yf, &PyId_throw); if (meth == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { Py_DECREF(yf); return NULL; } PyErr_Clear(); Py_DECREF(yf); goto throw_here; } gen->gi_running = 1; ret = PyObject_CallObject(meth, args); 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 */ gen->gi_frame->f_lasti += 2; 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; } throw_here: /* First, check the traceback argument, replacing None with NULL. */ 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"); return NULL; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); if (PyExceptionClass_Check(typ)) PyErr_NormalizeException(&typ, &val, &tb); else if (PyExceptionInstance_Check(typ)) { /* Raising an instance. The value should be a dummy. */ if (val && val != Py_None) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto failed_throw; } else { /* Normalize to raise <class>, <instance> */ Py_XDECREF(val); 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); goto failed_throw; } PyErr_Restore(typ, val, tb); return gen_send_ex(gen, Py_None, 1, 0); failed_throw: /* Didn't use our arguments, so restore their original refcounts */ Py_DECREF(typ); Py_XDECREF(val); Py_XDECREF(tb); return NULL; }
static PyObject *_YIELD_FROM( struct Nuitka_GeneratorObject *generator, PyObject *value ) { // This is the value, propagated back and forth the sub-generator and the // yield from consumer. PyObject *send_value = Py_None; while( 1 ) { // Send iteration value to the sub-generator, which may be a CPython // generator object, something with an iterator next, or a send method, // where the later is only required if values other than "None" need to // be passed in. PyObject *retval; // Exception, was thrown into us, need to send that to sub-generator. if ( generator->m_exception_type ) { // The yielding generator is being closed, but we also are tasked to // immediately close the currently running sub-generator. if ( EXCEPTION_MATCH_BOOL_SINGLE( generator->m_exception_type, PyExc_GeneratorExit ) ) { PyObject *close_method = PyObject_GetAttr( value, const_str_plain_close ); if ( close_method ) { PyObject *close_value = PyObject_Call( close_method, const_tuple_empty, NULL ); Py_DECREF( close_method ); if (unlikely( close_value == NULL )) { return NULL; } Py_DECREF( close_value ); } else { PyObject *error = GET_ERROR_OCCURRED(); if ( error != NULL && !EXCEPTION_MATCH_BOOL_SINGLE( error, PyExc_AttributeError ) ) { PyErr_WriteUnraisable( (PyObject *)value ); } } RAISE_GENERATOR_EXCEPTION( generator ); return NULL; } PyObject *throw_method = PyObject_GetAttr( value, const_str_plain_throw ); if ( throw_method ) { retval = PyObject_CallFunctionObjArgs( throw_method, generator->m_exception_type, generator->m_exception_value, generator->m_exception_tb, NULL ); Py_DECREF( throw_method ); if (unlikely( send_value == NULL )) { if ( EXCEPTION_MATCH_BOOL_SINGLE( GET_ERROR_OCCURRED(), PyExc_StopIteration ) ) { return ERROR_GET_STOP_ITERATION_VALUE(); } return NULL; } generator->m_exception_type = NULL; generator->m_exception_value = NULL; generator->m_exception_tb = NULL; } else if ( EXCEPTION_MATCH_BOOL_SINGLE( GET_ERROR_OCCURRED(), PyExc_AttributeError ) ) { CLEAR_ERROR_OCCURRED(); RAISE_GENERATOR_EXCEPTION( generator ); return NULL; } else { assert( ERROR_OCCURRED() ); Py_CLEAR( generator->m_exception_type ); Py_CLEAR( generator->m_exception_value ); Py_CLEAR( generator->m_exception_tb ); return NULL; } } else if ( PyGen_CheckExact( value ) ) { retval = PyGen_Send( (PyGenObject *)value, Py_None ); } else if ( send_value == Py_None && Py_TYPE( value )->tp_iternext != NULL ) { retval = Py_TYPE( value )->tp_iternext( value ); } else { // Bug compatibility here, before 3.3 tuples were unrolled in calls, which is what // PyObject_CallMethod does. #if PYTHON_VERSION >= 340 retval = PyObject_CallMethodObjArgs( value, const_str_plain_send, send_value, NULL ); #else retval = PyObject_CallMethod( value, (char *)"send", (char *)"O", send_value ); #endif } // Check the sub-generator result if ( retval == NULL ) { PyObject *error = GET_ERROR_OCCURRED(); if ( error == NULL ) { return INCREASE_REFCOUNT( Py_None ) ; } // The sub-generator has given an exception. In case of // StopIteration, we need to check the value, as it is going to be // the expression value of this "yield from", and we are done. All // other errors, we need to raise. if (likely( EXCEPTION_MATCH_BOOL_SINGLE( error, PyExc_StopIteration ) )) { return ERROR_GET_STOP_ITERATION_VALUE(); } return NULL; } else { generator->m_yielded = retval; #if PYTHON_VERSION >= 350 generator->m_yieldfrom = value; #endif // Return to the calling context. swapFiber( &generator->m_yielder_context, &generator->m_caller_context ); #if PYTHON_VERSION >= 350 generator->m_yieldfrom = NULL; #endif send_value = generator->m_yielded; CHECK_OBJECT( send_value ); } } }