https://github.com/python/cpython/commit/01e7405da400e8997f8964d06cc414045e144681
commit: 01e7405da400e8997f8964d06cc414045e144681
branch: main
author: Tian Gao <[email protected]>
committer: iritkatriel <[email protected]>
date: 2024-03-25T15:18:09Z
summary:

gh-112948: Make pdb completion similar to repl completion (#112950)

files:
A Misc/NEWS.d/next/Library/2023-12-11-00-51-51.gh-issue-112948.k-OKp5.rst
M Lib/pdb.py
M Lib/test/test_pdb.py

diff --git a/Lib/pdb.py b/Lib/pdb.py
index 88ea900e63f42b..f8f42ddcdb2b20 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -87,6 +87,7 @@
 import linecache
 
 from contextlib import contextmanager
+from rlcompleter import Completer
 from typing import Union
 
 
@@ -573,20 +574,14 @@ def displayhook(self, obj):
             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 @@ def default(self, line):
         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 @@ def completenames(self, text, line, begidx, endidx):
         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 @@ def _complete_expression(self, text, line, begidx, endidx):
             # 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
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index 69691e930562bc..9ee994e3fe309d 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -3567,6 +3567,57 @@ def test_expression_completion(self):
         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
diff --git 
a/Misc/NEWS.d/next/Library/2023-12-11-00-51-51.gh-issue-112948.k-OKp5.rst 
b/Misc/NEWS.d/next/Library/2023-12-11-00-51-51.gh-issue-112948.k-OKp5.rst
new file mode 100644
index 00000000000000..0925a7caba6f07
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-12-11-00-51-51.gh-issue-112948.k-OKp5.rst
@@ -0,0 +1 @@
+Make completion of :mod:`pdb` similar to Python REPL

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to