Skip to content

Commit 27fc62c

Browse files
gh-121468: Add current asyncio task as a convenience variable in pdb (#124367)
1 parent c107b15 commit 27fc62c

File tree

5 files changed

+49
-3
lines changed

5 files changed

+49
-3
lines changed

Doc/library/pdb.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -313,16 +313,20 @@ sets a global variable ``$foo`` which you can use in the debugger session. The
313313
less likely to interfere with your program compared to using normal variables
314314
like ``foo = 1``.
315315

316-
There are three preset *convenience variables*:
316+
There are four preset *convenience variables*:
317317

318318
* ``$_frame``: the current frame you are debugging
319319
* ``$_retval``: the return value if the frame is returning
320320
* ``$_exception``: the exception if the frame is raising an exception
321+
* ``$_asynctask``: the asyncio task if pdb stops in an async function
321322

322323
.. versionadded:: 3.12
323324

324325
Added the *convenience variable* feature.
325326

327+
.. versionadded:: 3.14
328+
Added the ``$_asynctask`` convenience variable.
329+
326330
.. index::
327331
pair: .pdbrc; file
328332
triple: debugger; configuration; file

Doc/whatsnew/3.14.rst

+3
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,9 @@ pdb
830830
fill in a 4-space indentation now, instead of inserting a ``\t`` character.
831831
(Contributed by Tian Gao in :gh:`130471`.)
832832

833+
* ``$_asynctask`` is added to access the current asyncio task if applicable.
834+
(Contributed by Tian Gao in :gh:`124367`.)
835+
833836

834837
pickle
835838
------

Lib/pdb.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import codeop
8080
import pprint
8181
import signal
82+
import asyncio
8283
import inspect
8384
import textwrap
8485
import tokenize
@@ -363,6 +364,8 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
363364
self._chained_exceptions = tuple()
364365
self._chained_exception_index = 0
365366

367+
self._current_task = None
368+
366369
def set_trace(self, frame=None, *, commands=None):
367370
Pdb._last_pdb_instance = self
368371
if frame is None:
@@ -405,7 +408,8 @@ def setup(self, f, tb):
405408
tb = tb.tb_next
406409
self.curframe = self.stack[self.curindex][0]
407410
self.set_convenience_variable(self.curframe, '_frame', self.curframe)
408-
411+
if self._current_task:
412+
self.set_convenience_variable(self.curframe, '_asynctask', self._current_task)
409413
self._save_initial_file_mtime(self.curframe)
410414

411415
if self._chained_exceptions:
@@ -616,6 +620,13 @@ def _hold_exceptions(self, exceptions):
616620
self._chained_exceptions = tuple()
617621
self._chained_exception_index = 0
618622

623+
def _get_asyncio_task(self):
624+
try:
625+
task = asyncio.current_task()
626+
except RuntimeError:
627+
task = None
628+
return task
629+
619630
def interaction(self, frame, tb_or_exc):
620631
# Restore the previous signal handler at the Pdb prompt.
621632
if Pdb._previous_sigint_handler:
@@ -626,6 +637,8 @@ def interaction(self, frame, tb_or_exc):
626637
else:
627638
Pdb._previous_sigint_handler = None
628639

640+
self._current_task = self._get_asyncio_task()
641+
629642
_chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
630643
if isinstance(tb_or_exc, BaseException):
631644
assert tb is not None, "main exception must have a traceback"

Lib/test/test_pdb.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from contextlib import ExitStack, redirect_stdout
1717
from io import StringIO
1818
from test import support
19-
from test.support import force_not_colorized, os_helper
19+
from test.support import force_not_colorized, has_socket_support, os_helper
2020
from test.support.import_helper import import_module
2121
from test.support.pty_helper import run_pty, FakeInput
2222
from test.support.script_helper import kill_python
@@ -2059,6 +2059,30 @@ def test_pdb_next_command_for_generator():
20592059
"""
20602060

20612061
if not SKIP_CORO_TESTS:
2062+
if has_socket_support:
2063+
def test_pdb_asynctask():
2064+
"""Testing $_asynctask is accessible in async context
2065+
2066+
>>> import asyncio
2067+
2068+
>>> async def test():
2069+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2070+
2071+
>>> def test_function():
2072+
... asyncio.run(test())
2073+
2074+
>>> with PdbTestInput([ # doctest: +ELLIPSIS
2075+
... '$_asynctask',
2076+
... 'continue',
2077+
... ]):
2078+
... test_function()
2079+
> <doctest test.test_pdb.test_pdb_asynctask[1]>(2)test()
2080+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2081+
(Pdb) $_asynctask
2082+
<Task pending name='Task-1' coro=<test() running at <doctest test.test_pdb.test_pdb_asynctask[1]>:2> ...
2083+
(Pdb) continue
2084+
"""
2085+
20622086
def test_pdb_next_command_for_coroutine():
20632087
"""Testing skip unwinding stack on yield for coroutines for "next" command
20642088
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
``$_asynctask`` is added as a :mod:`pdb` convenience variable to
2+
access the current asyncio task if applicable.

0 commit comments

Comments
 (0)