cpython: edefd3450834 (original) (raw)
Mercurial > cpython
changeset 83771:edefd3450834
Backout c89febab4648 following private feedback by Guido. (Issue #17807: Generators can now be finalized even when they are part of a reference cycle) [#17807]
Antoine Pitrou solipsis@pitrou.net | |
---|---|
date | Tue, 14 May 2013 20:37:52 +0200 |
parents | 85ecc4761a6c |
children | bb8093e427f9 |
files | Include/frameobject.h Include/genobject.h Lib/test/test_generators.py Lib/test/test_sys.py Misc/NEWS Modules/gcmodule.c Objects/frameobject.c Objects/genobject.c |
diffstat | 8 files changed, 244 insertions(+), 334 deletions(-)[+] [-] Include/frameobject.h 9 Include/genobject.h 1 Lib/test/test_generators.py 53 Lib/test/test_sys.py 2 Misc/NEWS 3 Modules/gcmodule.c 5 Objects/frameobject.c 254 Objects/genobject.c 251 |
line wrap: on
line diff
--- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -36,8 +36,6 @@ typedef struct _frame { non-generator frames. See the save_exc_state and swap_exc_state functions in ceval.c for details of their use. */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState f_tstate; int f_lasti; / Last instruction if called / @@ -86,13 +84,6 @@ PyAPI_FUNC(void) _PyFrame_DebugMallocSta / Return the line of code the frame is currently executing. */ PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject ); -/ Generator support */ -PyAPI_FUNC(PyObject *) _PyFrame_YieldingFrom(PyFrameObject *); -PyAPI_FUNC(PyObject *) _PyFrame_GeneratorSend(PyFrameObject *, PyObject *, int exc); -PyAPI_FUNC(PyObject *) _PyFrame_Finalize(PyFrameObject *); -PyAPI_FUNC(int) _PyFrame_CloseIterator(PyObject *); - - #ifdef __cplusplus } #endif
--- a/Include/genobject.h +++ b/Include/genobject.h @@ -33,7 +33,6 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame ); -/ Deprecated, kept for backwards compatibility. */ PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_Send(PyGenObject *, PyObject *);
--- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -1,55 +1,3 @@ -import gc -import sys -import unittest -import weakref - -from test import support - - -class FinalizationTest(unittest.TestCase): -
- def test_frame_resurrect(self):
# A generator frame can be resurrected by a generator's finalization.[](#l3.15)
def gen():[](#l3.16)
nonlocal frame[](#l3.17)
try:[](#l3.18)
yield[](#l3.19)
finally:[](#l3.20)
frame = sys._getframe()[](#l3.21)
g = gen()[](#l3.23)
wr = weakref.ref(g)[](#l3.24)
next(g)[](#l3.25)
del g[](#l3.26)
support.gc_collect()[](#l3.27)
self.assertIs(wr(), None)[](#l3.28)
self.assertTrue(frame)[](#l3.29)
del frame[](#l3.30)
support.gc_collect()[](#l3.31)
- def test_refcycle(self):
# A generator caught in a refcycle gets finalized anyway.[](#l3.34)
old_garbage = gc.garbage[:][](#l3.35)
finalized = False[](#l3.36)
def gen():[](#l3.37)
nonlocal finalized[](#l3.38)
try:[](#l3.39)
g = yield[](#l3.40)
yield 1[](#l3.41)
finally:[](#l3.42)
finalized = True[](#l3.43)
g = gen()[](#l3.45)
next(g)[](#l3.46)
g.send(g)[](#l3.47)
self.assertGreater(sys.getrefcount(g), 2)[](#l3.48)
self.assertFalse(finalized)[](#l3.49)
del g[](#l3.50)
support.gc_collect()[](#l3.51)
self.assertTrue(finalized)[](#l3.52)
self.assertEqual(gc.garbage, old_garbage)[](#l3.53)
- - tutorial_tests = """ Let's try a simple generator: @@ -1932,7 +1880,6 @@ test_generators just happened to be the
so this works as expected in both ways of running regrtest.
def test_main(verbose=None): from test import support, test_generators
This part isn't needed for regrtest, but for running the test directly.
--- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -764,7 +764,7 @@ class SizeofTest(unittest.TestCase): nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +[](#l4.5) ncells + nfrees - 1
check(x, vsize('13P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))[](#l4.7)
check(x, vsize('12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))[](#l4.8) # function[](#l4.9) def func(): pass[](#l4.10) check(func, size('12P'))[](#l4.11)
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,9 +15,6 @@ Core and Builtins
- Issue #17927: Frame objects kept arguments alive if they had been copied into a cell, even if the cell was cleared. -- Issue #17807: Generators can now be finalized even when they are part of
- a reference cycle. -
- Issue #1545463: At shutdown, defer finalization of codec modules so that stderr remains usable.
--- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -524,7 +524,10 @@ untrack_dicts(PyGC_Head *head) static int has_finalizer(PyObject *op) {
- if (PyGen_CheckExact(op))
return PyGen_NeedsFinalizing((PyGenObject *)op);[](#l6.9)
- else
return op->ob_type->tp_del != NULL;[](#l6.11)
}
/* Move the objects in unreachable with del methods into finalizers
.
--- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -31,195 +31,6 @@ frame_getlocals(PyFrameObject *f, void return f->f_locals; } -/
- if (f && f->f_stacktop) {
PyObject *bytecode = f->f_code->co_code;[](#l7.17)
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);[](#l7.18)
if (code[f->f_lasti + 1] != YIELD_FROM)[](#l7.20)
return NULL;[](#l7.21)
yf = f->f_stacktop[-1];[](#l7.22)
Py_INCREF(yf);[](#l7.23)
- }
- return yf;
-} - -PyObject * -_PyFrame_GeneratorSend(PyFrameObject *f, PyObject *arg, int exc) -{
- PyThreadState *tstate = PyThreadState_GET();
- PyObject *result;
- PyGenObject *gen = (PyGenObject *) f->f_gen;
- assert(gen == NULL || PyGen_CheckExact(gen));
- if (gen && gen->gi_running) {
PyErr_SetString(PyExc_ValueError,[](#l7.37)
"generator already executing");[](#l7.38)
return NULL;[](#l7.39)
- }
- if (f->f_stacktop == NULL) {
/* Only set exception if send() called, not throw() or next() */[](#l7.42)
if (arg && !exc)[](#l7.43)
PyErr_SetNone(PyExc_StopIteration);[](#l7.44)
return NULL;[](#l7.45)
- }
- if (f->f_lasti == -1) {
if (arg && arg != Py_None) {[](#l7.49)
PyErr_SetString(PyExc_TypeError,[](#l7.50)
"can't send non-None value to a "[](#l7.51)
"just-started generator");[](#l7.52)
return NULL;[](#l7.53)
}[](#l7.54)
- } else {
/* Push arg onto the frame's value stack */[](#l7.56)
result = arg ? arg : Py_None;[](#l7.57)
Py_INCREF(result);[](#l7.58)
*(f->f_stacktop++) = result;[](#l7.59)
- }
- /* Generators always return to their most recent caller, not
* necessarily their creator. */[](#l7.63)
- Py_XINCREF(tstate->frame);
- assert(f->f_back == NULL);
- f->f_back = tstate->frame;
- if (gen) {
Py_INCREF(gen);[](#l7.69)
gen->gi_running = 1;[](#l7.70)
- }
- result = PyEval_EvalFrameEx(f, exc);
- if (gen) {
gen->gi_running = 0;[](#l7.74)
/* In case running the frame has lost all external references[](#l7.75)
* to gen, we must be careful not to hold on an invalid object. */[](#l7.76)
if (Py_REFCNT(gen) == 1)[](#l7.77)
Py_CLEAR(gen);[](#l7.78)
else[](#l7.79)
Py_DECREF(gen);[](#l7.80)
- }
- /* Don't keep the reference to f_back any longer than necessary. It
* may keep a chain of frames alive or it could create a reference[](#l7.84)
* cycle. */[](#l7.85)
- assert(f->f_back == tstate->frame);
- Py_CLEAR(f->f_back);
- /* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */[](#l7.90)
- if (result && f->f_stacktop == NULL) {
if (result == Py_None) {[](#l7.92)
/* Delay exception instantiation if we can */[](#l7.93)
PyErr_SetNone(PyExc_StopIteration);[](#l7.94)
} else {[](#l7.95)
PyObject *e = PyObject_CallFunctionObjArgs([](#l7.96)
PyExc_StopIteration, result, NULL);[](#l7.97)
if (e != NULL) {[](#l7.98)
PyErr_SetObject(PyExc_StopIteration, e);[](#l7.99)
Py_DECREF(e);[](#l7.100)
}[](#l7.101)
}[](#l7.102)
Py_CLEAR(result);[](#l7.103)
- }
- if (f->f_stacktop == NULL) {
/* generator can't be rerun, so release the frame */[](#l7.107)
/* first clean reference cycle through stored exception traceback */[](#l7.108)
PyObject *t, *v, *tb;[](#l7.109)
t = f->f_exc_type;[](#l7.110)
v = f->f_exc_value;[](#l7.111)
tb = f->f_exc_traceback;[](#l7.112)
f->f_exc_type = NULL;[](#l7.113)
f->f_exc_value = NULL;[](#l7.114)
f->f_exc_traceback = NULL;[](#l7.115)
Py_XDECREF(t);[](#l7.116)
Py_XDECREF(v);[](#l7.117)
Py_XDECREF(tb);[](#l7.118)
if (gen) {[](#l7.119)
f->f_gen = NULL;[](#l7.120)
Py_CLEAR(gen->gi_frame);[](#l7.121)
}[](#l7.122)
- }
-} - -int -_PyFrame_CloseIterator(PyObject *yf) -{
- if (PyGen_CheckExact(yf)) {
PyFrameObject *f = ((PyGenObject *) yf)->gi_frame;[](#l7.135)
assert(f != NULL);[](#l7.136)
retval = _PyFrame_Finalize(f);[](#l7.137)
if (retval == NULL)[](#l7.138)
return -1;[](#l7.139)
- } else {
PyObject *meth = _PyObject_GetAttrId(yf, &PyId_close);[](#l7.141)
if (meth == NULL) {[](#l7.142)
if (!PyErr_ExceptionMatches(PyExc_AttributeError))[](#l7.143)
PyErr_WriteUnraisable(yf);[](#l7.144)
PyErr_Clear();[](#l7.145)
} else {[](#l7.146)
retval = PyObject_CallFunction(meth, "");[](#l7.147)
Py_DECREF(meth);[](#l7.148)
if (retval == NULL)[](#l7.149)
return -1;[](#l7.150)
}[](#l7.151)
- }
- Py_XDECREF(retval);
- return 0;
-} - -PyObject * -_PyFrame_Finalize(PyFrameObject *f) -{
- int err = 0;
- PyObject *retval;
- PyGenObject *gen = (PyGenObject *) f->f_gen;
- PyObject *yf = _PyFrame_YieldingFrom(f);
- assert(gen == NULL || PyGen_CheckExact(gen));
- if (yf) {
if (gen)[](#l7.167)
gen->gi_running = 1;[](#l7.168)
err = _PyFrame_CloseIterator(yf);[](#l7.169)
if (gen)[](#l7.170)
gen->gi_running = 0;[](#l7.171)
Py_DECREF(yf);[](#l7.172)
- }
- if (err == 0)
PyErr_SetNone(PyExc_GeneratorExit);[](#l7.175)
- retval = _PyFrame_GeneratorSend(f, Py_None, 1);
- if (retval) {
Py_DECREF(retval);[](#l7.178)
PyErr_SetString(PyExc_RuntimeError,[](#l7.179)
"generator ignored GeneratorExit");[](#l7.180)
return NULL;[](#l7.181)
- }
- if (PyErr_ExceptionMatches(PyExc_StopIteration)
|| PyErr_ExceptionMatches(PyExc_GeneratorExit)) {[](#l7.184)
PyErr_Clear(); /* ignore these errors */[](#l7.185)
Py_INCREF(Py_None);[](#l7.186)
return Py_None;[](#l7.187)
- }
- return NULL;
- */ - int PyFrame_GetLineNumber(PyFrameObject f) { @@ -609,43 +420,32 @@ static int numfree = 0; / numbe
#define PyFrame_MAXFREELIST 200 static void -frame_clear(PyFrameObject *f); - -static void frame_dealloc(PyFrameObject *f) {
- Py_REFCNT(f)++;
- frame_clear(f);
- Py_REFCNT(f)--;
- if (Py_REFCNT(f) > 0) {
/* Frame resurrected! */[](#l7.215)
Py_ssize_t refcnt = Py_REFCNT(f);[](#l7.216)
_Py_NewReference((PyObject *) f);[](#l7.217)
Py_REFCNT(f) = refcnt;[](#l7.218)
/* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so[](#l7.219)
* we need to undo that. */[](#l7.220)
_Py_DEC_REFTOTAL;[](#l7.221)
/* If Py_TRACE_REFS, _Py_NewReference re-added self to the object[](#l7.222)
* chain, so no more to do there.[](#l7.223)
* If COUNT_ALLOCS, the original decref bumped tp_frees, and[](#l7.224)
* _Py_NewReference bumped tp_allocs: both of those need to be[](#l7.225)
* undone.[](#l7.226)
*/[](#l7.227)
--(Py_TYPE(self)->tp_frees);[](#l7.229)
--(Py_TYPE(self)->tp_allocs);[](#l7.230)
- PyObject_GC_UnTrack(f); Py_TRASHCAN_SAFE_BEGIN(f)
- /* Kill all local variables */
- valuestack = f->f_valuestack;
- for (p = f->f_localsplus; p < valuestack; p++)
Py_CLEAR(*p);[](#l7.239)
- /* Free stack */
- if (f->f_stacktop != NULL) {
for (p = valuestack; p < f->f_stacktop; p++)[](#l7.243)
Py_XDECREF(*p);[](#l7.244)
- }
Py_XDECREF(f->f_back); Py_DECREF(f->f_builtins); Py_DECREF(f->f_globals); Py_CLEAR(f->f_locals);
- Py_CLEAR(f->f_trace);
- Py_CLEAR(f->f_exc_type);
- Py_CLEAR(f->f_exc_value);
- Py_CLEAR(f->f_exc_traceback);
co = f->f_code; if (co->co_zombieframe == NULL) @@ -697,25 +497,12 @@ frame_clear(PyFrameObject *f) { PyObject **fastlocals, **p, **oldtop; Py_ssize_t i, slots;
- if (f->f_back == NULL) {
PyObject *t, *v, *tb;[](#l7.265)
PyErr_Fetch(&t, &v, &tb);[](#l7.266)
/* Note that this can finalize a suspended generator frame even[](#l7.267)
* if the generator object was disposed of (i.e. if f_gen is NULL).[](#l7.268)
*/[](#l7.269)
retval = _PyFrame_Finalize(f);[](#l7.270)
if (retval == NULL) {[](#l7.271)
if (PyErr_Occurred())[](#l7.272)
PyErr_WriteUnraisable((PyObject *) f);[](#l7.273)
}[](#l7.274)
else[](#l7.275)
Py_DECREF(retval);[](#l7.276)
PyErr_Restore(t, v, tb);[](#l7.277)
- }
- /* Before anything else, make sure that this frame is clearly marked
* as being defunct! Else, e.g., a generator reachable from this[](#l7.282)
* frame may also point to this frame, believe itself to still be[](#l7.283)
* active, and try cleaning up this frame again.[](#l7.284)
oldtop = f->f_stacktop; f->f_stacktop = NULL; @@ -926,7 +713,6 @@ PyFrame_New(PyThreadState *tstate, PyCod f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0;*/[](#l7.285)
_PyObject_GC_TRACK(f); return f;
--- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -19,50 +19,112 @@ static void gen_dealloc(PyGenObject *gen) { PyObject *self = (PyObject *) gen;
_PyObject_GC_UNTRACK(gen); if (gen->gi_weakreflist != NULL) PyObject_ClearWeakRefs(self);
- gen->gi_frame = NULL;
- if (f) {
/* Close the generator by finalizing the frame */[](#l8.16)
PyObject *retval, *t, *v, *tb;[](#l8.17)
PyErr_Fetch(&t, &v, &tb);[](#l8.18)
f->f_gen = NULL;[](#l8.19)
retval = _PyFrame_Finalize(f);[](#l8.20)
if (retval)[](#l8.21)
Py_DECREF(retval);[](#l8.22)
else if (PyErr_Occurred())[](#l8.23)
PyErr_WriteUnraisable((PyObject *) gen);[](#l8.24)
Py_DECREF(f);[](#l8.25)
PyErr_Restore(t, v, tb);[](#l8.26)
- if (gen->gi_frame != NULL && gen->gi_frame->f_stacktop != NULL) {
/* Generator is paused, so we need to close */[](#l8.30)
Py_TYPE(gen)->tp_del(self);[](#l8.31)
if (self->ob_refcnt > 0)[](#l8.32)
} +return; /* resurrected. :( */[](#l8.33)
- _PyObject_GC_UNTRACK(self);
- Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); PyObject_GC_Del(gen); }
+ static PyObject * gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) {
- /* For compatibility, we check gi_running before f == NULL */ if (gen->gi_running) { PyErr_SetString(PyExc_ValueError, "generator already executing"); return NULL; }
- if (f == NULL) {
/* Only set exception if send() called, not throw() or next() */[](#l8.57)
- if (f == NULL || f->f_stacktop == NULL) {
}/* Only set exception if called from send() */[](#l8.59) if (arg && !exc)[](#l8.60) PyErr_SetNone(PyExc_StopIteration);[](#l8.61) return NULL;[](#l8.62)
- if (f->f_lasti == -1) {
if (arg && arg != Py_None) {[](#l8.67)
PyErr_SetString(PyExc_TypeError,[](#l8.68)
"can't send non-None value to a "[](#l8.69)
"just-started generator");[](#l8.70)
return NULL;[](#l8.71)
}[](#l8.72)
- } else {
/* Push arg onto the frame's value stack */[](#l8.74)
result = arg ? arg : Py_None;[](#l8.75)
Py_INCREF(result);[](#l8.76)
*(f->f_stacktop++) = result;[](#l8.77)
- }
- /* Generators always return to their most recent caller, not
* necessarily their creator. */[](#l8.81)
- Py_XINCREF(tstate->frame);
- assert(f->f_back == NULL);
- f->f_back = tstate->frame;
- /* Don't keep the reference to f_back any longer than necessary. It
* may keep a chain of frames alive or it could create a reference[](#l8.91)
* cycle. */[](#l8.92)
- assert(f->f_back == tstate->frame);
- Py_CLEAR(f->f_back);
- /* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */[](#l8.97)
- if (result && f->f_stacktop == NULL) {
if (result == Py_None) {[](#l8.99)
/* Delay exception instantiation if we can */[](#l8.100)
PyErr_SetNone(PyExc_StopIteration);[](#l8.101)
} else {[](#l8.102)
PyObject *e = PyObject_CallFunctionObjArgs([](#l8.103)
PyExc_StopIteration, result, NULL);[](#l8.104)
if (e != NULL) {[](#l8.105)
PyErr_SetObject(PyExc_StopIteration, e);[](#l8.106)
Py_DECREF(e);[](#l8.107)
}[](#l8.108)
}[](#l8.109)
Py_CLEAR(result);[](#l8.110)
- }
- if (!result || f->f_stacktop == NULL) {
/* generator can't be rerun, so release the frame */[](#l8.114)
/* first clean reference cycle through stored exception traceback */[](#l8.115)
PyObject *t, *v, *tb;[](#l8.116)
t = f->f_exc_type;[](#l8.117)
v = f->f_exc_value;[](#l8.118)
tb = f->f_exc_traceback;[](#l8.119)
f->f_exc_type = NULL;[](#l8.120)
f->f_exc_value = NULL;[](#l8.121)
f->f_exc_traceback = NULL;[](#l8.122)
Py_XDECREF(t);[](#l8.123)
Py_XDECREF(v);[](#l8.124)
Py_XDECREF(tb);[](#l8.125)
gen->gi_frame = NULL;[](#l8.126)
Py_DECREF(f);[](#l8.127)
- }
} PyDoc_STRVAR(send_doc, @@ -83,33 +145,146 @@ PyDoc_STRVAR(close_doc,
- close a subiterator being delegated to by yield-from. */ +static int +gen_close_iter(PyObject *yf) +{
- if (PyGen_CheckExact(yf)) {
retval = gen_close((PyGenObject *)yf, NULL);[](#l8.145)
if (retval == NULL)[](#l8.146)
return -1;[](#l8.147)
- } else {
PyObject *meth = _PyObject_GetAttrId(yf, &PyId_close);[](#l8.149)
if (meth == NULL) {[](#l8.150)
if (!PyErr_ExceptionMatches(PyExc_AttributeError))[](#l8.151)
PyErr_WriteUnraisable(yf);[](#l8.152)
PyErr_Clear();[](#l8.153)
} else {[](#l8.154)
retval = PyObject_CallFunction(meth, "");[](#l8.155)
Py_DECREF(meth);[](#l8.156)
if (retval == NULL)[](#l8.157)
return -1;[](#l8.158)
}[](#l8.159)
- }
- Py_XDECREF(retval);
- return 0;
+} + static PyObject * gen_yf(PyGenObject *gen) {
- if (f && f->f_stacktop) {
PyObject *bytecode = f->f_code->co_code;[](#l8.176)
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);[](#l8.177)
if (code[f->f_lasti + 1] != YIELD_FROM)[](#l8.179)
return NULL;[](#l8.180)
yf = f->f_stacktop[-1];[](#l8.181)
Py_INCREF(yf);[](#l8.182)
- }
} static PyObject * gen_close(PyGenObject *gen, PyObject *args) {
- /* For compatibility, we check gi_running before f == NULL */
- if (gen->gi_running) {
PyErr_SetString(PyExc_ValueError,[](#l8.198)
"generator already executing");[](#l8.199)
- if (yf) {
gen->gi_running = 1;[](#l8.201)
err = gen_close_iter(yf);[](#l8.202)
gen->gi_running = 0;[](#l8.203)
Py_DECREF(yf);[](#l8.204)
- }
- if (err == 0)
PyErr_SetNone(PyExc_GeneratorExit);[](#l8.207)
- retval = gen_send_ex(gen, Py_None, 1);
- if (retval) {
Py_DECREF(retval);[](#l8.210)
PyErr_SetString(PyExc_RuntimeError,[](#l8.211)
}"generator ignored GeneratorExit");[](#l8.212) return NULL;[](#l8.213)
- if (PyErr_ExceptionMatches(PyExc_StopIteration)
|| PyErr_ExceptionMatches(PyExc_GeneratorExit)) {[](#l8.218)
PyErr_Clear(); /* ignore these errors */[](#l8.219)
Py_INCREF(Py_None);[](#l8.220)
return Py_None;[](#l8.221)
- }
- 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 */[](#l8.234)
return;[](#l8.235)
- /* Undo the temporary resurrection; can't use DECREF here, it would
* cause a recursive call.[](#l8.256)
*/[](#l8.257)
- assert(self->ob_refcnt > 0);
- if (--self->ob_refcnt == 0)
return; /* this is the normal path out */[](#l8.260)
- /* close() resurrected it! Make it look like the original Py_DECREF
* never happened.[](#l8.263)
*/[](#l8.264)
- {
Py_ssize_t refcnt = self->ob_refcnt;[](#l8.266)
_Py_NewReference(self);[](#l8.267)
self->ob_refcnt = refcnt;[](#l8.268)
- }
- assert(PyType_IS_GC(Py_TYPE(self)) &&
_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);[](#l8.271)
- /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
* we need to undo that. */[](#l8.274)
- _Py_DEC_REFTOTAL;
- /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
* chain, so no more to do there.[](#l8.277)
* If COUNT_ALLOCS, the original decref bumped tp_frees, and[](#l8.278)
* _Py_NewReference bumped tp_allocs: both of those need to be[](#l8.279)
* undone.[](#l8.280)
*/[](#l8.281)
+#endif } + + PyDoc_STRVAR(throw_doc, "throw(typ[,val[,tb]]) -> raise exception in generator,\n[](#l8.291) return next yielded value or raise StopIteration."); @@ -131,7 +306,7 @@ gen_throw(PyGenObject *gen, PyObject *ar int err; if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) { gen->gi_running = 1;
err = _PyFrame_CloseIterator(yf);[](#l8.297)
err = gen_close_iter(yf);[](#l8.298) gen->gi_running = 0;[](#l8.299) Py_DECREF(yf);[](#l8.300) if (err < 0)[](#l8.301)
@@ -369,6 +544,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_cache / 0, / tp_subclasses / 0, / tp_weaklist */
}; PyObject * @@ -380,7 +556,6 @@ PyGen_New(PyFrameObject *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; @@ -392,5 +567,17 @@ PyGen_New(PyFrameObject *f) int PyGen_NeedsFinalizing(PyGenObject *gen) {
- if (f == NULL || f->f_stacktop == NULL)
return 0; /* no frame or empty blockstack == no finalization */[](#l8.326)