mirror of https://github.com/python/cpython
gh-112948: Make pdb completion similar to repl completion (#112950)
This commit is contained in:
parent
9db2a8f914
commit
01e7405da4
43
Lib/pdb.py
43
Lib/pdb.py
|
@ -87,6 +87,7 @@ import traceback
|
|||
import linecache
|
||||
|
||||
from contextlib import contextmanager
|
||||
from rlcompleter import Completer
|
||||
from typing import Union
|
||||
|
||||
|
||||
|
@ -573,20 +574,14 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
self.message(repr(obj))
|
||||
|
||||
@contextmanager
|
||||
def _disable_tab_completion(self):
|
||||
if self.use_rawinput and self.completekey == 'tab':
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
yield
|
||||
return
|
||||
try:
|
||||
readline.parse_and_bind('tab: self-insert')
|
||||
yield
|
||||
finally:
|
||||
readline.parse_and_bind('tab: complete')
|
||||
else:
|
||||
def _disable_command_completion(self):
|
||||
completenames = self.completenames
|
||||
try:
|
||||
self.completenames = self.completedefault
|
||||
yield
|
||||
finally:
|
||||
self.completenames = completenames
|
||||
return
|
||||
|
||||
def default(self, line):
|
||||
if line[:1] == '!': line = line[1:].strip()
|
||||
|
@ -595,7 +590,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
try:
|
||||
if (code := codeop.compile_command(line + '\n', '<stdin>', 'single')) is None:
|
||||
# Multi-line mode
|
||||
with self._disable_tab_completion():
|
||||
with self._disable_command_completion():
|
||||
buffer = line
|
||||
continue_prompt = "... "
|
||||
while (code := codeop.compile_command(buffer, '<stdin>', 'single')) is None:
|
||||
|
@ -771,7 +766,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
if commands:
|
||||
return commands
|
||||
else:
|
||||
return self._complete_expression(text, line, begidx, endidx)
|
||||
expressions = self._complete_expression(text, line, begidx, endidx)
|
||||
if expressions:
|
||||
return expressions
|
||||
return self.completedefault(text, line, begidx, endidx)
|
||||
|
||||
def _complete_location(self, text, line, begidx, endidx):
|
||||
# Complete a file/module/function location for break/tbreak/clear.
|
||||
|
@ -828,6 +826,21 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
# Complete a simple name.
|
||||
return [n for n in ns.keys() if n.startswith(text)]
|
||||
|
||||
def completedefault(self, text, line, begidx, endidx):
|
||||
if text.startswith("$"):
|
||||
# Complete convenience variables
|
||||
conv_vars = self.curframe.f_globals.get('__pdb_convenience_variables', {})
|
||||
return [f"${name}" for name in conv_vars if name.startswith(text[1:])]
|
||||
|
||||
# Use rlcompleter to do the completion
|
||||
state = 0
|
||||
matches = []
|
||||
completer = Completer(self.curframe.f_globals | self.curframe_locals)
|
||||
while (match := completer.complete(text, state)) is not None:
|
||||
matches.append(match)
|
||||
state += 1
|
||||
return matches
|
||||
|
||||
# Command definitions, called by cmdloop()
|
||||
# The argument is the remaining string on the command line
|
||||
# Return true to exit from the command loop
|
||||
|
|
|
@ -3567,6 +3567,57 @@ class PdbTestReadline(unittest.TestCase):
|
|||
self.assertIn(b'species', output)
|
||||
self.assertIn(b'$_frame', output)
|
||||
|
||||
def test_builtin_completion(self):
|
||||
script = textwrap.dedent("""
|
||||
value = "speci"
|
||||
import pdb; pdb.Pdb().set_trace()
|
||||
""")
|
||||
|
||||
# Complete: print(value + 'al')
|
||||
input = b"pri\tval\t + 'al')\n"
|
||||
|
||||
# Continue
|
||||
input += b"c\n"
|
||||
|
||||
output = run_pty(script, input)
|
||||
|
||||
self.assertIn(b'special', output)
|
||||
|
||||
def test_local_namespace(self):
|
||||
script = textwrap.dedent("""
|
||||
def f():
|
||||
original = "I live Pythin"
|
||||
import pdb; pdb.Pdb().set_trace()
|
||||
f()
|
||||
""")
|
||||
|
||||
# Complete: original.replace('i', 'o')
|
||||
input = b"orig\t.repl\t('i', 'o')\n"
|
||||
|
||||
# Continue
|
||||
input += b"c\n"
|
||||
|
||||
output = run_pty(script, input)
|
||||
|
||||
self.assertIn(b'I love Python', output)
|
||||
|
||||
def test_multiline_completion(self):
|
||||
script = textwrap.dedent("""
|
||||
import pdb; pdb.Pdb().set_trace()
|
||||
""")
|
||||
|
||||
input = b"def func():\n"
|
||||
# Complete: \treturn 40 + 2
|
||||
input += b"\tret\t 40 + 2\n"
|
||||
input += b"\n"
|
||||
# Complete: func()
|
||||
input += b"fun\t()\n"
|
||||
input += b"c\n"
|
||||
|
||||
output = run_pty(script, input)
|
||||
|
||||
self.assertIn(b'42', output)
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
from test import test_pdb
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Make completion of :mod:`pdb` similar to Python REPL
|
Loading…
Reference in New Issue