Skip to content

Commit 327f5ff

Browse files
gh-133153: Use rlcompleter for pdb's interact command (#133176)
1 parent 0e21ed7 commit 327f5ff

File tree

3 files changed

+60
-10
lines changed

3 files changed

+60
-10
lines changed

Lib/pdb.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,22 @@ def completedefault(self, text, line, begidx, endidx):
11571157
state += 1
11581158
return matches
11591159

1160+
@contextmanager
1161+
def _enable_rlcompleter(self, ns):
1162+
try:
1163+
import readline
1164+
except ImportError:
1165+
yield
1166+
return
1167+
1168+
try:
1169+
old_completer = readline.get_completer()
1170+
completer = Completer(ns)
1171+
readline.set_completer(completer.complete)
1172+
yield
1173+
finally:
1174+
readline.set_completer(old_completer)
1175+
11601176
# Pdb meta commands, only intended to be used internally by pdb
11611177

11621178
def _pdbcmd_print_frame_status(self, arg):
@@ -2242,9 +2258,10 @@ def do_interact(self, arg):
22422258
contains all the (global and local) names found in the current scope.
22432259
"""
22442260
ns = {**self.curframe.f_globals, **self.curframe.f_locals}
2245-
console = _PdbInteractiveConsole(ns, message=self.message)
2246-
console.interact(banner="*pdb interact start*",
2247-
exitmsg="*exit from pdb interact command*")
2261+
with self._enable_rlcompleter(ns):
2262+
console = _PdbInteractiveConsole(ns, message=self.message)
2263+
console.interact(banner="*pdb interact start*",
2264+
exitmsg="*exit from pdb interact command*")
22482265

22492266
def do_alias(self, arg):
22502267
"""alias [name [command]]
@@ -2749,14 +2766,18 @@ def _read_reply(self):
27492766
self.error(f"Ignoring invalid message from client: {msg}")
27502767

27512768
def _complete_any(self, text, line, begidx, endidx):
2752-
if begidx == 0:
2753-
return self.completenames(text, line, begidx, endidx)
2754-
2755-
cmd = self.parseline(line)[0]
2756-
if cmd:
2757-
compfunc = getattr(self, "complete_" + cmd, self.completedefault)
2758-
else:
2769+
# If we're in 'interact' mode, we need to use the default completer
2770+
if self._interact_state:
27592771
compfunc = self.completedefault
2772+
else:
2773+
if begidx == 0:
2774+
return self.completenames(text, line, begidx, endidx)
2775+
2776+
cmd = self.parseline(line)[0]
2777+
if cmd:
2778+
compfunc = getattr(self, "complete_" + cmd, self.completedefault)
2779+
else:
2780+
compfunc = self.completedefault
27602781
return compfunc(text, line, begidx, endidx)
27612782

27622783
def cmdloop(self, intro=None):

Lib/test/test_pdb.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4855,6 +4855,34 @@ def func():
48554855
self.assertIn(b'4', output)
48564856
self.assertNotIn(b'Error', output)
48574857

4858+
def test_interact_completion(self):
4859+
script = textwrap.dedent("""
4860+
value = "speci"
4861+
import pdb; pdb.Pdb().set_trace()
4862+
""")
4863+
4864+
# Enter interact mode
4865+
input = b"interact\n"
4866+
# Should fail to complete 'display' because that's a pdb command
4867+
input += b"disp\t\n"
4868+
# 'value' should still work
4869+
input += b"val\t + 'al'\n"
4870+
# Let's define a function to test <tab>
4871+
input += b"def f():\n"
4872+
input += b"\treturn 42\n"
4873+
input += b"\n"
4874+
input += b"f() * 2\n"
4875+
# Exit interact mode
4876+
input += b"exit()\n"
4877+
# continue
4878+
input += b"c\n"
4879+
4880+
output = run_pty(script, input)
4881+
4882+
self.assertIn(b"'disp' is not defined", output)
4883+
self.assertIn(b'special', output)
4884+
self.assertIn(b'84', output)
4885+
48584886

48594887
def load_tests(loader, tests, pattern):
48604888
from test import test_pdb
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do not complete :mod:`pdb` commands in ``interact`` mode of :mod:`pdb`.

0 commit comments

Comments
 (0)