gh-121468: Add current asyncio task as a convenience variable in pdb (#124367)

This commit is contained in:
Tian Gao 2025-03-14 14:46:26 -04:00 committed by GitHub
parent c107b15ab8
commit 27fc62cf4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 49 additions and 3 deletions

View file

@ -313,16 +313,20 @@ sets a global variable ``$foo`` which you can use in the debugger session. The
less likely to interfere with your program compared to using normal variables less likely to interfere with your program compared to using normal variables
like ``foo = 1``. like ``foo = 1``.
There are three preset *convenience variables*: There are four preset *convenience variables*:
* ``$_frame``: the current frame you are debugging * ``$_frame``: the current frame you are debugging
* ``$_retval``: the return value if the frame is returning * ``$_retval``: the return value if the frame is returning
* ``$_exception``: the exception if the frame is raising an exception * ``$_exception``: the exception if the frame is raising an exception
* ``$_asynctask``: the asyncio task if pdb stops in an async function
.. versionadded:: 3.12 .. versionadded:: 3.12
Added the *convenience variable* feature. Added the *convenience variable* feature.
.. versionadded:: 3.14
Added the ``$_asynctask`` convenience variable.
.. index:: .. index::
pair: .pdbrc; file pair: .pdbrc; file
triple: debugger; configuration; file triple: debugger; configuration; file

View file

@ -830,6 +830,9 @@ pdb
fill in a 4-space indentation now, instead of inserting a ``\t`` character. fill in a 4-space indentation now, instead of inserting a ``\t`` character.
(Contributed by Tian Gao in :gh:`130471`.) (Contributed by Tian Gao in :gh:`130471`.)
* ``$_asynctask`` is added to access the current asyncio task if applicable.
(Contributed by Tian Gao in :gh:`124367`.)
pickle pickle
------ ------

View file

@ -79,6 +79,7 @@ import types
import codeop import codeop
import pprint import pprint
import signal import signal
import asyncio
import inspect import inspect
import textwrap import textwrap
import tokenize import tokenize
@ -363,6 +364,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self._chained_exceptions = tuple() self._chained_exceptions = tuple()
self._chained_exception_index = 0 self._chained_exception_index = 0
self._current_task = None
def set_trace(self, frame=None, *, commands=None): def set_trace(self, frame=None, *, commands=None):
Pdb._last_pdb_instance = self Pdb._last_pdb_instance = self
if frame is None: if frame is None:
@ -405,7 +408,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
tb = tb.tb_next tb = tb.tb_next
self.curframe = self.stack[self.curindex][0] self.curframe = self.stack[self.curindex][0]
self.set_convenience_variable(self.curframe, '_frame', self.curframe) self.set_convenience_variable(self.curframe, '_frame', self.curframe)
if self._current_task:
self.set_convenience_variable(self.curframe, '_asynctask', self._current_task)
self._save_initial_file_mtime(self.curframe) self._save_initial_file_mtime(self.curframe)
if self._chained_exceptions: if self._chained_exceptions:
@ -616,6 +620,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self._chained_exceptions = tuple() self._chained_exceptions = tuple()
self._chained_exception_index = 0 self._chained_exception_index = 0
def _get_asyncio_task(self):
try:
task = asyncio.current_task()
except RuntimeError:
task = None
return task
def interaction(self, frame, tb_or_exc): def interaction(self, frame, tb_or_exc):
# Restore the previous signal handler at the Pdb prompt. # Restore the previous signal handler at the Pdb prompt.
if Pdb._previous_sigint_handler: if Pdb._previous_sigint_handler:
@ -626,6 +637,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
else: else:
Pdb._previous_sigint_handler = None Pdb._previous_sigint_handler = None
self._current_task = self._get_asyncio_task()
_chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc) _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
if isinstance(tb_or_exc, BaseException): if isinstance(tb_or_exc, BaseException):
assert tb is not None, "main exception must have a traceback" assert tb is not None, "main exception must have a traceback"

View file

@ -16,7 +16,7 @@ import zipfile
from contextlib import ExitStack, redirect_stdout from contextlib import ExitStack, redirect_stdout
from io import StringIO from io import StringIO
from test import support from test import support
from test.support import force_not_colorized, os_helper from test.support import force_not_colorized, has_socket_support, os_helper
from test.support.import_helper import import_module from test.support.import_helper import import_module
from test.support.pty_helper import run_pty, FakeInput from test.support.pty_helper import run_pty, FakeInput
from test.support.script_helper import kill_python from test.support.script_helper import kill_python
@ -2059,6 +2059,30 @@ def test_pdb_next_command_for_generator():
""" """
if not SKIP_CORO_TESTS: if not SKIP_CORO_TESTS:
if has_socket_support:
def test_pdb_asynctask():
"""Testing $_asynctask is accessible in async context
>>> import asyncio
>>> async def test():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
>>> def test_function():
... asyncio.run(test())
>>> with PdbTestInput([ # doctest: +ELLIPSIS
... '$_asynctask',
... 'continue',
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_asynctask[1]>(2)test()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) $_asynctask
<Task pending name='Task-1' coro=<test() running at <doctest test.test_pdb.test_pdb_asynctask[1]>:2> ...
(Pdb) continue
"""
def test_pdb_next_command_for_coroutine(): def test_pdb_next_command_for_coroutine():
"""Testing skip unwinding stack on yield for coroutines for "next" command """Testing skip unwinding stack on yield for coroutines for "next" command

View file

@ -0,0 +1,2 @@
``$_asynctask`` is added as a :mod:`pdb` convenience variable to
access the current asyncio task if applicable.