From b7d52b8103e33ea005ce27d4a4a068fe6b6b954c Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 14 Nov 2023 04:22:25 -0800 Subject: [PATCH] gh-110944: Make pdb completion work for alias and convenience vars (GH-110945) --- Lib/pdb.py | 18 +++++++++++++++- Lib/test/test_pdb.py | 21 +++++++++++++++++++ ...-10-16-18-41-51.gh-issue-110944.CmUKXo.rst | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index a4b02e010a6be6a..ed78d749a47fa8e 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -238,7 +238,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, try: import readline # remove some common file name delimiters - readline.set_completer_delims(' \t\n`@#$%^&*()=+[{]}\\|;:\'",<>?') + readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?') except ImportError: pass self.allow_kbdint = False @@ -686,6 +686,18 @@ def set_convenience_variable(self, frame, name, value): # Generic completion functions. Individual complete_foo methods can be # assigned below to one of these functions. + def completenames(self, text, line, begidx, endidx): + # Overwrite completenames() of cmd so for the command completion, + # if no current command matches, check for expressions as well + commands = super().completenames(text, line, begidx, endidx) + for alias in self.aliases: + if alias.startswith(text): + commands.append(alias) + if commands: + return commands + else: + return self._complete_expression(text, line, begidx, endidx) + def _complete_location(self, text, line, begidx, endidx): # Complete a file/module/function location for break/tbreak/clear. if line.strip().endswith((':', ',')): @@ -720,6 +732,10 @@ def _complete_expression(self, text, line, begidx, endidx): # complete builtins, and they clutter the namespace quite heavily, so we # leave them out. ns = {**self.curframe.f_globals, **self.curframe_locals} + 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:])] if '.' in text: # Walk an attribute chain up to the last part, similar to what # rlcompleter does. This will bail if any of the parts are not diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 2ec4e2ee0e03601..67a4053a2ac8bca 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3289,6 +3289,27 @@ def test_basic_completion(self): self.assertIn(b'continue', output) self.assertIn(b'hello!', output) + def test_expression_completion(self): + script = textwrap.dedent(""" + value = "speci" + import pdb; pdb.Pdb().set_trace() + """) + + # Complete: value + 'al' + input = b"val\t + 'al'\n" + # Complete: p value + 'es' + input += b"p val\t + 'es'\n" + # Complete: $_frame + input += b"$_fra\t\n" + # Continue + input += b"c\n" + + output = run_pty(script, input) + + self.assertIn(b'special', output) + self.assertIn(b'species', output) + self.assertIn(b'$_frame', output) + def load_tests(loader, tests, pattern): from test import test_pdb diff --git a/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst b/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst new file mode 100644 index 000000000000000..ec9ca5a11f1ac8a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst @@ -0,0 +1 @@ +Support alias and convenience vars for :mod:`pdb` completion