Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Lib/bdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import fnmatch
import sys
import os
from inspect import CO_GENERATOR
from inspect import CO_GENERATOR, CO_COROUTINE

__all__ = ["BdbQuit", "Bdb", "Breakpoint"]

Expand Down Expand Up @@ -77,7 +77,7 @@ def dispatch_call(self, frame, arg):
# No need to trace this function
return # None
# Ignore call events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
if self.stopframe and frame.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE):
return self.trace_dispatch
self.user_call(frame, arg)
if self.quitting: raise BdbQuit
Expand All @@ -86,7 +86,7 @@ def dispatch_call(self, frame, arg):
def dispatch_return(self, frame, arg):
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:
if self.stopframe and frame.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE):
return self.trace_dispatch
try:
self.frame_returning = frame
Expand All @@ -104,7 +104,7 @@ def dispatch_exception(self, frame, arg):
# 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
if not (frame.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE)
and arg[0] is StopIteration and arg[2] is None):
self.user_exception(frame, arg)
if self.quitting: raise BdbQuit
Expand All @@ -113,7 +113,7 @@ def dispatch_exception(self, frame, arg):
# 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 self.stopframe.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE)
and arg[0] in (StopIteration, GeneratorExit)):
self.user_exception(frame, arg)
if self.quitting: raise BdbQuit
Expand Down Expand Up @@ -230,7 +230,7 @@ def set_next(self, frame):

def set_return(self, frame):
"""Stop when returning from the given frame."""
if frame.f_code.co_flags & CO_GENERATOR:
if frame.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE):
self._set_stopinfo(frame, None, -1)
else:
self._set_stopinfo(frame.f_back, frame)
Expand Down
55 changes: 55 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,61 @@ def test_pdb_next_command_for_generator():
finished
"""

def test_pdb_next_command_for_coroutine():
"""Testing skip unwindng stack on yield for coroutines for "next" command

>>> import asyncio

>>> async def test_coro():
... await asyncio.sleep(0)
... await asyncio.sleep(0)
... await asyncio.sleep(0)

>>> async def test_main():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... await test_coro()

>>> def test_function():
... loop = asyncio.get_event_loop()
... loop.run_until_complete(test_main())
... loop.close()
... print("finished")

>>> with PdbTestInput(['step',
... 'step',
... 'next',
... 'next',
... 'next',
... 'step',
... 'continue']):
... test_function()
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[2]>(3)test_main()
-> await test_coro()
(Pdb) step
--Call--
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[1]>(1)test_coro()
-> async def test_coro():
(Pdb) step
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[1]>(2)test_coro()
-> await asyncio.sleep(0)
(Pdb) next
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[1]>(3)test_coro()
-> await asyncio.sleep(0)
(Pdb) next
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[1]>(4)test_coro()
-> await asyncio.sleep(0)
(Pdb) next
Internal StopIteration
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[2]>(3)test_main()
-> await test_coro()
(Pdb) step
--Return--
> <doctest test.test_pdb.test_pdb_next_command_for_coroutine[2]>(3)test_main()->None
-> await test_coro()
(Pdb) continue
finished
"""

def test_pdb_return_command_for_generator():
"""Testing no unwindng stack on yield for generators
for "return" command
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pdb and other debuggers dependent on bdb.py will correctly step over (next
command) native coroutines. Patch by Pablo Galindo.