]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-133349: Enable auto-indent for pdb's multi-line mode (#133350)
authorTian Gao <gaogaotiantian@hotmail.com>
Mon, 5 May 2025 17:48:09 +0000 (10:48 -0700)
committerGitHub <noreply@github.com>
Mon, 5 May 2025 17:48:09 +0000 (13:48 -0400)
Doc/whatsnew/3.14.rst
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2025-05-03-16-04-04.gh-issue-133349.kAhJDY.rst [new file with mode: 0644]

index 0c4bd8600858f6cb72eec146eddb264726328227..795403a9b7c1071d7392ddecc0f9d95b4335025d 100644 (file)
@@ -1443,6 +1443,11 @@ pdb
   fill in a 4-space indentation now, instead of inserting a ``\t`` character.
   (Contributed by Tian Gao in :gh:`130471`.)
 
+* Auto-indent is introduced in :mod:`pdb` multi-line input. It will either
+  keep the indentation of the last line or insert a 4-space indentation when
+  it detects a new code block.
+  (Contributed by Tian Gao in :gh:`133350`.)
+
 * ``$_asynctask`` is added to access the current asyncio task if applicable.
   (Contributed by Tian Gao in :gh:`124367`.)
 
index b30df59d793f244973b5a560f8a6db7d91b26a9a..0de8bbe37e471ed4eec4ebf6fa1b16dbd871d449 100644 (file)
@@ -745,12 +745,34 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             self.message(repr(obj))
 
     @contextmanager
-    def _enable_multiline_completion(self):
+    def _enable_multiline_input(self):
+        try:
+            import readline
+        except ImportError:
+            yield
+            return
+
+        def input_auto_indent():
+            last_index = readline.get_current_history_length()
+            last_line = readline.get_history_item(last_index)
+            if last_line:
+                if last_line.isspace():
+                    # If the last line is empty, we don't need to indent
+                    return
+
+                last_line = last_line.rstrip('\r\n')
+                indent = len(last_line) - len(last_line.lstrip())
+                if last_line.endswith(":"):
+                    indent += 4
+                readline.insert_text(' ' * indent)
+
         completenames = self.completenames
         try:
             self.completenames = self.complete_multiline_names
+            readline.set_startup_hook(input_auto_indent)
             yield
         finally:
+            readline.set_startup_hook()
             self.completenames = completenames
         return
 
@@ -859,7 +881,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         try:
             if (code := codeop.compile_command(line + '\n', '<stdin>', 'single')) is None:
                 # Multi-line mode
-                with self._enable_multiline_completion():
+                with self._enable_multiline_input():
                     buffer = line
                     continue_prompt = "...   "
                     while (code := codeop.compile_command(buffer, '<stdin>', 'single')) is None:
@@ -881,7 +903,11 @@ class Pdb(bdb.Bdb, cmd.Cmd):
                                 return None, None, False
                             else:
                                 line = line.rstrip('\r\n')
-                        buffer += '\n' + line
+                        if line.isspace():
+                            # empty line, just continue
+                            buffer += '\n'
+                        else:
+                            buffer += '\n' + line
                     self.lastcmd = buffer
         except SyntaxError as e:
             # Maybe it's an await expression/statement
index 9223a7130d4e0d653502f2f21925244ee4b86464..05f2ec191d4e1c69a660796bcc26dc2cc7afd319 100644 (file)
@@ -4849,14 +4849,35 @@ class PdbTestReadline(unittest.TestCase):
 
         self.assertIn(b'I love Python', output)
 
+    def test_multiline_auto_indent(self):
+        script = textwrap.dedent("""
+            import pdb; pdb.Pdb().set_trace()
+        """)
+
+        input = b"def f(x):\n"
+        input += b"if x > 0:\n"
+        input += b"x += 1\n"
+        input += b"return x\n"
+        # We need to do backspaces to remove the auto-indentation
+        input += b"\x08\x08\x08\x08else:\n"
+        input += b"return -x\n"
+        input += b"\n"
+        input += b"f(-21-21)\n"
+        input += b"c\n"
+
+        output = run_pty(script, input)
+
+        self.assertIn(b'42', 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"
+        # Auto-indent
+        # Complete: return 40 + 2
+        input += b"ret\t 40 + 2\n"
         input += b"\n"
         # Complete: func()
         input += b"fun\t()\n"
@@ -4876,12 +4897,13 @@ class PdbTestReadline(unittest.TestCase):
         # if the completion is not working as expected
         input = textwrap.dedent("""\
             def func():
-            \ta = 1
-             \ta += 1
-              \ta += 1
-               \tif a > 0:
-                    a += 1
-            \t\treturn a
+            a = 1
+            \x08\ta += 1
+            \x08\x08\ta += 1
+            \x08\x08\x08\ta += 1
+            \x08\x08\x08\x08\tif a > 0:
+            a += 1
+            \x08\x08\x08\x08return a
 
             func()
             c
@@ -4889,7 +4911,7 @@ class PdbTestReadline(unittest.TestCase):
 
         output = run_pty(script, input)
 
-        self.assertIn(b'4', output)
+        self.assertIn(b'5', output)
         self.assertNotIn(b'Error', output)
 
     def test_interact_completion(self):
diff --git a/Misc/NEWS.d/next/Library/2025-05-03-16-04-04.gh-issue-133349.kAhJDY.rst b/Misc/NEWS.d/next/Library/2025-05-03-16-04-04.gh-issue-133349.kAhJDY.rst
new file mode 100644 (file)
index 0000000..cf0c199
--- /dev/null
@@ -0,0 +1 @@
+Introduced auto-indent in :mod:`pdb` multi-line input.