diff options
| author | Amaury Forgeot d'Arc <amauryfa@gmail.com> | 2007-11-13 22:43:05 (GMT) | 
|---|---|---|
| committer | Amaury Forgeot d'Arc <amauryfa@gmail.com> | 2007-11-13 22:43:05 (GMT) | 
| commit | c572dc3752871a48127db1fb3c8bc20889d2be34 (patch) | |
| tree | 1dd5cb18cea5fcaa1a91a53413dfe3a3ea918a3e /Python/ceval.c | |
| parent | f5ccd459d767464d7033f6ef7ba73a53e3eae268 (diff) | |
| download | cpython-c572dc3752871a48127db1fb3c8bc20889d2be34.zip cpython-c572dc3752871a48127db1fb3c8bc20889d2be34.tar.gz cpython-c572dc3752871a48127db1fb3c8bc20889d2be34.tar.bz2  | |
Backport for issue1265 (pdb bug with "with" statement).
When an unfinished generator-iterator is garbage collected, PyEval_EvalFrameEx
is called with a GeneratorExit exception set.  This leads to funny results
if the sys.settrace function itself makes use of generators.
A visible effect is that the settrace function is reset to None.
Another is that the eventual "finally" block of the generator is not called.
It is necessary to save/restore the exception around the call to the trace
function.
This happens a lot with py3k: isinstance() of an ABCMeta instance runs
    def __instancecheck__(cls, instance):
        """Override for isinstance(instance, cls)."""
        return any(cls.__subclasscheck__(c)
                   for c in {instance.__class__, type(instance)})
which lets an opened generator expression each time it returns True.
And the problem can be reproduced in 2.5 with pure python code.
Diffstat (limited to 'Python/ceval.c')
| -rw-r--r-- | Python/ceval.c | 19 | 
1 files changed, 12 insertions, 7 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index 9dddd2f..4f6b731 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -105,7 +105,7 @@ static int prtrace(PyObject *, char *);  #endif  static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,  		      int, PyObject *); -static void call_trace_protected(Py_tracefunc, PyObject *, +static int call_trace_protected(Py_tracefunc, PyObject *,  				 PyFrameObject *, int, PyObject *);  static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *);  static int maybe_call_line_trace(Py_tracefunc, PyObject *, @@ -710,8 +710,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)  			   an argument which depends on the situation.  			   The global trace function is also called  			   whenever an exception is detected. */ -			if (call_trace(tstate->c_tracefunc, tstate->c_traceobj, -				       f, PyTrace_CALL, Py_None)) { +			if (call_trace_protected(tstate->c_tracefunc,  +						 tstate->c_traceobj, +						 f, PyTrace_CALL, Py_None)) {  				/* Trace function raised an error */  				goto exit_eval_frame;  			} @@ -719,9 +720,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)  		if (tstate->c_profilefunc != NULL) {  			/* Similar for c_profilefunc, except it needn't  			   return itself and isn't called for "line" events */ -			if (call_trace(tstate->c_profilefunc, -				       tstate->c_profileobj, -				       f, PyTrace_CALL, Py_None)) { +			if (call_trace_protected(tstate->c_profilefunc, +						 tstate->c_profileobj, +						 f, PyTrace_CALL, Py_None)) {  				/* Profile function raised an error */  				goto exit_eval_frame;  			} @@ -3192,7 +3193,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self, PyFrameObject *f)  	}  } -static void +static int  call_trace_protected(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,  		     int what, PyObject *arg)  { @@ -3201,11 +3202,15 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,  	PyErr_Fetch(&type, &value, &traceback);  	err = call_trace(func, obj, frame, what, arg);  	if (err == 0) +	{  		PyErr_Restore(type, value, traceback); +		return 0; +	}  	else {  		Py_XDECREF(type);  		Py_XDECREF(value);  		Py_XDECREF(traceback); +		return -1;  	}  }  | 
