From 5c52a717707cf31b276587d84d95e36a8318d6b3 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 23 Sep 2024 11:55:45 -0700 Subject: [PATCH 1/6] Show asyncio information in pdb --- Lib/pdb.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Lib/pdb.py b/Lib/pdb.py index 228de489a9cef1..0a782bed387aa1 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -79,6 +79,7 @@ import codeop import pprint import signal +import asyncio import inspect import textwrap import tokenize @@ -361,6 +362,9 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, self._chained_exceptions = tuple() self._chained_exception_index = 0 + self._running_loop = None + self._current_task = None + def set_trace(self, frame=None): Pdb._last_pdb_instance = self if frame is None: @@ -407,6 +411,8 @@ def setup(self, f, tb): self.curframe_locals = self.curframe.f_locals self.set_convenience_variable(self.curframe, '_frame', self.curframe) + self.set_convenience_variable(self.curframe, '_asynctask', self._current_task) + if self._chained_exceptions: self.set_convenience_variable( self.curframe, @@ -596,6 +602,38 @@ def _hold_exceptions(self, exceptions): self._chained_exceptions = tuple() self._chained_exception_index = 0 + def _get_asyncio_loop_and_task(self): + try: + loop = asyncio.get_event_loop() + task = asyncio.current_task(loop) + except RuntimeError: + loop = task = None + return loop, task + + def _asyncio_task_repr(self, task): + import asyncio.base_futures + + if task.cancelling() and not task.done(): + status = 'cancelling' + else: + info = asyncio.base_futures._future_repr_info(task) + status = info[0] + + coro = task._coro + + if coro is not None: + if hasattr(coro, '__qualname__') and coro.__qualname__: + coro_name = coro.__qualname__ + elif hasattr(coro, '__name__') and coro.__name__: + coro_name = coro.__name__ + else: + # Stop masking Cython bugs, expose them in a friendly way. + coro_name = f'<{type(coro).__name__} without __name__>' + else: + coro_name = 'unknown' + + return f"{task.get_name()}: <{coro_name} {status}>" + def interaction(self, frame, tb_or_exc): # Restore the previous signal handler at the Pdb prompt. if Pdb._previous_sigint_handler: @@ -606,6 +644,8 @@ def interaction(self, frame, tb_or_exc): else: Pdb._previous_sigint_handler = None + self._running_loop, self._current_task = self._get_asyncio_loop_and_task() + _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc) if isinstance(tb_or_exc, BaseException): assert tb is not None, "main exception must have a traceback" @@ -995,6 +1035,8 @@ def completedefault(self, text, line, begidx, endidx): # Pdb meta commands, only intended to be used internally by pdb def _pdbcmd_print_frame_status(self, arg): + if self._current_task: + self.message(self._asyncio_task_repr(self._current_task)) self.print_stack_trace(0) self._show_display() From ae16c8bdc6fbe507c3d54c442fea2c047bbea0b5 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 11 Mar 2025 17:51:40 -0400 Subject: [PATCH 2/6] Remove the print --- Doc/library/pdb.rst | 6 +++++- Doc/whatsnew/3.14.rst | 3 +++ Lib/pdb.py | 41 +++++++---------------------------------- 3 files changed, 15 insertions(+), 35 deletions(-) diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index b31625e6b0082f..44848b26200de5 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -304,16 +304,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 like ``foo = 1``. -There are three preset *convenience variables*: +There are four preset *convenience variables*: * ``$_frame``: the current frame you are debugging * ``$_retval``: the return value if the frame is returning * ``$_exception``: the exception if the frame is raising an exception +* ``$_asynctask``: the asyncio task if pdb stops in an async function .. versionadded:: 3.12 Added the *convenience variable* feature. +.. versionadded:: 3.14 + Added the ``$_asynctask`` convenience variable. + .. index:: pair: .pdbrc; file triple: debugger; configuration; file diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2402fb23c86b85..44148a4d3b55f4 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -811,6 +811,9 @@ pdb fill in a 4-space indentation now, instead of inserting a ``\t`` character. (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 ------ diff --git a/Lib/pdb.py b/Lib/pdb.py index 5a1d5d2b99abe6..30238a8b2d61ba 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -364,7 +364,6 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, self._chained_exceptions = tuple() self._chained_exception_index = 0 - self._running_loop = None self._current_task = None def set_trace(self, frame=None, *, commands=None): @@ -409,7 +408,8 @@ def setup(self, f, tb): tb = tb.tb_next self.curframe = self.stack[self.curindex][0] self.set_convenience_variable(self.curframe, '_frame', self.curframe) - self.set_convenience_variable(self.curframe, '_asynctask', self._current_task) + if self._current_task: + self.set_convenience_variable(self.curframe, '_asynctask', self._current_task) self._save_initial_file_mtime(self.curframe) if self._chained_exceptions: @@ -620,37 +620,12 @@ def _hold_exceptions(self, exceptions): self._chained_exceptions = tuple() self._chained_exception_index = 0 - def _get_asyncio_loop_and_task(self): + def _get_asyncio_task(self): try: - loop = asyncio.get_event_loop() - task = asyncio.current_task(loop) + task = asyncio.current_task() except RuntimeError: - loop = task = None - return loop, task - - def _asyncio_task_repr(self, task): - import asyncio.base_futures - - if task.cancelling() and not task.done(): - status = 'cancelling' - else: - info = asyncio.base_futures._future_repr_info(task) - status = info[0] - - coro = task._coro - - if coro is not None: - if hasattr(coro, '__qualname__') and coro.__qualname__: - coro_name = coro.__qualname__ - elif hasattr(coro, '__name__') and coro.__name__: - coro_name = coro.__name__ - else: - # Stop masking Cython bugs, expose them in a friendly way. - coro_name = f'<{type(coro).__name__} without __name__>' - else: - coro_name = 'unknown' - - return f"{task.get_name()}: <{coro_name} {status}>" + task = None + return task def interaction(self, frame, tb_or_exc): # Restore the previous signal handler at the Pdb prompt. @@ -662,7 +637,7 @@ def interaction(self, frame, tb_or_exc): else: Pdb._previous_sigint_handler = None - self._running_loop, self._current_task = self._get_asyncio_loop_and_task() + self._current_task = self._get_asyncio_task() _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc) if isinstance(tb_or_exc, BaseException): @@ -1067,8 +1042,6 @@ def completedefault(self, text, line, begidx, endidx): # Pdb meta commands, only intended to be used internally by pdb def _pdbcmd_print_frame_status(self, arg): - if self._current_task: - self.message(self._asyncio_task_repr(self._current_task)) self.print_stack_trace(0) self._validate_file_mtime() self._show_display() From 490a6d9e3ca163c9ed6d1ff2f757676476cd6d35 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:52:38 +0000 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst b/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst new file mode 100644 index 00000000000000..b696a05a2e0814 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst @@ -0,0 +1 @@ +``$_asynctask`` is added to access the current asyncio task if applicable. From 297a6841c9d11e04284e4a15cf500e96c0c4ee8b Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 11 Mar 2025 17:58:05 -0400 Subject: [PATCH 4/6] Add test --- Lib/test/test_pdb.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 94332f1daee479..36903f6d517cd4 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2059,6 +2059,29 @@ def test_pdb_next_command_for_generator(): """ if not SKIP_CORO_TESTS: + 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() + > (2)test() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) $_asynctask + :2> ... + (Pdb) continue + """ + def test_pdb_next_command_for_coroutine(): """Testing skip unwinding stack on yield for coroutines for "next" command From dcb96df5baba1804dd3f0d485915729e66032e42 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 11 Mar 2025 18:18:32 -0400 Subject: [PATCH 5/6] Skip asyncio test on wasi --- Lib/test/test_pdb.py | 47 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 36903f6d517cd4..d5956b6b989604 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -16,7 +16,7 @@ from contextlib import ExitStack, redirect_stdout from io import StringIO 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.pty_helper import run_pty, FakeInput from test.support.script_helper import kill_python @@ -2059,28 +2059,29 @@ def test_pdb_next_command_for_generator(): """ if not SKIP_CORO_TESTS: - 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() - > (2)test() - -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() - (Pdb) $_asynctask - :2> ... - (Pdb) continue - """ + 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() + > (2)test() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) $_asynctask + :2> ... + (Pdb) continue + """ def test_pdb_next_command_for_coroutine(): """Testing skip unwinding stack on yield for coroutines for "next" command From 9c66d9b92327ca3ed42b96e41dfe1681a125febb Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 14 Mar 2025 11:36:04 -0400 Subject: [PATCH 6/6] Update 2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst --- .../Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst b/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst index b696a05a2e0814..833f156d261a06 100644 --- a/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst +++ b/Misc/NEWS.d/next/Library/2025-03-11-21-52-33.gh-issue-121468.WsEP02.rst @@ -1 +1,2 @@ -``$_asynctask`` is added to access the current asyncio task if applicable. +``$_asynctask`` is added as a :mod:`pdb` convenience variable to +access the current asyncio task if applicable.