https://github.com/python/cpython/commit/ff4959b6b0cf6da5d2f59260a9103b31c812b0ba
commit: ff4959b6b0cf6da5d2f59260a9103b31c812b0ba
branch: main
author: Tian Gao <[email protected]>
committer: ambv <[email protected]>
date: 2025-05-05T09:49:52+02:00
summary:
gh-113081: Highlight source code in pdb (#133355)
files:
A Misc/NEWS.d/next/Library/2025-05-03-18-48-54.gh-issue-113081.JsLJ1X.rst
M Doc/library/pdb.rst
M Doc/whatsnew/3.14.rst
M Lib/pdb.py
M Lib/test/test_pdb.py
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index 3c8c07074993f2..a0304edddf6478 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -243,7 +243,7 @@ The ``run*`` functions and :func:`set_trace` are aliases
for instantiating the
access further features, you have to do this yourself:
.. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None, \
- nosigint=False, readrc=True, mode=None, backend=None)
+ nosigint=False, readrc=True, mode=None, backend=None,
colorize=False)
:class:`Pdb` is the debugger class.
@@ -273,6 +273,9 @@ access further features, you have to do this yourself:
is passed, the default backend will be used. See
:func:`set_default_backend`.
Otherwise the supported backends are ``'settrace'`` and ``'monitoring'``.
+ The *colorize* argument, if set to ``True``, will enable colorized output
in the
+ debugger, if color is supported. This will highlight source code displayed
in pdb.
+
Example call to enable tracing with *skip*::
import pdb; pdb.Pdb(skip=['django.*']).set_trace()
@@ -295,6 +298,9 @@ access further features, you have to do this yourself:
.. versionadded:: 3.14
Added the *backend* argument.
+ .. versionadded:: 3.14
+ Added the *colorize* argument.
+
.. versionchanged:: 3.14
Inline breakpoints like :func:`breakpoint` or :func:`pdb.set_trace` will
always stop the program at calling frame, ignoring the *skip* pattern
(if any).
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 4b5515900cad41..330140010160f6 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1436,6 +1436,11 @@ pdb
function.
(Contributed by Tian Gao in :gh:`132576`.)
+* Source code displayed in :mod:`pdb` will be syntax-highlighted. This feature
+ can be controlled using the same methods as PyREPL, in addition to the newly
+ added ``colorize`` argument of :class:`pdb.Pdb`.
+ (Contributed by Tian Gao in :gh:`133355`.)
+
pickle
------
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 2aa60c75396085..195bfa557ef1e4 100644
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -93,6 +93,7 @@
import traceback
import linecache
import _colorize
+import _pyrepl.utils
from contextlib import closing
from contextlib import contextmanager
@@ -339,7 +340,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
_last_pdb_instance = None
def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
- nosigint=False, readrc=True, mode=None, backend=None):
+ nosigint=False, readrc=True, mode=None, backend=None,
colorize=False):
bdb.Bdb.__init__(self, skip=skip, backend=backend if backend else
get_default_backend())
cmd.Cmd.__init__(self, completekey, stdin, stdout)
sys.audit("pdb.Pdb")
@@ -352,6 +353,7 @@ def __init__(self, completekey='tab', stdin=None,
stdout=None, skip=None,
self._wait_for_mainpyfile = False
self.tb_lineno = {}
self.mode = mode
+ self.colorize = _colorize.can_colorize(file=stdout or sys.stdout) and
colorize
# Try to load readline if it exists
try:
import readline
@@ -1036,6 +1038,13 @@ def handle_command_def(self, line):
return True
return False
+ def _colorize_code(self, code):
+ if self.colorize:
+ colors = list(_pyrepl.utils.gen_colors(code))
+ chars, _ = _pyrepl.utils.disp_str(code, colors=colors)
+ code = "".join(chars)
+ return code
+
# interface abstraction functions
def message(self, msg, end='\n'):
@@ -2166,6 +2175,8 @@ def _print_lines(self, lines, start, breaks=(),
frame=None):
s += '->'
elif lineno == exc_lineno:
s += '>>'
+ if self.colorize:
+ line = self._colorize_code(line)
self.message(s + '\t' + line.rstrip())
def do_whatis(self, arg):
@@ -2365,8 +2376,14 @@ def print_stack_entry(self, frame_lineno,
prompt_prefix=line_prefix):
prefix = '> '
else:
prefix = ' '
- self.message(prefix +
- self.format_stack_entry(frame_lineno, prompt_prefix))
+ stack_entry = self.format_stack_entry(frame_lineno, prompt_prefix)
+ if self.colorize:
+ lines = stack_entry.split(prompt_prefix, 1)
+ if len(lines) > 1:
+ # We have some code to display
+ lines[1] = self._colorize_code(lines[1])
+ stack_entry = prompt_prefix.join(lines)
+ self.message(prefix + stack_entry)
# Provide help
@@ -2604,7 +2621,7 @@ def set_trace(*, header=None, commands=None):
if Pdb._last_pdb_instance is not None:
pdb = Pdb._last_pdb_instance
else:
- pdb = Pdb(mode='inline', backend='monitoring')
+ pdb = Pdb(mode='inline', backend='monitoring', colorize=True)
if header is not None:
pdb.message(header)
pdb.set_trace(sys._getframe().f_back, commands=commands)
@@ -2619,7 +2636,7 @@ async def set_trace_async(*, header=None, commands=None):
if Pdb._last_pdb_instance is not None:
pdb = Pdb._last_pdb_instance
else:
- pdb = Pdb(mode='inline', backend='monitoring')
+ pdb = Pdb(mode='inline', backend='monitoring', colorize=True)
if header is not None:
pdb.message(header)
await pdb.set_trace_async(sys._getframe().f_back, commands=commands)
@@ -2633,7 +2650,7 @@ def __init__(self, sockfile, owns_sockfile=True,
**kwargs):
self._sockfile = sockfile
self._command_name_cache = []
self._write_failed = False
- super().__init__(**kwargs)
+ super().__init__(colorize=False, **kwargs)
@staticmethod
def protocol_version():
@@ -3345,7 +3362,7 @@ def main():
# modified by the script being debugged. It's a bad idea when it was
# changed by the user from the command line. There is a "restart" command
# which allows explicit specification of command line arguments.
- pdb = Pdb(mode='cli', backend='monitoring')
+ pdb = Pdb(mode='cli', backend='monitoring', colorize=True)
pdb.rcLines.extend(opts.commands)
while True:
try:
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index be365a5a3ddeec..9223a7130d4e0d 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -1,7 +1,9 @@
# A test suite for pdb; not very comprehensive at the moment.
+import _colorize
import doctest
import gc
+import io
import os
import pdb
import sys
@@ -3446,6 +3448,7 @@ def test_pdb_issue_gh_65052():
"""
[email protected]_not_colorized_test_class
@support.requires_subprocess()
class PdbTestCase(unittest.TestCase):
def tearDown(self):
@@ -4688,6 +4691,40 @@ def foo():
self.assertIn("42", stdout)
[email protected](_colorize.can_colorize(), "Test requires colorize")
+class PdbTestColorize(unittest.TestCase):
+ def setUp(self):
+ self._original_can_colorize = _colorize.can_colorize
+ # Force colorize to be enabled because we are sending data
+ # to a StringIO
+ _colorize.can_colorize = lambda *args, **kwargs: True
+
+ def tearDown(self):
+ _colorize.can_colorize = self._original_can_colorize
+
+ def test_code_display(self):
+ output = io.StringIO()
+ p = pdb.Pdb(stdout=output, colorize=True)
+ p.set_trace(commands=['ll', 'c'])
+ self.assertIn("\x1b", output.getvalue())
+
+ output = io.StringIO()
+ p = pdb.Pdb(stdout=output, colorize=False)
+ p.set_trace(commands=['ll', 'c'])
+ self.assertNotIn("\x1b", output.getvalue())
+
+ output = io.StringIO()
+ p = pdb.Pdb(stdout=output)
+ p.set_trace(commands=['ll', 'c'])
+ self.assertNotIn("\x1b", output.getvalue())
+
+ def test_stack_entry(self):
+ output = io.StringIO()
+ p = pdb.Pdb(stdout=output, colorize=True)
+ p.set_trace(commands=['w', 'c'])
+ self.assertIn("\x1b", output.getvalue())
+
+
@support.force_not_colorized_test_class
@support.requires_subprocess()
class TestREPLSession(unittest.TestCase):
diff --git
a/Misc/NEWS.d/next/Library/2025-05-03-18-48-54.gh-issue-113081.JsLJ1X.rst
b/Misc/NEWS.d/next/Library/2025-05-03-18-48-54.gh-issue-113081.JsLJ1X.rst
new file mode 100644
index 00000000000000..43c2d9dfa39117
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-03-18-48-54.gh-issue-113081.JsLJ1X.rst
@@ -0,0 +1 @@
+Highlight syntax on source code in :mod:`pdb`.
_______________________________________________
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]