Kaydet (Commit) 8820c239 authored tarafından Guido van Rossum's avatar Guido van Rossum

Better behavior when stepping over yield[from]. Fixes issue 16596. By Xavier de Gaye.

üst 9c55a58a
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import fnmatch import fnmatch
import sys import sys
import os import os
from inspect import CO_GENERATOR
__all__ = ["BdbQuit", "Bdb", "Breakpoint"] __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
...@@ -75,24 +76,48 @@ class Bdb: ...@@ -75,24 +76,48 @@ class Bdb:
if not (self.stop_here(frame) or self.break_anywhere(frame)): if not (self.stop_here(frame) or self.break_anywhere(frame)):
# No need to trace this function # No need to trace this function
return # None return # None
# Ignore call events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
return self.trace_dispatch
self.user_call(frame, arg) self.user_call(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
return self.trace_dispatch return self.trace_dispatch
def dispatch_return(self, frame, arg): def dispatch_return(self, frame, arg):
if self.stop_here(frame) or frame == self.returnframe: if self.stop_here(frame) or frame == self.returnframe:
# Ignore return events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
return self.trace_dispatch
try: try:
self.frame_returning = frame self.frame_returning = frame
self.user_return(frame, arg) self.user_return(frame, arg)
finally: finally:
self.frame_returning = None self.frame_returning = None
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
# The user issued a 'next' or 'until' command.
if self.stopframe is frame and self.stoplineno != -1:
self._set_stopinfo(None, None)
return self.trace_dispatch return self.trace_dispatch
def dispatch_exception(self, frame, arg): def dispatch_exception(self, frame, arg):
if self.stop_here(frame): if self.stop_here(frame):
# When stepping with next/until/return in a generator frame, skip
# the internal StopIteration exception (with no traceback)
# triggered by a subiterator run with the 'yield from' statement.
if not (frame.f_code.co_flags & CO_GENERATOR
and arg[0] is StopIteration and arg[2] is None):
self.user_exception(frame, arg)
if self.quitting: raise BdbQuit
# Stop at the StopIteration or GeneratorExit exception when the user
# has set stopframe in a generator by issuing a return command, or a
# next/until command at the last statement in the generator before the
# exception.
elif (self.stopframe and frame is not self.stopframe
and self.stopframe.f_code.co_flags & CO_GENERATOR
and arg[0] in (StopIteration, GeneratorExit)):
self.user_exception(frame, arg) self.user_exception(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
return self.trace_dispatch return self.trace_dispatch
# Normally derived classes don't override the following # Normally derived classes don't override the following
...@@ -115,10 +140,8 @@ class Bdb: ...@@ -115,10 +140,8 @@ class Bdb:
if self.stoplineno == -1: if self.stoplineno == -1:
return False return False
return frame.f_lineno >= self.stoplineno return frame.f_lineno >= self.stoplineno
while frame is not None and frame is not self.stopframe: if not self.stopframe:
if frame is self.botframe: return True
return True
frame = frame.f_back
return False return False
def break_here(self, frame): def break_here(self, frame):
...@@ -207,7 +230,10 @@ class Bdb: ...@@ -207,7 +230,10 @@ class Bdb:
def set_return(self, frame): def set_return(self, frame):
"""Stop when returning from the given frame.""" """Stop when returning from the given frame."""
self._set_stopinfo(frame.f_back, frame) if frame.f_code.co_flags & CO_GENERATOR:
self._set_stopinfo(frame, None, -1)
else:
self._set_stopinfo(frame.f_back, frame)
def set_trace(self, frame=None): def set_trace(self, frame=None):
"""Start debugging from `frame`. """Start debugging from `frame`.
......
...@@ -297,8 +297,16 @@ class Pdb(bdb.Bdb, cmd.Cmd): ...@@ -297,8 +297,16 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return return
exc_type, exc_value, exc_traceback = exc_info exc_type, exc_value, exc_traceback = exc_info
frame.f_locals['__exception__'] = exc_type, exc_value frame.f_locals['__exception__'] = exc_type, exc_value
self.message(traceback.format_exception_only(exc_type,
exc_value)[-1].strip()) # An 'Internal StopIteration' exception is an exception debug event
# issued by the interpreter when handling a subgenerator run with
# 'yield from' or a generator controled by a for loop. No exception has
# actually occured in this case. The debugger uses this debug event to
# stop when the debuggee is returning from such generators.
prefix = 'Internal ' if (not exc_traceback
and exc_type is StopIteration) else ''
self.message('%s%s' % (prefix,
traceback.format_exception_only(exc_type, exc_value)[-1].strip()))
self.interaction(frame, exc_traceback) self.interaction(frame, exc_traceback)
# General interaction function # General interaction function
......
This diff is collapsed.
...@@ -1904,6 +1904,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -1904,6 +1904,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
Py_DECREF(v); Py_DECREF(v);
if (retval == NULL) { if (retval == NULL) {
PyObject *val; PyObject *val;
if (tstate->c_tracefunc != NULL
&& PyErr_ExceptionMatches(PyExc_StopIteration))
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, f);
err = _PyGen_FetchStopIterationValue(&val); err = _PyGen_FetchStopIterationValue(&val);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -2654,6 +2657,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2654,6 +2657,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) if (!PyErr_ExceptionMatches(PyExc_StopIteration))
goto error; goto error;
else if (tstate->c_tracefunc != NULL)
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, f);
PyErr_Clear(); PyErr_Clear();
} }
/* iterator ended normally */ /* iterator ended normally */
......
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