gh-133153: Use rlcompleter for pdb's interact command (#133176)

This commit is contained in:
Tian Gao 2025-04-30 15:19:13 -07:00 committed by GitHub
parent 0e21ed7c09
commit 327f5ff9fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 10 deletions

View file

@ -1157,6 +1157,22 @@ def completedefault(self, text, line, begidx, endidx):
state += 1
return matches
@contextmanager
def _enable_rlcompleter(self, ns):
try:
import readline
except ImportError:
yield
return
try:
old_completer = readline.get_completer()
completer = Completer(ns)
readline.set_completer(completer.complete)
yield
finally:
readline.set_completer(old_completer)
# Pdb meta commands, only intended to be used internally by pdb
def _pdbcmd_print_frame_status(self, arg):
@ -2242,9 +2258,10 @@ def do_interact(self, arg):
contains all the (global and local) names found in the current scope.
"""
ns = {**self.curframe.f_globals, **self.curframe.f_locals}
console = _PdbInteractiveConsole(ns, message=self.message)
console.interact(banner="*pdb interact start*",
exitmsg="*exit from pdb interact command*")
with self._enable_rlcompleter(ns):
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]]
@ -2749,14 +2766,18 @@ def _read_reply(self):
self.error(f"Ignoring invalid message from client: {msg}")
def _complete_any(self, text, line, begidx, endidx):
if begidx == 0:
return self.completenames(text, line, begidx, endidx)
cmd = self.parseline(line)[0]
if cmd:
compfunc = getattr(self, "complete_" + cmd, self.completedefault)
else:
# If we're in 'interact' mode, we need to use the default completer
if self._interact_state:
compfunc = self.completedefault
else:
if begidx == 0:
return self.completenames(text, line, begidx, endidx)
cmd = self.parseline(line)[0]
if cmd:
compfunc = getattr(self, "complete_" + cmd, self.completedefault)
else:
compfunc = self.completedefault
return compfunc(text, line, begidx, endidx)
def cmdloop(self, intro=None):

View file

@ -4855,6 +4855,34 @@ def func():
self.assertIn(b'4', output)
self.assertNotIn(b'Error', output)
def test_interact_completion(self):
script = textwrap.dedent("""
value = "speci"
import pdb; pdb.Pdb().set_trace()
""")
# Enter interact mode
input = b"interact\n"
# Should fail to complete 'display' because that's a pdb command
input += b"disp\t\n"
# 'value' should still work
input += b"val\t + 'al'\n"
# Let's define a function to test <tab>
input += b"def f():\n"
input += b"\treturn 42\n"
input += b"\n"
input += b"f() * 2\n"
# Exit interact mode
input += b"exit()\n"
# continue
input += b"c\n"
output = run_pty(script, input)
self.assertIn(b"'disp' is not defined", output)
self.assertIn(b'special', output)
self.assertIn(b'84', output)
def load_tests(loader, tests, pattern):
from test import test_pdb

View file

@ -0,0 +1 @@
Do not complete :mod:`pdb` commands in ``interact`` mode of :mod:`pdb`.