[Python-checkins] r58964 - in python/branches/release25-maint: Lib/test/test_trace.py Misc/NEWS Python/ceval.c

amaury.forgeotdarc python-checkins at python.org
Tue Nov 13 23:43:06 CET 2007


Author: amaury.forgeotdarc
Date: Tue Nov 13 23:43:05 2007
New Revision: 58964
Modified:
 python/branches/release25-maint/Lib/test/test_trace.py
 python/branches/release25-maint/Misc/NEWS
 python/branches/release25-maint/Python/ceval.c
Log:
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.
Modified: python/branches/release25-maint/Lib/test/test_trace.py
==============================================================================
--- python/branches/release25-maint/Lib/test/test_trace.py	(original)
+++ python/branches/release25-maint/Lib/test/test_trace.py	Tue Nov 13 23:43:05 2007
@@ -204,12 +204,44 @@
 (6, 'line'),
 (6, 'return')]
 
+def generator_function():
+ try:
+ yield True
+ "continued"
+ finally:
+ "finally"
+def generator_example():
+ # any() will leave the generator before its end
+ x = any(generator_function())
+
+ # the following lines were not traced
+ for x in range(10):
+ y = x
+
+generator_example.events = ([(0, 'call'),
+ (2, 'line'),
+ (-6, 'call'),
+ (-5, 'line'),
+ (-4, 'line'),
+ (-4, 'return'),
+ (-4, 'call'),
+ (-4, 'exception'),
+ (-1, 'line'),
+ (-1, 'return')] +
+ [(5, 'line'), (6, 'line')] * 10 +
+ [(5, 'line'), (5, 'return')])
+
+
 class Tracer:
 def __init__(self):
 self.events = []
 def trace(self, frame, event, arg):
 self.events.append((frame.f_lineno, event))
 return self.trace
+ def traceWithGenexp(self, frame, event, arg):
+ (o for o in [1])
+ self.events.append((frame.f_lineno, event))
+ return self.trace
 
 class TraceTestCase(unittest.TestCase):
 def compare_events(self, line_offset, events, expected_events):
@@ -217,8 +249,8 @@
 if events != expected_events:
 self.fail(
 "events did not match expectation:\n" +
- "\n".join(difflib.ndiff(map(str, expected_events),
- map(str, events))))
+ "\n".join(difflib.ndiff([str(x) for x in expected_events],
+ [str(x) for x in events])))
 
 
 def run_test(self, func):
@@ -262,6 +294,19 @@
 def test_12_tighterloop(self):
 self.run_test(tighterloop_example)
 
+ def test_13_genexp(self):
+ self.run_test(generator_example)
+ # issue1265: if the trace function contains a generator,
+ # and if the traced function contains another generator
+ # that is not completely exhausted, the trace stopped.
+ # Worse: the 'finally' clause was not invoked.
+ tracer = Tracer()
+ sys.settrace(tracer.traceWithGenexp)
+ generator_example()
+ sys.settrace(None)
+ self.compare_events(generator_example.func_code.co_firstlineno,
+ tracer.events, generator_example.events)
+
 class RaisingTraceFuncTestCase(unittest.TestCase):
 def trace(self, frame, event, arg):
 """A trace function that raises an exception in response to a
Modified: python/branches/release25-maint/Misc/NEWS
==============================================================================
--- python/branches/release25-maint/Misc/NEWS	(original)
+++ python/branches/release25-maint/Misc/NEWS	Tue Nov 13 23:43:05 2007
@@ -12,6 +12,10 @@
 Core and builtins
 -----------------
 
+- Issue #1265: Fix a problem with sys.settrace, if the tracing function uses a
+ generator expression when at the same time the executed code is closing a
+ paused generator.
+
 - Issue 1704621: Fix segfaults in list_repeat() and list_inplace_repeat().
 
 - Issue #1147: Generators were not raising a DeprecationWarning when a string
Modified: python/branches/release25-maint/Python/ceval.c
==============================================================================
--- python/branches/release25-maint/Python/ceval.c	(original)
+++ python/branches/release25-maint/Python/ceval.c	Tue Nov 13 23:43:05 2007
@@ -105,7 +105,7 @@
 #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 @@
 			 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 @@
 		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 @@
 	}
 }
 
-static void
+static int
 call_trace_protected(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
 		 int what, PyObject *arg)
 {
@@ -3201,11 +3202,15 @@
 	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;
 	}
 }
 


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /