mirror of
https://github.com/python/cpython.git
synced 2025-08-31 22:18:28 +00:00
gh-113081: Highlight source code in pdb (#133355)
This commit is contained in:
parent
08d7687094
commit
ff4959b6b0
5 changed files with 74 additions and 8 deletions
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
------
|
||||
|
|
31
Lib/pdb.py
31
Lib/pdb.py
|
@ -93,6 +93,7 @@ import itertools
|
|||
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 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
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 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
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 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
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 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
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 @@ class _PdbServer(Pdb):
|
|||
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:
|
||||
|
|
|
@ -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():
|
|||
"""
|
||||
|
||||
|
||||
@support.force_not_colorized_test_class
|
||||
@support.requires_subprocess()
|
||||
class PdbTestCase(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
|
@ -4688,6 +4691,40 @@ class PdbTestInline(unittest.TestCase):
|
|||
self.assertIn("42", stdout)
|
||||
|
||||
|
||||
@unittest.skipUnless(_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):
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Highlight syntax on source code in :mod:`pdb`.
|
Loading…
Add table
Add a link
Reference in a new issue