Commit d099cfe2 authored by Vitja Makarov's avatar Vitja Makarov

Fix generator's frame object cycle

parent b93b19c7
...@@ -23,6 +23,7 @@ static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_GeneratorObject ...@@ -23,6 +23,7 @@ static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_GeneratorObject
//////////////////// Generator.proto //////////////////// //////////////////// Generator.proto ////////////////////
#define __Pyx_Generator_USED #define __Pyx_Generator_USED
#include <structmember.h> #include <structmember.h>
#include <frameobject.h>
typedef PyObject *(*__pyx_generator_body_t)(PyObject *, PyObject *); typedef PyObject *(*__pyx_generator_body_t)(PyObject *, PyObject *);
...@@ -186,19 +187,43 @@ PyObject *__Pyx_Generator_SendEx(__pyx_GeneratorObject *self, PyObject *value) { ...@@ -186,19 +187,43 @@ PyObject *__Pyx_Generator_SendEx(__pyx_GeneratorObject *self, PyObject *value) {
} }
if (value) if (value) {
__Pyx_ExceptionSwap(&self->exc_type, &self->exc_value, &self->exc_traceback); /* Generators always return to their most recent caller, not
else * necessarily their creator. */
if (self->exc_traceback) {
PyThreadState *tstate = PyThreadState_GET();
PyTracebackObject *tb = (PyTracebackObject *) self->exc_traceback;
PyFrameObject *f = tb->tb_frame;
Py_XINCREF(tstate->frame);
assert(f->f_back == NULL);
f->f_back = tstate->frame;
}
__Pyx_ExceptionSwap(&self->exc_type, &self->exc_value,
&self->exc_traceback);
} else {
__Pyx_Generator_ExceptionClear(self); __Pyx_Generator_ExceptionClear(self);
}
self->is_running = 1; self->is_running = 1;
retval = self->body((PyObject *) self, value); retval = self->body((PyObject *) self, value);
self->is_running = 0; self->is_running = 0;
if (retval) if (retval) {
__Pyx_ExceptionSwap(&self->exc_type, &self->exc_value, &self->exc_traceback); __Pyx_ExceptionSwap(&self->exc_type, &self->exc_value,
else &self->exc_traceback);
/* 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
* cycle. */
if (self->exc_traceback) {
PyTracebackObject *tb = (PyTracebackObject *) self->exc_traceback;
PyFrameObject *f = tb->tb_frame;
Py_CLEAR(f->f_back);
}
} else {
__Pyx_Generator_ExceptionClear(self); __Pyx_Generator_ExceptionClear(self);
}
return retval; return retval;
} }
......
# mode: run
# tags: generator
import sys
def _next(it):
if sys.version_info[0] >= 3:
return next(it)
else:
return it.next()
def test_generator_frame_cycle():
"""
>>> test_generator_frame_cycle()
("I'm done",)
"""
testit = []
def whoo():
try:
yield
except:
yield
finally:
testit.append("I'm done")
g = whoo()
_next(g)
# Frame object cycle
eval('g.throw(ValueError)', {'g': g})
del g
return tuple(testit)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment