Kaydet (Commit) 46877024 authored tarafından Pablo Galindo's avatar Pablo Galindo Kaydeden (comit) Andrew Svetlov

bpo-32650: Add native coroutine support to bdb when stepping over line (GH-5400)

üst 7c99e931
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import fnmatch import fnmatch
import sys import sys
import os import os
from inspect import CO_GENERATOR from inspect import CO_GENERATOR, CO_COROUTINE
__all__ = ["BdbQuit", "Bdb", "Breakpoint"] __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
...@@ -127,7 +127,7 @@ class Bdb: ...@@ -127,7 +127,7 @@ class Bdb:
# No need to trace this function # No need to trace this function
return # None return # None
# Ignore call events in generator except when stepping. # 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 return self.trace_dispatch
self.user_call(frame, arg) self.user_call(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
...@@ -142,7 +142,7 @@ class Bdb: ...@@ -142,7 +142,7 @@ class Bdb:
""" """
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. # 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 return self.trace_dispatch
try: try:
self.frame_returning = frame self.frame_returning = frame
...@@ -166,7 +166,7 @@ class Bdb: ...@@ -166,7 +166,7 @@ class Bdb:
# When stepping with next/until/return in a generator frame, skip # When stepping with next/until/return in a generator frame, skip
# the internal StopIteration exception (with no traceback) # the internal StopIteration exception (with no traceback)
# triggered by a subiterator run with the 'yield from' statement. # 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): and arg[0] is StopIteration and arg[2] is None):
self.user_exception(frame, arg) self.user_exception(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
...@@ -175,7 +175,7 @@ class Bdb: ...@@ -175,7 +175,7 @@ class Bdb:
# next/until command at the last statement in the generator before the # next/until command at the last statement in the generator before the
# exception. # exception.
elif (self.stopframe and frame is not self.stopframe 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)): 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
...@@ -309,7 +309,7 @@ class Bdb: ...@@ -309,7 +309,7 @@ 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."""
if frame.f_code.co_flags & CO_GENERATOR: if frame.f_code.co_flags & (CO_GENERATOR | CO_COROUTINE):
self._set_stopinfo(frame, None, -1) self._set_stopinfo(frame, None, -1)
else: else:
self._set_stopinfo(frame.f_back, frame) self._set_stopinfo(frame.f_back, frame)
......
...@@ -727,6 +727,61 @@ def test_pdb_next_command_for_generator(): ...@@ -727,6 +727,61 @@ def test_pdb_next_command_for_generator():
finished 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(): def test_pdb_return_command_for_generator():
"""Testing no unwindng stack on yield for generators """Testing no unwindng stack on yield for generators
for "return" command for "return" command
......
Pdb and other debuggers dependent on bdb.py will correctly step over (next
command) native coroutines. Patch by Pablo Galindo.
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