Skip to content

gh-102980: Redirect pdb interact command, add tests and improve docs #111194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 7, 2023
19 changes: 18 additions & 1 deletion Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,27 @@ can be overridden by the local file.

Start an interactive interpreter (using the :mod:`code` module) whose global
namespace contains all the (global and local) names found in the current
scope.
scope. Use ``exit()`` or ``quit()`` to exit the interpreter and return to
the debugger.

.. note::

Because interact creates a new global namespace with the current global
and local namespace for execution, assignment to variables will not
affect the original namespaces.
However, modification to the mutable objects will be reflected in the
original namespaces.

.. versionadded:: 3.2

.. versionadded:: 3.13
``exit()`` and ``quit()`` can be used to exit :pdbcmd:`interact`
command.

.. versionchanged:: 3.13
:pdbcmd:`interact` directs its output to the debugger's
output channel rather than :data:`sys.stderr`.

.. _debugger-aliases:

.. pdbcommand:: alias [name [command]]
Expand Down
17 changes: 14 additions & 3 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ def namespace(self):
)


class _PdbInteractiveConsole(code.InteractiveConsole):
def __init__(self, ns, message):
self._message = message
super().__init__(locals=ns, local_exit=True)

def write(self, data):
self._message(data, end='')


# Interaction prompt line will separate file and call info from code
# text using value of line_prefix string. A newline and arrow may
# be to your liking. You can set it once pdb is imported using the
Expand Down Expand Up @@ -672,8 +681,8 @@ def handle_command_def(self, line):

# interface abstraction functions

def message(self, msg):
print(msg, file=self.stdout)
def message(self, msg, end='\n'):
print(msg, end=end, file=self.stdout)

def error(self, msg):
print('***', msg, file=self.stdout)
Expand Down Expand Up @@ -1786,7 +1795,9 @@ def do_interact(self, arg):
contains all the (global and local) names found in the current scope.
"""
ns = {**self.curframe.f_globals, **self.curframe_locals}
code.interact("*interactive*", local=ns, local_exit=True)
console = _PdbInteractiveConsole(ns, message=self.message)
console.interact(banner="*pdb interact start*",
exitmsg="*exit from pdb interact command*")

def do_alias(self, arg):
"""alias [name [command]]
Expand Down
53 changes: 53 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,59 @@ def test_pdb_where_command():
(Pdb) continue
"""

def test_pdb_interact_command():
"""Test interact command

>>> g = 0
>>> dict_g = {}

>>> def test_function():
... x = 1
... lst_local = []
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()

>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... 'interact',
... 'x',
... 'g',
... 'x = 2',
... 'g = 3',
... 'dict_g["a"] = True',
... 'lst_local.append(x)',
... 'exit()',
... 'p x',
... 'p g',
... 'p dict_g',
... 'p lst_local',
... 'continue',
... ]):
... test_function()
--Return--
> <doctest test.test_pdb.test_pdb_interact_command[2]>(4)test_function()->None
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) interact
*pdb interact start*
... x
1
... g
0
... x = 2
... g = 3
... dict_g["a"] = True
... lst_local.append(x)
... exit()
*exit from pdb interact command*
(Pdb) p x
1
(Pdb) p g
0
(Pdb) p dict_g
{'a': True}
(Pdb) p lst_local
[2]
(Pdb) continue
"""

def test_convenience_variables():
"""Test convenience variables

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Redirect the output of ``interact`` command of :mod:`pdb` to the same channel as the debugger. Add tests and improve docs.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this describing the code change in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. So currently pdb uses code.interact directly for interact command. However, code.interact has a hard-coded output to sys.stderr. pdb supports defining output channel, and we kind of use that for our tests. The user would also expect the output from interact command flushed to the same channel as pdb (stdout instead of stderr by default actually).

This PR subclassed code.InteractiveConsole to overwrite the write method, in order to redirect the output to self.message.