]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-145378: Generate consistent colors for pdb commands (#149305)
authorTian Gao <gaogaotiantian@hotmail.com>
Tue, 5 May 2026 08:25:45 +0000 (01:25 -0700)
committerGitHub <noreply@github.com>
Tue, 5 May 2026 08:25:45 +0000 (01:25 -0700)
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2026-05-03-01-49-57.gh-issue-145378.rtyAWM.rst [new file with mode: 0644]

index bdc9caf80ec26eaa66d5b780f196faa24af4255d..f2a653cf53c74899e2e8092509d7f7c29f15fa10 100644 (file)
@@ -402,6 +402,7 @@ class PdbPyReplInput:
                 completer_delims=frozenset(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?')
             )
         )
+        self.readline_wrapper.get_reader().gen_colors = self.gen_colors
 
     def readline(self):
 
@@ -463,6 +464,26 @@ class PdbPyReplInput:
         except IndexError:
             return None
 
+    def gen_colors(self, buffer):
+        from _pyrepl.utils import ColorSpan, Span
+
+        if not buffer.strip():
+            return
+
+        leading_spaces = len(buffer) - len(buffer.lstrip())
+        leading_text = buffer.split()[0]
+        if hasattr(self.pdb_instance, 'do_' + leading_text):
+            yield ColorSpan(
+                Span(leading_spaces, leading_spaces + len(leading_text) - 1),
+                "soft_keyword"
+            )
+            # Redact the command text with spaces so there will be no duplicated
+            # color span generated for it later.
+            redact_length = leading_spaces + len(leading_text)
+            buffer = ' ' * redact_length + buffer[redact_length:]
+
+        yield from _pyrepl.utils.gen_colors(buffer)
+
 
 class Pdb(bdb.Bdb, cmd.Cmd):
     _previous_sigint_handler = None
index db90019975521ea74c1c234658b2cc5a0f8f76ab..8b6ccfbf051e6e5784f73226d7d894add062348a 100644 (file)
@@ -4981,6 +4981,29 @@ class PdbTestColorize(unittest.TestCase):
         p.set_trace(commands=['w', 'c'])
         self.assertIn("\x1b", output.getvalue())
 
+    @unittest.skipIf(not pdb._pyrepl_available(), "pyrepl is not available")
+    def test_gen_colors(self):
+        p = pdb.Pdb()
+        gen_colors = p.pyrepl_input.gen_colors
+
+        test_cases = [
+            ("longlist", [((0, 7), "soft_keyword")]),
+            ("!longlist", [((0, 0), "op")]),
+            ("list", [((0, 3), "soft_keyword")]),
+            ("list(", [((0, 3), "builtin"), ((4, 4), "op")]),
+            ("a = 1", [
+                ((0, 0), "soft_keyword"),
+                ((2, 2), "op"),
+                ((4, 4), "number"),
+            ])
+        ]
+
+        for buffer, expected in test_cases:
+            for color_span, ((start, end), tag) in zip(gen_colors(buffer), expected, strict=True):
+                self.assertEqual(color_span.span.start, start)
+                self.assertEqual(color_span.span.end, end)
+                self.assertEqual(color_span.tag, tag)
+
 
 @support.force_not_colorized_test_class
 @support.requires_subprocess()
diff --git a/Misc/NEWS.d/next/Library/2026-05-03-01-49-57.gh-issue-145378.rtyAWM.rst b/Misc/NEWS.d/next/Library/2026-05-03-01-49-57.gh-issue-145378.rtyAWM.rst
new file mode 100644 (file)
index 0000000..416ad4b
--- /dev/null
@@ -0,0 +1 @@
+Generate consistent colors for :mod:`pdb` commands in :mod:`pdb` REPL.