Skip to content

Commit 18aa905

Browse files
gh-141295: Persist pdb command history with PyREPL
1 parent da6629c commit 18aa905

7 files changed

Lines changed: 67 additions & 1 deletion

File tree

‎Doc/library/pdb.rst‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ The debugger's prompt is ``(Pdb)``, which is the indicator that you are in debug
7575
arguments of the ``p`` command.
7676

7777

78+
.. versionadded:: 3.16
79+
When using PyREPL, debugger commands are saved in ``.python_history.pdb`` in the
80+
user's home directory and loaded for subsequent sessions. If
81+
:envvar:`PYTHON_HISTORY` is set, its value with ``.pdb`` appended is used
82+
instead. Set :envvar:`PYTHON_HISTORY_PDB` to use a different history file.
83+
84+
7885
.. _pdb-cli:
7986

8087
Command-line interface

‎Doc/using/cmdline.rst‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,17 @@ conflict.
13711371

13721372
.. versionadded:: 3.13
13731373

1374+
.. envvar:: PYTHON_HISTORY_PDB
1375+
1376+
Set the location of the command history file used by :mod:`pdb` with
1377+
PyREPL. By default, it is the value of :envvar:`PYTHON_HISTORY` with ``.pdb``
1378+
appended, or ``.python_history.pdb`` in the user's home directory if
1379+
:envvar:`PYTHON_HISTORY` is unset or empty.
1380+
An empty value also selects the default. This variable is ignored with
1381+
:option:`-E` or :option:`-I`.
1382+
1383+
.. versionadded:: 3.16
1384+
13741385
.. envvar:: PYTHON_GIL
13751386

13761387
If this variable is set to ``1``, the global interpreter lock (GIL) will be

‎Lib/pdb.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
import typing
8787
import inspect
8888
import weakref
89+
import warnings
8990
import builtins
9091
import tempfile
9192
import textwrap
@@ -404,6 +405,22 @@ def __init__(self, pdb_instance, stdin, stdout, prompt):
404405
)
405406
)
406407
self.readline_wrapper.get_reader().gen_colors = self.gen_colors
408+
self.history_file = self._get_history_file()
409+
self.readline_wrapper.set_history_length(0)
410+
try:
411+
self.readline_wrapper.read_history_file(self.history_file)
412+
except OSError:
413+
pass
414+
415+
@staticmethod
416+
def _get_history_file():
417+
"""Return the pdb history path, defaulting to Python's history plus .pdb."""
418+
if not sys.flags.ignore_environment:
419+
history = os.environ.get("PYTHON_HISTORY_PDB")
420+
if history:
421+
return history
422+
from site import gethistoryfile
423+
return gethistoryfile() + '.pdb'
407424

408425
def readline(self):
409426

@@ -430,6 +447,10 @@ def more_lines(text):
430447
'... ' + ' ' * (len(self.prompt) - 4)
431448
) + '\n'
432449
)
450+
try:
451+
self.readline_wrapper.append_history_file(self.history_file)
452+
except (FileNotFoundError, PermissionError, OSError) as e:
453+
warnings.warn(f"failed to open the history file for writing: {e}")
433454
return multiline
434455
except EOFError:
435456
return 'EOF'

‎Lib/test/test_pdb.py‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5296,9 +5296,27 @@ def test_interact_completion(self):
52965296

52975297
@unittest.skipIf(not pdb._pyrepl_available(), "pyrepl is not available")
52985298
class PdbTestReadlinePyREPL(PdbTestReadline):
5299+
@classmethod
5300+
def setUpClass(cls):
5301+
super().setUpClass()
5302+
directory = cls.enterClassContext(os_helper.temp_dir())
5303+
cls.history_file = os.path.join(directory, 'history')
5304+
52995305
def _run_pty(self, script, input):
53005306
# Override the env to make sure pyrepl is used in this test class
5301-
return super()._run_pty(script, input, env={**os.environ})
5307+
env = os.environ | {'PYTHON_HISTORY_PDB': self.history_file}
5308+
return super()._run_pty(script, input, env=env)
5309+
5310+
def test_history(self):
5311+
script = "import pdb; pdb.Pdb().set_trace()"
5312+
os_helper.unlink(self.history_file)
5313+
self._run_pty(script, b'p 6 * 7\ncontinue\n')
5314+
with open(self.history_file, encoding='utf-8') as file:
5315+
self.assertEqual(file.read().splitlines(), ['p 6 * 7', 'continue'])
5316+
5317+
# Recall 'continue', then 'p 6 * 7' in a new debugger process.
5318+
output = self._run_pty(script, b'\x1bOA\x1bOA\ncontinue\n')
5319+
self.assertIn('42', output)
53025320

53035321
def test_pyrepl_used(self):
53045322
script = textwrap.dedent("""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Save and restore :mod:`pdb` command history when using PyREPL. The new
2+
:envvar:`PYTHON_HISTORY_PDB` environment variable overrides the default
3+
history path: :envvar:`PYTHON_HISTORY` with ``.pdb`` appended, or
4+
``.python_history.pdb`` in the user's home directory.

‎Misc/python.man‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@ on. Setting it to 0 forces the GIL off. Only available in builds configured
568568
with \fB\-\-disable\-gil\fP.
569569
.IP
570570
This is equivalent to the \fB\-X gil\fR option.
571+
.IP PYTHON_HISTORY_PDB
572+
Set the location of the pdb command history file when using PyREPL
573+
(by default, PYTHON_HISTORY with .pdb appended, or .python_history.pdb in
574+
the user's home directory).
571575
.IP PYTHON_HISTORY
572576
This environment variable can be used to set the location of a history file
573577
(on Unix, it is \fI~/.python_history\fP by default).

‎Python/initconfig.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ static const char usage_envvars[] =
525525
" set to an integer in the range [0,4294967295] to get hash\n"
526526
" values with a predictable seed.\n"
527527
"#E{PYTHON_HISTORY} : the location of a .python_history file.\n"
528+
"#E{PYTHON_HISTORY_PDB}: the location of a .python_history.pdb file used by PyREPL.\n"
528529
"#E{PYTHONHOME} : alternate <prefix> directory (or <prefix>#D<exec_prefix>).\n"
529530
" The default module search path uses #H.\n"
530531
"#E{PYTHONIOENCODING}: encoding[:errors] used for stdin/stdout/stderr\n"

0 commit comments

Comments
 (0)