##// END OF EJS Templates
Reify colors from debugger.
Matthias Bussonnier -
Show More
@@ -1,1122 +1,1100 b''
1 """
1 """
2 Pdb debugger class.
2 Pdb debugger class.
3
3
4
4
5 This is an extension to PDB which adds a number of new features.
5 This is an extension to PDB which adds a number of new features.
6 Note that there is also the `IPython.terminal.debugger` class which provides UI
6 Note that there is also the `IPython.terminal.debugger` class which provides UI
7 improvements.
7 improvements.
8
8
9 We also strongly recommend to use this via the `ipdb` package, which provides
9 We also strongly recommend to use this via the `ipdb` package, which provides
10 extra configuration options.
10 extra configuration options.
11
11
12 Among other things, this subclass of PDB:
12 Among other things, this subclass of PDB:
13 - supports many IPython magics like pdef/psource
13 - supports many IPython magics like pdef/psource
14 - hide frames in tracebacks based on `__tracebackhide__`
14 - hide frames in tracebacks based on `__tracebackhide__`
15 - allows to skip frames based on `__debuggerskip__`
15 - allows to skip frames based on `__debuggerskip__`
16
16
17 The skipping and hiding frames are configurable via the `skip_predicates`
17 The skipping and hiding frames are configurable via the `skip_predicates`
18 command.
18 command.
19
19
20 By default, frames from readonly files will be hidden, frames containing
20 By default, frames from readonly files will be hidden, frames containing
21 ``__tracebackhide__=True`` will be hidden.
21 ``__tracebackhide__=True`` will be hidden.
22
22
23 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
23 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
24 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
24 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
25
25
26 >>> def helpers_helper():
26 >>> def helpers_helper():
27 ... pass
27 ... pass
28 ...
28 ...
29 ... def helper_1():
29 ... def helper_1():
30 ... print("don't step in me")
30 ... print("don't step in me")
31 ... helpers_helpers() # will be stepped over unless breakpoint set.
31 ... helpers_helpers() # will be stepped over unless breakpoint set.
32 ...
32 ...
33 ...
33 ...
34 ... def helper_2():
34 ... def helper_2():
35 ... print("in me neither")
35 ... print("in me neither")
36 ...
36 ...
37
37
38 One can define a decorator that wraps a function between the two helpers:
38 One can define a decorator that wraps a function between the two helpers:
39
39
40 >>> def pdb_skipped_decorator(function):
40 >>> def pdb_skipped_decorator(function):
41 ...
41 ...
42 ...
42 ...
43 ... def wrapped_fn(*args, **kwargs):
43 ... def wrapped_fn(*args, **kwargs):
44 ... __debuggerskip__ = True
44 ... __debuggerskip__ = True
45 ... helper_1()
45 ... helper_1()
46 ... __debuggerskip__ = False
46 ... __debuggerskip__ = False
47 ... result = function(*args, **kwargs)
47 ... result = function(*args, **kwargs)
48 ... __debuggerskip__ = True
48 ... __debuggerskip__ = True
49 ... helper_2()
49 ... helper_2()
50 ... # setting __debuggerskip__ to False again is not necessary
50 ... # setting __debuggerskip__ to False again is not necessary
51 ... return result
51 ... return result
52 ...
52 ...
53 ... return wrapped_fn
53 ... return wrapped_fn
54
54
55 When decorating a function, ipdb will directly step into ``bar()`` by
55 When decorating a function, ipdb will directly step into ``bar()`` by
56 default:
56 default:
57
57
58 >>> @foo_decorator
58 >>> @foo_decorator
59 ... def bar(x, y):
59 ... def bar(x, y):
60 ... return x * y
60 ... return x * y
61
61
62
62
63 You can toggle the behavior with
63 You can toggle the behavior with
64
64
65 ipdb> skip_predicates debuggerskip false
65 ipdb> skip_predicates debuggerskip false
66
66
67 or configure it in your ``.pdbrc``
67 or configure it in your ``.pdbrc``
68
68
69
69
70
70
71 License
71 License
72 -------
72 -------
73
73
74 Modified from the standard pdb.Pdb class to avoid including readline, so that
74 Modified from the standard pdb.Pdb class to avoid including readline, so that
75 the command line completion of other programs which include this isn't
75 the command line completion of other programs which include this isn't
76 damaged.
76 damaged.
77
77
78 In the future, this class will be expanded with improvements over the standard
78 In the future, this class will be expanded with improvements over the standard
79 pdb.
79 pdb.
80
80
81 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
81 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
82 with minor changes. Licensing should therefore be under the standard Python
82 with minor changes. Licensing should therefore be under the standard Python
83 terms. For details on the PSF (Python Software Foundation) standard license,
83 terms. For details on the PSF (Python Software Foundation) standard license,
84 see:
84 see:
85
85
86 https://docs.python.org/2/license.html
86 https://docs.python.org/2/license.html
87
87
88
88
89 All the changes since then are under the same license as IPython.
89 All the changes since then are under the same license as IPython.
90
90
91 """
91 """
92
92
93 #*****************************************************************************
93 #*****************************************************************************
94 #
94 #
95 # This file is licensed under the PSF license.
95 # This file is licensed under the PSF license.
96 #
96 #
97 # Copyright (C) 2001 Python Software Foundation, www.python.org
97 # Copyright (C) 2001 Python Software Foundation, www.python.org
98 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
98 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
99 #
99 #
100 #
100 #
101 #*****************************************************************************
101 #*****************************************************************************
102
102
103 from __future__ import annotations
103 from __future__ import annotations
104
104
105 import inspect
105 import inspect
106 import linecache
106 import linecache
107 import os
107 import os
108 import re
108 import re
109 import sys
109 import sys
110 from contextlib import contextmanager
110 from contextlib import contextmanager
111 from functools import lru_cache
111 from functools import lru_cache
112
112
113 from IPython import get_ipython
113 from IPython import get_ipython
114 from IPython.core.excolors import exception_colors
114 from IPython.core.excolors import exception_colors
115 from IPython.utils import PyColorize, coloransi, py3compat
115 from IPython.utils import PyColorize, coloransi, py3compat
116
116
117 from typing import TYPE_CHECKING
117 from typing import TYPE_CHECKING
118
118
119 if TYPE_CHECKING:
119 if TYPE_CHECKING:
120 # otherwise circular import
120 # otherwise circular import
121 from IPython.core.interactiveshell import InteractiveShell
121 from IPython.core.interactiveshell import InteractiveShell
122
122
123 # skip module docstests
123 # skip module docstests
124 __skip_doctest__ = True
124 __skip_doctest__ = True
125
125
126 prompt = 'ipdb> '
126 prompt = 'ipdb> '
127
127
128 # We have to check this directly from sys.argv, config struct not yet available
128 # We have to check this directly from sys.argv, config struct not yet available
129 from pdb import Pdb as OldPdb
129 from pdb import Pdb as OldPdb
130
130
131 # Allow the set_trace code to operate outside of an ipython instance, even if
131 # Allow the set_trace code to operate outside of an ipython instance, even if
132 # it does so with some limitations. The rest of this support is implemented in
132 # it does so with some limitations. The rest of this support is implemented in
133 # the Tracer constructor.
133 # the Tracer constructor.
134
134
135 DEBUGGERSKIP = "__debuggerskip__"
135 DEBUGGERSKIP = "__debuggerskip__"
136
136
137
137
138 # this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
138 # this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
139 # on lower python versions, we backported the feature.
139 # on lower python versions, we backported the feature.
140 CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
140 CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
141
141
142
142
143 def make_arrow(pad):
143 def make_arrow(pad):
144 """generate the leading arrow in front of traceback or debugger"""
144 """generate the leading arrow in front of traceback or debugger"""
145 if pad >= 2:
145 if pad >= 2:
146 return '-'*(pad-2) + '> '
146 return '-'*(pad-2) + '> '
147 elif pad == 1:
147 elif pad == 1:
148 return '>'
148 return '>'
149 return ''
149 return ''
150
150
151
151
152 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
152 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
153 """Exception hook which handles `BdbQuit` exceptions.
153 """Exception hook which handles `BdbQuit` exceptions.
154
154
155 All other exceptions are processed using the `excepthook`
155 All other exceptions are processed using the `excepthook`
156 parameter.
156 parameter.
157 """
157 """
158 raise ValueError(
158 raise ValueError(
159 "`BdbQuit_excepthook` is deprecated since version 5.1",
159 "`BdbQuit_excepthook` is deprecated since version 5.1",
160 )
160 )
161
161
162
163 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
164 raise ValueError(
165 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
166 DeprecationWarning, stacklevel=2)
167
168
169 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
162 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
170
163
171
164
172 def strip_indentation(multiline_string):
165 def strip_indentation(multiline_string):
173 return RGX_EXTRA_INDENT.sub('', multiline_string)
166 return RGX_EXTRA_INDENT.sub('', multiline_string)
174
167
175
168
176 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
169 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
177 """Make new_fn have old_fn's doc string. This is particularly useful
170 """Make new_fn have old_fn's doc string. This is particularly useful
178 for the ``do_...`` commands that hook into the help system.
171 for the ``do_...`` commands that hook into the help system.
179 Adapted from from a comp.lang.python posting
172 Adapted from from a comp.lang.python posting
180 by Duncan Booth."""
173 by Duncan Booth."""
181 def wrapper(*args, **kw):
174 def wrapper(*args, **kw):
182 return new_fn(*args, **kw)
175 return new_fn(*args, **kw)
183 if old_fn.__doc__:
176 if old_fn.__doc__:
184 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
177 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
185 return wrapper
178 return wrapper
186
179
187
180
188 class Pdb(OldPdb):
181 class Pdb(OldPdb):
189 """Modified Pdb class, does not load readline.
182 """Modified Pdb class, does not load readline.
190
183
191 for a standalone version that uses prompt_toolkit, see
184 for a standalone version that uses prompt_toolkit, see
192 `IPython.terminal.debugger.TerminalPdb` and
185 `IPython.terminal.debugger.TerminalPdb` and
193 `IPython.terminal.debugger.set_trace()`
186 `IPython.terminal.debugger.set_trace()`
194
187
195
188
196 This debugger can hide and skip frames that are tagged according to some predicates.
189 This debugger can hide and skip frames that are tagged according to some predicates.
197 See the `skip_predicates` commands.
190 See the `skip_predicates` commands.
198
191
199 """
192 """
200
193
201 shell: InteractiveShell
194 shell: InteractiveShell
202
195
203 if CHAIN_EXCEPTIONS:
196 if CHAIN_EXCEPTIONS:
204 MAX_CHAINED_EXCEPTION_DEPTH = 999
197 MAX_CHAINED_EXCEPTION_DEPTH = 999
205
198
206 default_predicates = {
199 default_predicates = {
207 "tbhide": True,
200 "tbhide": True,
208 "readonly": False,
201 "readonly": False,
209 "ipython_internal": True,
202 "ipython_internal": True,
210 "debuggerskip": True,
203 "debuggerskip": True,
211 }
204 }
212
205
213 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
206 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
214 """Create a new IPython debugger.
207 """Create a new IPython debugger.
215
208
216 Parameters
209 Parameters
217 ----------
210 ----------
218 completekey : default None
211 completekey : default None
219 Passed to pdb.Pdb.
212 Passed to pdb.Pdb.
220 stdin : default None
213 stdin : default None
221 Passed to pdb.Pdb.
214 Passed to pdb.Pdb.
222 stdout : default None
215 stdout : default None
223 Passed to pdb.Pdb.
216 Passed to pdb.Pdb.
224 context : int
217 context : int
225 Number of lines of source code context to show when
218 Number of lines of source code context to show when
226 displaying stacktrace information.
219 displaying stacktrace information.
227 **kwargs
220 **kwargs
228 Passed to pdb.Pdb.
221 Passed to pdb.Pdb.
229
222
230 Notes
223 Notes
231 -----
224 -----
232 The possibilities are python version dependent, see the python
225 The possibilities are python version dependent, see the python
233 docs for more info.
226 docs for more info.
234 """
227 """
235
228
236 # Parent constructor:
229 # Parent constructor:
237 try:
230 try:
238 self.context = int(context)
231 self.context = int(context)
239 if self.context <= 0:
232 if self.context <= 0:
240 raise ValueError("Context must be a positive integer")
233 raise ValueError("Context must be a positive integer")
241 except (TypeError, ValueError) as e:
234 except (TypeError, ValueError) as e:
242 raise ValueError("Context must be a positive integer") from e
235 raise ValueError("Context must be a positive integer") from e
243
236
244 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
237 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
245 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
238 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
246
239
247 # IPython changes...
240 # IPython changes...
248 self.shell = get_ipython()
241 self.shell = get_ipython()
249
242
250 if self.shell is None:
243 if self.shell is None:
251 save_main = sys.modules['__main__']
244 save_main = sys.modules['__main__']
252 # No IPython instance running, we must create one
245 # No IPython instance running, we must create one
253 from IPython.terminal.interactiveshell import \
246 from IPython.terminal.interactiveshell import \
254 TerminalInteractiveShell
247 TerminalInteractiveShell
255 self.shell = TerminalInteractiveShell.instance()
248 self.shell = TerminalInteractiveShell.instance()
256 # needed by any code which calls __import__("__main__") after
249 # needed by any code which calls __import__("__main__") after
257 # the debugger was entered. See also #9941.
250 # the debugger was entered. See also #9941.
258 sys.modules["__main__"] = save_main
251 sys.modules["__main__"] = save_main
259
252
260
253
261 color_scheme = self.shell.colors
254 color_scheme = self.shell.colors
262
255
263 self.aliases = {}
256 self.aliases = {}
264
257
265 # Create color table: we copy the default one from the traceback
258 # Create color table: we copy the default one from the traceback
266 # module and add a few attributes needed for debugging
259 # module and add a few attributes needed for debugging
267 self.color_scheme_table = exception_colors()
260 self.color_scheme_table = exception_colors()
268
261
269 # shorthands
262 # shorthands
270 C = coloransi.TermColors
263 C = coloransi.TermColors
271 cst = self.color_scheme_table
264 cst = self.color_scheme_table
272
265
273 cst['NoColor'].colors.prompt = C.NoColor
274 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
275 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
276
277 cst['Linux'].colors.prompt = C.Green
278 cst['Linux'].colors.breakpoint_enabled = C.LightRed
279 cst['Linux'].colors.breakpoint_disabled = C.Red
280
281 cst['LightBG'].colors.prompt = C.Blue
282 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
283 cst['LightBG'].colors.breakpoint_disabled = C.Red
284
285 cst['Neutral'].colors.prompt = C.Blue
286 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
287 cst['Neutral'].colors.breakpoint_disabled = C.Red
288
266
289 # Add a python parser so we can syntax highlight source while
267 # Add a python parser so we can syntax highlight source while
290 # debugging.
268 # debugging.
291 self.parser = PyColorize.Parser(style=color_scheme)
269 self.parser = PyColorize.Parser(style=color_scheme)
292 self.set_colors(color_scheme)
270 self.set_colors(color_scheme)
293
271
294 # Set the prompt - the default prompt is '(Pdb)'
272 # Set the prompt - the default prompt is '(Pdb)'
295 self.prompt = prompt
273 self.prompt = prompt
296 self.skip_hidden = True
274 self.skip_hidden = True
297 self.report_skipped = True
275 self.report_skipped = True
298
276
299 # list of predicates we use to skip frames
277 # list of predicates we use to skip frames
300 self._predicates = self.default_predicates
278 self._predicates = self.default_predicates
301
279
302 if CHAIN_EXCEPTIONS:
280 if CHAIN_EXCEPTIONS:
303 self._chained_exceptions = tuple()
281 self._chained_exceptions = tuple()
304 self._chained_exception_index = 0
282 self._chained_exception_index = 0
305
283
306 #
284 #
307 def set_colors(self, scheme):
285 def set_colors(self, scheme):
308 """Shorthand access to the color table scheme selector method."""
286 """Shorthand access to the color table scheme selector method."""
309 self.color_scheme_table.set_active_scheme(scheme)
287 self.color_scheme_table.set_active_scheme(scheme)
310 self.parser.style = scheme
288 self.parser.style = scheme
311
289
312 def set_trace(self, frame=None):
290 def set_trace(self, frame=None):
313 if frame is None:
291 if frame is None:
314 frame = sys._getframe().f_back
292 frame = sys._getframe().f_back
315 self.initial_frame = frame
293 self.initial_frame = frame
316 return super().set_trace(frame)
294 return super().set_trace(frame)
317
295
318 def _hidden_predicate(self, frame):
296 def _hidden_predicate(self, frame):
319 """
297 """
320 Given a frame return whether it it should be hidden or not by IPython.
298 Given a frame return whether it it should be hidden or not by IPython.
321 """
299 """
322
300
323 if self._predicates["readonly"]:
301 if self._predicates["readonly"]:
324 fname = frame.f_code.co_filename
302 fname = frame.f_code.co_filename
325 # we need to check for file existence and interactively define
303 # we need to check for file existence and interactively define
326 # function would otherwise appear as RO.
304 # function would otherwise appear as RO.
327 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
305 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
328 return True
306 return True
329
307
330 if self._predicates["tbhide"]:
308 if self._predicates["tbhide"]:
331 if frame in (self.curframe, getattr(self, "initial_frame", None)):
309 if frame in (self.curframe, getattr(self, "initial_frame", None)):
332 return False
310 return False
333 frame_locals = self._get_frame_locals(frame)
311 frame_locals = self._get_frame_locals(frame)
334 if "__tracebackhide__" not in frame_locals:
312 if "__tracebackhide__" not in frame_locals:
335 return False
313 return False
336 return frame_locals["__tracebackhide__"]
314 return frame_locals["__tracebackhide__"]
337 return False
315 return False
338
316
339 def hidden_frames(self, stack):
317 def hidden_frames(self, stack):
340 """
318 """
341 Given an index in the stack return whether it should be skipped.
319 Given an index in the stack return whether it should be skipped.
342
320
343 This is used in up/down and where to skip frames.
321 This is used in up/down and where to skip frames.
344 """
322 """
345 # The f_locals dictionary is updated from the actual frame
323 # The f_locals dictionary is updated from the actual frame
346 # locals whenever the .f_locals accessor is called, so we
324 # locals whenever the .f_locals accessor is called, so we
347 # avoid calling it here to preserve self.curframe_locals.
325 # avoid calling it here to preserve self.curframe_locals.
348 # Furthermore, there is no good reason to hide the current frame.
326 # Furthermore, there is no good reason to hide the current frame.
349 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
327 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
350 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
328 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
351 if ip_start and self._predicates["ipython_internal"]:
329 if ip_start and self._predicates["ipython_internal"]:
352 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
330 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
353 return ip_hide
331 return ip_hide
354
332
355 if CHAIN_EXCEPTIONS:
333 if CHAIN_EXCEPTIONS:
356
334
357 def _get_tb_and_exceptions(self, tb_or_exc):
335 def _get_tb_and_exceptions(self, tb_or_exc):
358 """
336 """
359 Given a tracecack or an exception, return a tuple of chained exceptions
337 Given a tracecack or an exception, return a tuple of chained exceptions
360 and current traceback to inspect.
338 and current traceback to inspect.
361 This will deal with selecting the right ``__cause__`` or ``__context__``
339 This will deal with selecting the right ``__cause__`` or ``__context__``
362 as well as handling cycles, and return a flattened list of exceptions we
340 as well as handling cycles, and return a flattened list of exceptions we
363 can jump to with do_exceptions.
341 can jump to with do_exceptions.
364 """
342 """
365 _exceptions = []
343 _exceptions = []
366 if isinstance(tb_or_exc, BaseException):
344 if isinstance(tb_or_exc, BaseException):
367 traceback, current = tb_or_exc.__traceback__, tb_or_exc
345 traceback, current = tb_or_exc.__traceback__, tb_or_exc
368
346
369 while current is not None:
347 while current is not None:
370 if current in _exceptions:
348 if current in _exceptions:
371 break
349 break
372 _exceptions.append(current)
350 _exceptions.append(current)
373 if current.__cause__ is not None:
351 if current.__cause__ is not None:
374 current = current.__cause__
352 current = current.__cause__
375 elif (
353 elif (
376 current.__context__ is not None
354 current.__context__ is not None
377 and not current.__suppress_context__
355 and not current.__suppress_context__
378 ):
356 ):
379 current = current.__context__
357 current = current.__context__
380
358
381 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
359 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
382 self.message(
360 self.message(
383 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
361 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
384 " chained exceptions found, not all exceptions"
362 " chained exceptions found, not all exceptions"
385 "will be browsable with `exceptions`."
363 "will be browsable with `exceptions`."
386 )
364 )
387 break
365 break
388 else:
366 else:
389 traceback = tb_or_exc
367 traceback = tb_or_exc
390 return tuple(reversed(_exceptions)), traceback
368 return tuple(reversed(_exceptions)), traceback
391
369
392 @contextmanager
370 @contextmanager
393 def _hold_exceptions(self, exceptions):
371 def _hold_exceptions(self, exceptions):
394 """
372 """
395 Context manager to ensure proper cleaning of exceptions references
373 Context manager to ensure proper cleaning of exceptions references
396 When given a chained exception instead of a traceback,
374 When given a chained exception instead of a traceback,
397 pdb may hold references to many objects which may leak memory.
375 pdb may hold references to many objects which may leak memory.
398 We use this context manager to make sure everything is properly cleaned
376 We use this context manager to make sure everything is properly cleaned
399 """
377 """
400 try:
378 try:
401 self._chained_exceptions = exceptions
379 self._chained_exceptions = exceptions
402 self._chained_exception_index = len(exceptions) - 1
380 self._chained_exception_index = len(exceptions) - 1
403 yield
381 yield
404 finally:
382 finally:
405 # we can't put those in forget as otherwise they would
383 # we can't put those in forget as otherwise they would
406 # be cleared on exception change
384 # be cleared on exception change
407 self._chained_exceptions = tuple()
385 self._chained_exceptions = tuple()
408 self._chained_exception_index = 0
386 self._chained_exception_index = 0
409
387
410 def do_exceptions(self, arg):
388 def do_exceptions(self, arg):
411 """exceptions [number]
389 """exceptions [number]
412 List or change current exception in an exception chain.
390 List or change current exception in an exception chain.
413 Without arguments, list all the current exception in the exception
391 Without arguments, list all the current exception in the exception
414 chain. Exceptions will be numbered, with the current exception indicated
392 chain. Exceptions will be numbered, with the current exception indicated
415 with an arrow.
393 with an arrow.
416 If given an integer as argument, switch to the exception at that index.
394 If given an integer as argument, switch to the exception at that index.
417 """
395 """
418 if not self._chained_exceptions:
396 if not self._chained_exceptions:
419 self.message(
397 self.message(
420 "Did not find chained exceptions. To move between"
398 "Did not find chained exceptions. To move between"
421 " exceptions, pdb/post_mortem must be given an exception"
399 " exceptions, pdb/post_mortem must be given an exception"
422 " object rather than a traceback."
400 " object rather than a traceback."
423 )
401 )
424 return
402 return
425 if not arg:
403 if not arg:
426 for ix, exc in enumerate(self._chained_exceptions):
404 for ix, exc in enumerate(self._chained_exceptions):
427 prompt = ">" if ix == self._chained_exception_index else " "
405 prompt = ">" if ix == self._chained_exception_index else " "
428 rep = repr(exc)
406 rep = repr(exc)
429 if len(rep) > 80:
407 if len(rep) > 80:
430 rep = rep[:77] + "..."
408 rep = rep[:77] + "..."
431 indicator = (
409 indicator = (
432 " -"
410 " -"
433 if self._chained_exceptions[ix].__traceback__ is None
411 if self._chained_exceptions[ix].__traceback__ is None
434 else f"{ix:>3}"
412 else f"{ix:>3}"
435 )
413 )
436 self.message(f"{prompt} {indicator} {rep}")
414 self.message(f"{prompt} {indicator} {rep}")
437 else:
415 else:
438 try:
416 try:
439 number = int(arg)
417 number = int(arg)
440 except ValueError:
418 except ValueError:
441 self.error("Argument must be an integer")
419 self.error("Argument must be an integer")
442 return
420 return
443 if 0 <= number < len(self._chained_exceptions):
421 if 0 <= number < len(self._chained_exceptions):
444 if self._chained_exceptions[number].__traceback__ is None:
422 if self._chained_exceptions[number].__traceback__ is None:
445 self.error(
423 self.error(
446 "This exception does not have a traceback, cannot jump to it"
424 "This exception does not have a traceback, cannot jump to it"
447 )
425 )
448 return
426 return
449
427
450 self._chained_exception_index = number
428 self._chained_exception_index = number
451 self.setup(None, self._chained_exceptions[number].__traceback__)
429 self.setup(None, self._chained_exceptions[number].__traceback__)
452 self.print_stack_entry(self.stack[self.curindex])
430 self.print_stack_entry(self.stack[self.curindex])
453 else:
431 else:
454 self.error("No exception with that number")
432 self.error("No exception with that number")
455
433
456 def interaction(self, frame, tb_or_exc):
434 def interaction(self, frame, tb_or_exc):
457 try:
435 try:
458 if CHAIN_EXCEPTIONS:
436 if CHAIN_EXCEPTIONS:
459 # this context manager is part of interaction in 3.13
437 # this context manager is part of interaction in 3.13
460 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
438 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
461 if isinstance(tb_or_exc, BaseException):
439 if isinstance(tb_or_exc, BaseException):
462 assert tb is not None, "main exception must have a traceback"
440 assert tb is not None, "main exception must have a traceback"
463 with self._hold_exceptions(_chained_exceptions):
441 with self._hold_exceptions(_chained_exceptions):
464 OldPdb.interaction(self, frame, tb)
442 OldPdb.interaction(self, frame, tb)
465 else:
443 else:
466 OldPdb.interaction(self, frame, tb_or_exc)
444 OldPdb.interaction(self, frame, tb_or_exc)
467
445
468 except KeyboardInterrupt:
446 except KeyboardInterrupt:
469 self.stdout.write("\n" + self.shell.get_exception_only())
447 self.stdout.write("\n" + self.shell.get_exception_only())
470
448
471 def precmd(self, line):
449 def precmd(self, line):
472 """Perform useful escapes on the command before it is executed."""
450 """Perform useful escapes on the command before it is executed."""
473
451
474 if line.endswith("??"):
452 if line.endswith("??"):
475 line = "pinfo2 " + line[:-2]
453 line = "pinfo2 " + line[:-2]
476 elif line.endswith("?"):
454 elif line.endswith("?"):
477 line = "pinfo " + line[:-1]
455 line = "pinfo " + line[:-1]
478
456
479 line = super().precmd(line)
457 line = super().precmd(line)
480
458
481 return line
459 return line
482
460
483 def new_do_quit(self, arg):
461 def new_do_quit(self, arg):
484 return OldPdb.do_quit(self, arg)
462 return OldPdb.do_quit(self, arg)
485
463
486 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
464 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
487
465
488 def print_stack_trace(self, context=None):
466 def print_stack_trace(self, context=None):
489 Colors = self.color_scheme_table.active_colors
467 Colors = self.color_scheme_table.active_colors
490 ColorsNormal = Colors.Normal
468 ColorsNormal = Colors.Normal
491 if context is None:
469 if context is None:
492 context = self.context
470 context = self.context
493 try:
471 try:
494 context = int(context)
472 context = int(context)
495 if context <= 0:
473 if context <= 0:
496 raise ValueError("Context must be a positive integer")
474 raise ValueError("Context must be a positive integer")
497 except (TypeError, ValueError) as e:
475 except (TypeError, ValueError) as e:
498 raise ValueError("Context must be a positive integer") from e
476 raise ValueError("Context must be a positive integer") from e
499 try:
477 try:
500 skipped = 0
478 skipped = 0
501 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
479 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
502 if hidden and self.skip_hidden:
480 if hidden and self.skip_hidden:
503 skipped += 1
481 skipped += 1
504 continue
482 continue
505 if skipped:
483 if skipped:
506 print(
484 print(
507 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
485 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
508 )
486 )
509 skipped = 0
487 skipped = 0
510 self.print_stack_entry(frame_lineno, context=context)
488 self.print_stack_entry(frame_lineno, context=context)
511 if skipped:
489 if skipped:
512 print(
490 print(
513 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
491 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
514 )
492 )
515 except KeyboardInterrupt:
493 except KeyboardInterrupt:
516 pass
494 pass
517
495
518 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
496 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
519 context=None):
497 context=None):
520 if context is None:
498 if context is None:
521 context = self.context
499 context = self.context
522 try:
500 try:
523 context = int(context)
501 context = int(context)
524 if context <= 0:
502 if context <= 0:
525 raise ValueError("Context must be a positive integer")
503 raise ValueError("Context must be a positive integer")
526 except (TypeError, ValueError) as e:
504 except (TypeError, ValueError) as e:
527 raise ValueError("Context must be a positive integer") from e
505 raise ValueError("Context must be a positive integer") from e
528 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
506 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
529
507
530 # vds: >>
508 # vds: >>
531 frame, lineno = frame_lineno
509 frame, lineno = frame_lineno
532 filename = frame.f_code.co_filename
510 filename = frame.f_code.co_filename
533 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
511 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
534 # vds: <<
512 # vds: <<
535
513
536 def _get_frame_locals(self, frame):
514 def _get_frame_locals(self, frame):
537 """ "
515 """ "
538 Accessing f_local of current frame reset the namespace, so we want to avoid
516 Accessing f_local of current frame reset the namespace, so we want to avoid
539 that or the following can happen
517 that or the following can happen
540
518
541 ipdb> foo
519 ipdb> foo
542 "old"
520 "old"
543 ipdb> foo = "new"
521 ipdb> foo = "new"
544 ipdb> foo
522 ipdb> foo
545 "new"
523 "new"
546 ipdb> where
524 ipdb> where
547 ipdb> foo
525 ipdb> foo
548 "old"
526 "old"
549
527
550 So if frame is self.current_frame we instead return self.curframe_locals
528 So if frame is self.current_frame we instead return self.curframe_locals
551
529
552 """
530 """
553 if frame is self.curframe:
531 if frame is self.curframe:
554 return self.curframe_locals
532 return self.curframe_locals
555 else:
533 else:
556 return frame.f_locals
534 return frame.f_locals
557
535
558 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
536 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
559 if context is None:
537 if context is None:
560 context = self.context
538 context = self.context
561 try:
539 try:
562 context = int(context)
540 context = int(context)
563 if context <= 0:
541 if context <= 0:
564 print("Context must be a positive integer", file=self.stdout)
542 print("Context must be a positive integer", file=self.stdout)
565 except (TypeError, ValueError):
543 except (TypeError, ValueError):
566 print("Context must be a positive integer", file=self.stdout)
544 print("Context must be a positive integer", file=self.stdout)
567
545
568 import reprlib
546 import reprlib
569
547
570 ret = []
548 ret = []
571
549
572 Colors = self.color_scheme_table.active_colors
550 Colors = self.color_scheme_table.active_colors
573 ColorsNormal = Colors.Normal
551 ColorsNormal = Colors.Normal
574 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
552 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
575 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
553 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
576 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
554 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
577 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
555 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
578
556
579 frame, lineno = frame_lineno
557 frame, lineno = frame_lineno
580
558
581 return_value = ''
559 return_value = ''
582 loc_frame = self._get_frame_locals(frame)
560 loc_frame = self._get_frame_locals(frame)
583 if "__return__" in loc_frame:
561 if "__return__" in loc_frame:
584 rv = loc_frame["__return__"]
562 rv = loc_frame["__return__"]
585 # return_value += '->'
563 # return_value += '->'
586 return_value += reprlib.repr(rv) + "\n"
564 return_value += reprlib.repr(rv) + "\n"
587 ret.append(return_value)
565 ret.append(return_value)
588
566
589 #s = filename + '(' + `lineno` + ')'
567 #s = filename + '(' + `lineno` + ')'
590 filename = self.canonic(frame.f_code.co_filename)
568 filename = self.canonic(frame.f_code.co_filename)
591 link = tpl_link % py3compat.cast_unicode(filename)
569 link = tpl_link % py3compat.cast_unicode(filename)
592
570
593 if frame.f_code.co_name:
571 if frame.f_code.co_name:
594 func = frame.f_code.co_name
572 func = frame.f_code.co_name
595 else:
573 else:
596 func = "<lambda>"
574 func = "<lambda>"
597
575
598 call = ""
576 call = ""
599 if func != "?":
577 if func != "?":
600 if "__args__" in loc_frame:
578 if "__args__" in loc_frame:
601 args = reprlib.repr(loc_frame["__args__"])
579 args = reprlib.repr(loc_frame["__args__"])
602 else:
580 else:
603 args = '()'
581 args = '()'
604 call = tpl_call % (func, args)
582 call = tpl_call % (func, args)
605
583
606 # The level info should be generated in the same format pdb uses, to
584 # The level info should be generated in the same format pdb uses, to
607 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
585 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
608 if frame is self.curframe:
586 if frame is self.curframe:
609 ret.append('> ')
587 ret.append('> ')
610 else:
588 else:
611 ret.append(" ")
589 ret.append(" ")
612 ret.append("%s(%s)%s\n" % (link, lineno, call))
590 ret.append("%s(%s)%s\n" % (link, lineno, call))
613
591
614 start = lineno - 1 - context//2
592 start = lineno - 1 - context//2
615 lines = linecache.getlines(filename)
593 lines = linecache.getlines(filename)
616 start = min(start, len(lines) - context)
594 start = min(start, len(lines) - context)
617 start = max(start, 0)
595 start = max(start, 0)
618 lines = lines[start : start + context]
596 lines = lines[start : start + context]
619
597
620 for i, line in enumerate(lines):
598 for i, line in enumerate(lines):
621 show_arrow = start + 1 + i == lineno
599 show_arrow = start + 1 + i == lineno
622 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
600 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
623 ret.append(
601 ret.append(
624 self.__format_line(
602 self.__format_line(
625 linetpl, filename, start + 1 + i, line, arrow=show_arrow
603 linetpl, filename, start + 1 + i, line, arrow=show_arrow
626 )
604 )
627 )
605 )
628 return "".join(ret)
606 return "".join(ret)
629
607
630 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
608 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
631 bp_mark = ""
609 bp_mark = ""
632 bp_mark_color = ""
610 bp_mark_color = ""
633
611
634 new_line, err = self.parser.format2(line, 'str')
612 new_line, err = self.parser.format2(line, 'str')
635 if not err:
613 if not err:
636 line = new_line
614 line = new_line
637
615
638 bp = None
616 bp = None
639 if lineno in self.get_file_breaks(filename):
617 if lineno in self.get_file_breaks(filename):
640 bps = self.get_breaks(filename, lineno)
618 bps = self.get_breaks(filename, lineno)
641 bp = bps[-1]
619 bp = bps[-1]
642
620
643 if bp:
621 if bp:
644 Colors = self.color_scheme_table.active_colors
622 Colors = self.color_scheme_table.active_colors
645 bp_mark = str(bp.number)
623 bp_mark = str(bp.number)
646 bp_mark_color = Colors.breakpoint_enabled
624 bp_mark_color = Colors.breakpoint_enabled
647 if not bp.enabled:
625 if not bp.enabled:
648 bp_mark_color = Colors.breakpoint_disabled
626 bp_mark_color = Colors.breakpoint_disabled
649
627
650 numbers_width = 7
628 numbers_width = 7
651 if arrow:
629 if arrow:
652 # This is the line with the error
630 # This is the line with the error
653 pad = numbers_width - len(str(lineno)) - len(bp_mark)
631 pad = numbers_width - len(str(lineno)) - len(bp_mark)
654 num = '%s%s' % (make_arrow(pad), str(lineno))
632 num = '%s%s' % (make_arrow(pad), str(lineno))
655 else:
633 else:
656 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
634 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
657
635
658 return tpl_line % (bp_mark_color + bp_mark, num, line)
636 return tpl_line % (bp_mark_color + bp_mark, num, line)
659
637
660 def print_list_lines(self, filename, first, last):
638 def print_list_lines(self, filename, first, last):
661 """The printing (as opposed to the parsing part of a 'list'
639 """The printing (as opposed to the parsing part of a 'list'
662 command."""
640 command."""
663 try:
641 try:
664 Colors = self.color_scheme_table.active_colors
642 Colors = self.color_scheme_table.active_colors
665 ColorsNormal = Colors.Normal
643 ColorsNormal = Colors.Normal
666 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
644 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
667 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
645 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
668 src = []
646 src = []
669 if filename == "<string>" and hasattr(self, "_exec_filename"):
647 if filename == "<string>" and hasattr(self, "_exec_filename"):
670 filename = self._exec_filename
648 filename = self._exec_filename
671
649
672 for lineno in range(first, last+1):
650 for lineno in range(first, last+1):
673 line = linecache.getline(filename, lineno)
651 line = linecache.getline(filename, lineno)
674 if not line:
652 if not line:
675 break
653 break
676
654
677 if lineno == self.curframe.f_lineno:
655 if lineno == self.curframe.f_lineno:
678 line = self.__format_line(
656 line = self.__format_line(
679 tpl_line_em, filename, lineno, line, arrow=True
657 tpl_line_em, filename, lineno, line, arrow=True
680 )
658 )
681 else:
659 else:
682 line = self.__format_line(
660 line = self.__format_line(
683 tpl_line, filename, lineno, line, arrow=False
661 tpl_line, filename, lineno, line, arrow=False
684 )
662 )
685
663
686 src.append(line)
664 src.append(line)
687 self.lineno = lineno
665 self.lineno = lineno
688
666
689 print(''.join(src), file=self.stdout)
667 print(''.join(src), file=self.stdout)
690
668
691 except KeyboardInterrupt:
669 except KeyboardInterrupt:
692 pass
670 pass
693
671
694 def do_skip_predicates(self, args):
672 def do_skip_predicates(self, args):
695 """
673 """
696 Turn on/off individual predicates as to whether a frame should be hidden/skip.
674 Turn on/off individual predicates as to whether a frame should be hidden/skip.
697
675
698 The global option to skip (or not) hidden frames is set with skip_hidden
676 The global option to skip (or not) hidden frames is set with skip_hidden
699
677
700 To change the value of a predicate
678 To change the value of a predicate
701
679
702 skip_predicates key [true|false]
680 skip_predicates key [true|false]
703
681
704 Call without arguments to see the current values.
682 Call without arguments to see the current values.
705
683
706 To permanently change the value of an option add the corresponding
684 To permanently change the value of an option add the corresponding
707 command to your ``~/.pdbrc`` file. If you are programmatically using the
685 command to your ``~/.pdbrc`` file. If you are programmatically using the
708 Pdb instance you can also change the ``default_predicates`` class
686 Pdb instance you can also change the ``default_predicates`` class
709 attribute.
687 attribute.
710 """
688 """
711 if not args.strip():
689 if not args.strip():
712 print("current predicates:")
690 print("current predicates:")
713 for p, v in self._predicates.items():
691 for p, v in self._predicates.items():
714 print(" ", p, ":", v)
692 print(" ", p, ":", v)
715 return
693 return
716 type_value = args.strip().split(" ")
694 type_value = args.strip().split(" ")
717 if len(type_value) != 2:
695 if len(type_value) != 2:
718 print(
696 print(
719 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
697 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
720 )
698 )
721 return
699 return
722
700
723 type_, value = type_value
701 type_, value = type_value
724 if type_ not in self._predicates:
702 if type_ not in self._predicates:
725 print(f"{type_!r} not in {set(self._predicates.keys())}")
703 print(f"{type_!r} not in {set(self._predicates.keys())}")
726 return
704 return
727 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
705 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
728 print(
706 print(
729 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
707 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
730 )
708 )
731 return
709 return
732
710
733 self._predicates[type_] = value.lower() in ("true", "yes", "1")
711 self._predicates[type_] = value.lower() in ("true", "yes", "1")
734 if not any(self._predicates.values()):
712 if not any(self._predicates.values()):
735 print(
713 print(
736 "Warning, all predicates set to False, skip_hidden may not have any effects."
714 "Warning, all predicates set to False, skip_hidden may not have any effects."
737 )
715 )
738
716
739 def do_skip_hidden(self, arg):
717 def do_skip_hidden(self, arg):
740 """
718 """
741 Change whether or not we should skip frames with the
719 Change whether or not we should skip frames with the
742 __tracebackhide__ attribute.
720 __tracebackhide__ attribute.
743 """
721 """
744 if not arg.strip():
722 if not arg.strip():
745 print(
723 print(
746 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
724 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
747 )
725 )
748 elif arg.strip().lower() in ("true", "yes"):
726 elif arg.strip().lower() in ("true", "yes"):
749 self.skip_hidden = True
727 self.skip_hidden = True
750 elif arg.strip().lower() in ("false", "no"):
728 elif arg.strip().lower() in ("false", "no"):
751 self.skip_hidden = False
729 self.skip_hidden = False
752 if not any(self._predicates.values()):
730 if not any(self._predicates.values()):
753 print(
731 print(
754 "Warning, all predicates set to False, skip_hidden may not have any effects."
732 "Warning, all predicates set to False, skip_hidden may not have any effects."
755 )
733 )
756
734
757 def do_list(self, arg):
735 def do_list(self, arg):
758 """Print lines of code from the current stack frame
736 """Print lines of code from the current stack frame
759 """
737 """
760 self.lastcmd = 'list'
738 self.lastcmd = 'list'
761 last = None
739 last = None
762 if arg and arg != ".":
740 if arg and arg != ".":
763 try:
741 try:
764 x = eval(arg, {}, {})
742 x = eval(arg, {}, {})
765 if type(x) == type(()):
743 if type(x) == type(()):
766 first, last = x
744 first, last = x
767 first = int(first)
745 first = int(first)
768 last = int(last)
746 last = int(last)
769 if last < first:
747 if last < first:
770 # Assume it's a count
748 # Assume it's a count
771 last = first + last
749 last = first + last
772 else:
750 else:
773 first = max(1, int(x) - 5)
751 first = max(1, int(x) - 5)
774 except:
752 except:
775 print('*** Error in argument:', repr(arg), file=self.stdout)
753 print('*** Error in argument:', repr(arg), file=self.stdout)
776 return
754 return
777 elif self.lineno is None or arg == ".":
755 elif self.lineno is None or arg == ".":
778 first = max(1, self.curframe.f_lineno - 5)
756 first = max(1, self.curframe.f_lineno - 5)
779 else:
757 else:
780 first = self.lineno + 1
758 first = self.lineno + 1
781 if last is None:
759 if last is None:
782 last = first + 10
760 last = first + 10
783 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
761 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
784
762
785 # vds: >>
763 # vds: >>
786 lineno = first
764 lineno = first
787 filename = self.curframe.f_code.co_filename
765 filename = self.curframe.f_code.co_filename
788 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
766 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
789 # vds: <<
767 # vds: <<
790
768
791 do_l = do_list
769 do_l = do_list
792
770
793 def getsourcelines(self, obj):
771 def getsourcelines(self, obj):
794 lines, lineno = inspect.findsource(obj)
772 lines, lineno = inspect.findsource(obj)
795 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
773 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
796 # must be a module frame: do not try to cut a block out of it
774 # must be a module frame: do not try to cut a block out of it
797 return lines, 1
775 return lines, 1
798 elif inspect.ismodule(obj):
776 elif inspect.ismodule(obj):
799 return lines, 1
777 return lines, 1
800 return inspect.getblock(lines[lineno:]), lineno+1
778 return inspect.getblock(lines[lineno:]), lineno+1
801
779
802 def do_longlist(self, arg):
780 def do_longlist(self, arg):
803 """Print lines of code from the current stack frame.
781 """Print lines of code from the current stack frame.
804
782
805 Shows more lines than 'list' does.
783 Shows more lines than 'list' does.
806 """
784 """
807 self.lastcmd = 'longlist'
785 self.lastcmd = 'longlist'
808 try:
786 try:
809 lines, lineno = self.getsourcelines(self.curframe)
787 lines, lineno = self.getsourcelines(self.curframe)
810 except OSError as err:
788 except OSError as err:
811 self.error(err)
789 self.error(err)
812 return
790 return
813 last = lineno + len(lines)
791 last = lineno + len(lines)
814 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
792 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
815 do_ll = do_longlist
793 do_ll = do_longlist
816
794
817 def do_debug(self, arg):
795 def do_debug(self, arg):
818 """debug code
796 """debug code
819 Enter a recursive debugger that steps through the code
797 Enter a recursive debugger that steps through the code
820 argument (which is an arbitrary expression or statement to be
798 argument (which is an arbitrary expression or statement to be
821 executed in the current environment).
799 executed in the current environment).
822 """
800 """
823 trace_function = sys.gettrace()
801 trace_function = sys.gettrace()
824 sys.settrace(None)
802 sys.settrace(None)
825 globals = self.curframe.f_globals
803 globals = self.curframe.f_globals
826 locals = self.curframe_locals
804 locals = self.curframe_locals
827 p = self.__class__(completekey=self.completekey,
805 p = self.__class__(completekey=self.completekey,
828 stdin=self.stdin, stdout=self.stdout)
806 stdin=self.stdin, stdout=self.stdout)
829 p.use_rawinput = self.use_rawinput
807 p.use_rawinput = self.use_rawinput
830 p.prompt = "(%s) " % self.prompt.strip()
808 p.prompt = "(%s) " % self.prompt.strip()
831 self.message("ENTERING RECURSIVE DEBUGGER")
809 self.message("ENTERING RECURSIVE DEBUGGER")
832 sys.call_tracing(p.run, (arg, globals, locals))
810 sys.call_tracing(p.run, (arg, globals, locals))
833 self.message("LEAVING RECURSIVE DEBUGGER")
811 self.message("LEAVING RECURSIVE DEBUGGER")
834 sys.settrace(trace_function)
812 sys.settrace(trace_function)
835 self.lastcmd = p.lastcmd
813 self.lastcmd = p.lastcmd
836
814
837 def do_pdef(self, arg):
815 def do_pdef(self, arg):
838 """Print the call signature for any callable object.
816 """Print the call signature for any callable object.
839
817
840 The debugger interface to %pdef"""
818 The debugger interface to %pdef"""
841 namespaces = [
819 namespaces = [
842 ("Locals", self.curframe_locals),
820 ("Locals", self.curframe_locals),
843 ("Globals", self.curframe.f_globals),
821 ("Globals", self.curframe.f_globals),
844 ]
822 ]
845 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
823 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
846
824
847 def do_pdoc(self, arg):
825 def do_pdoc(self, arg):
848 """Print the docstring for an object.
826 """Print the docstring for an object.
849
827
850 The debugger interface to %pdoc."""
828 The debugger interface to %pdoc."""
851 namespaces = [
829 namespaces = [
852 ("Locals", self.curframe_locals),
830 ("Locals", self.curframe_locals),
853 ("Globals", self.curframe.f_globals),
831 ("Globals", self.curframe.f_globals),
854 ]
832 ]
855 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
833 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
856
834
857 def do_pfile(self, arg):
835 def do_pfile(self, arg):
858 """Print (or run through pager) the file where an object is defined.
836 """Print (or run through pager) the file where an object is defined.
859
837
860 The debugger interface to %pfile.
838 The debugger interface to %pfile.
861 """
839 """
862 namespaces = [
840 namespaces = [
863 ("Locals", self.curframe_locals),
841 ("Locals", self.curframe_locals),
864 ("Globals", self.curframe.f_globals),
842 ("Globals", self.curframe.f_globals),
865 ]
843 ]
866 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
844 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
867
845
868 def do_pinfo(self, arg):
846 def do_pinfo(self, arg):
869 """Provide detailed information about an object.
847 """Provide detailed information about an object.
870
848
871 The debugger interface to %pinfo, i.e., obj?."""
849 The debugger interface to %pinfo, i.e., obj?."""
872 namespaces = [
850 namespaces = [
873 ("Locals", self.curframe_locals),
851 ("Locals", self.curframe_locals),
874 ("Globals", self.curframe.f_globals),
852 ("Globals", self.curframe.f_globals),
875 ]
853 ]
876 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
854 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
877
855
878 def do_pinfo2(self, arg):
856 def do_pinfo2(self, arg):
879 """Provide extra detailed information about an object.
857 """Provide extra detailed information about an object.
880
858
881 The debugger interface to %pinfo2, i.e., obj??."""
859 The debugger interface to %pinfo2, i.e., obj??."""
882 namespaces = [
860 namespaces = [
883 ("Locals", self.curframe_locals),
861 ("Locals", self.curframe_locals),
884 ("Globals", self.curframe.f_globals),
862 ("Globals", self.curframe.f_globals),
885 ]
863 ]
886 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
864 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
887
865
888 def do_psource(self, arg):
866 def do_psource(self, arg):
889 """Print (or run through pager) the source code for an object."""
867 """Print (or run through pager) the source code for an object."""
890 namespaces = [
868 namespaces = [
891 ("Locals", self.curframe_locals),
869 ("Locals", self.curframe_locals),
892 ("Globals", self.curframe.f_globals),
870 ("Globals", self.curframe.f_globals),
893 ]
871 ]
894 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
872 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
895
873
896 def do_where(self, arg):
874 def do_where(self, arg):
897 """w(here)
875 """w(here)
898 Print a stack trace, with the most recent frame at the bottom.
876 Print a stack trace, with the most recent frame at the bottom.
899 An arrow indicates the "current frame", which determines the
877 An arrow indicates the "current frame", which determines the
900 context of most commands. 'bt' is an alias for this command.
878 context of most commands. 'bt' is an alias for this command.
901
879
902 Take a number as argument as an (optional) number of context line to
880 Take a number as argument as an (optional) number of context line to
903 print"""
881 print"""
904 if arg:
882 if arg:
905 try:
883 try:
906 context = int(arg)
884 context = int(arg)
907 except ValueError as err:
885 except ValueError as err:
908 self.error(err)
886 self.error(err)
909 return
887 return
910 self.print_stack_trace(context)
888 self.print_stack_trace(context)
911 else:
889 else:
912 self.print_stack_trace()
890 self.print_stack_trace()
913
891
914 do_w = do_where
892 do_w = do_where
915
893
916 def break_anywhere(self, frame):
894 def break_anywhere(self, frame):
917 """
895 """
918 _stop_in_decorator_internals is overly restrictive, as we may still want
896 _stop_in_decorator_internals is overly restrictive, as we may still want
919 to trace function calls, so we need to also update break_anywhere so
897 to trace function calls, so we need to also update break_anywhere so
920 that is we don't `stop_here`, because of debugger skip, we may still
898 that is we don't `stop_here`, because of debugger skip, we may still
921 stop at any point inside the function
899 stop at any point inside the function
922
900
923 """
901 """
924
902
925 sup = super().break_anywhere(frame)
903 sup = super().break_anywhere(frame)
926 if sup:
904 if sup:
927 return sup
905 return sup
928 if self._predicates["debuggerskip"]:
906 if self._predicates["debuggerskip"]:
929 if DEBUGGERSKIP in frame.f_code.co_varnames:
907 if DEBUGGERSKIP in frame.f_code.co_varnames:
930 return True
908 return True
931 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
909 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
932 return True
910 return True
933 return False
911 return False
934
912
935 def _is_in_decorator_internal_and_should_skip(self, frame):
913 def _is_in_decorator_internal_and_should_skip(self, frame):
936 """
914 """
937 Utility to tell us whether we are in a decorator internal and should stop.
915 Utility to tell us whether we are in a decorator internal and should stop.
938
916
939 """
917 """
940 # if we are disabled don't skip
918 # if we are disabled don't skip
941 if not self._predicates["debuggerskip"]:
919 if not self._predicates["debuggerskip"]:
942 return False
920 return False
943
921
944 return self._cachable_skip(frame)
922 return self._cachable_skip(frame)
945
923
946 @lru_cache
924 @lru_cache
947 def _cachable_skip(self, frame):
925 def _cachable_skip(self, frame):
948 # if frame is tagged, skip by default.
926 # if frame is tagged, skip by default.
949 if DEBUGGERSKIP in frame.f_code.co_varnames:
927 if DEBUGGERSKIP in frame.f_code.co_varnames:
950 return True
928 return True
951
929
952 # if one of the parent frame value set to True skip as well.
930 # if one of the parent frame value set to True skip as well.
953
931
954 cframe = frame
932 cframe = frame
955 while getattr(cframe, "f_back", None):
933 while getattr(cframe, "f_back", None):
956 cframe = cframe.f_back
934 cframe = cframe.f_back
957 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
935 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
958 return True
936 return True
959
937
960 return False
938 return False
961
939
962 def stop_here(self, frame):
940 def stop_here(self, frame):
963 if self._is_in_decorator_internal_and_should_skip(frame) is True:
941 if self._is_in_decorator_internal_and_should_skip(frame) is True:
964 return False
942 return False
965
943
966 hidden = False
944 hidden = False
967 if self.skip_hidden:
945 if self.skip_hidden:
968 hidden = self._hidden_predicate(frame)
946 hidden = self._hidden_predicate(frame)
969 if hidden:
947 if hidden:
970 if self.report_skipped:
948 if self.report_skipped:
971 Colors = self.color_scheme_table.active_colors
949 Colors = self.color_scheme_table.active_colors
972 ColorsNormal = Colors.Normal
950 ColorsNormal = Colors.Normal
973 print(
951 print(
974 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
952 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
975 )
953 )
976 return super().stop_here(frame)
954 return super().stop_here(frame)
977
955
978 def do_up(self, arg):
956 def do_up(self, arg):
979 """u(p) [count]
957 """u(p) [count]
980 Move the current frame count (default one) levels up in the
958 Move the current frame count (default one) levels up in the
981 stack trace (to an older frame).
959 stack trace (to an older frame).
982
960
983 Will skip hidden frames.
961 Will skip hidden frames.
984 """
962 """
985 # modified version of upstream that skips
963 # modified version of upstream that skips
986 # frames with __tracebackhide__
964 # frames with __tracebackhide__
987 if self.curindex == 0:
965 if self.curindex == 0:
988 self.error("Oldest frame")
966 self.error("Oldest frame")
989 return
967 return
990 try:
968 try:
991 count = int(arg or 1)
969 count = int(arg or 1)
992 except ValueError:
970 except ValueError:
993 self.error("Invalid frame count (%s)" % arg)
971 self.error("Invalid frame count (%s)" % arg)
994 return
972 return
995 skipped = 0
973 skipped = 0
996 if count < 0:
974 if count < 0:
997 _newframe = 0
975 _newframe = 0
998 else:
976 else:
999 counter = 0
977 counter = 0
1000 hidden_frames = self.hidden_frames(self.stack)
978 hidden_frames = self.hidden_frames(self.stack)
1001 for i in range(self.curindex - 1, -1, -1):
979 for i in range(self.curindex - 1, -1, -1):
1002 if hidden_frames[i] and self.skip_hidden:
980 if hidden_frames[i] and self.skip_hidden:
1003 skipped += 1
981 skipped += 1
1004 continue
982 continue
1005 counter += 1
983 counter += 1
1006 if counter >= count:
984 if counter >= count:
1007 break
985 break
1008 else:
986 else:
1009 # if no break occurred.
987 # if no break occurred.
1010 self.error(
988 self.error(
1011 "all frames above hidden, use `skip_hidden False` to get get into those."
989 "all frames above hidden, use `skip_hidden False` to get get into those."
1012 )
990 )
1013 return
991 return
1014
992
1015 Colors = self.color_scheme_table.active_colors
993 Colors = self.color_scheme_table.active_colors
1016 ColorsNormal = Colors.Normal
994 ColorsNormal = Colors.Normal
1017 _newframe = i
995 _newframe = i
1018 self._select_frame(_newframe)
996 self._select_frame(_newframe)
1019 if skipped:
997 if skipped:
1020 print(
998 print(
1021 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
999 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1022 )
1000 )
1023
1001
1024 def do_down(self, arg):
1002 def do_down(self, arg):
1025 """d(own) [count]
1003 """d(own) [count]
1026 Move the current frame count (default one) levels down in the
1004 Move the current frame count (default one) levels down in the
1027 stack trace (to a newer frame).
1005 stack trace (to a newer frame).
1028
1006
1029 Will skip hidden frames.
1007 Will skip hidden frames.
1030 """
1008 """
1031 if self.curindex + 1 == len(self.stack):
1009 if self.curindex + 1 == len(self.stack):
1032 self.error("Newest frame")
1010 self.error("Newest frame")
1033 return
1011 return
1034 try:
1012 try:
1035 count = int(arg or 1)
1013 count = int(arg or 1)
1036 except ValueError:
1014 except ValueError:
1037 self.error("Invalid frame count (%s)" % arg)
1015 self.error("Invalid frame count (%s)" % arg)
1038 return
1016 return
1039 if count < 0:
1017 if count < 0:
1040 _newframe = len(self.stack) - 1
1018 _newframe = len(self.stack) - 1
1041 else:
1019 else:
1042 counter = 0
1020 counter = 0
1043 skipped = 0
1021 skipped = 0
1044 hidden_frames = self.hidden_frames(self.stack)
1022 hidden_frames = self.hidden_frames(self.stack)
1045 for i in range(self.curindex + 1, len(self.stack)):
1023 for i in range(self.curindex + 1, len(self.stack)):
1046 if hidden_frames[i] and self.skip_hidden:
1024 if hidden_frames[i] and self.skip_hidden:
1047 skipped += 1
1025 skipped += 1
1048 continue
1026 continue
1049 counter += 1
1027 counter += 1
1050 if counter >= count:
1028 if counter >= count:
1051 break
1029 break
1052 else:
1030 else:
1053 self.error(
1031 self.error(
1054 "all frames below hidden, use `skip_hidden False` to get get into those."
1032 "all frames below hidden, use `skip_hidden False` to get get into those."
1055 )
1033 )
1056 return
1034 return
1057
1035
1058 Colors = self.color_scheme_table.active_colors
1036 Colors = self.color_scheme_table.active_colors
1059 ColorsNormal = Colors.Normal
1037 ColorsNormal = Colors.Normal
1060 if skipped:
1038 if skipped:
1061 print(
1039 print(
1062 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1040 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1063 )
1041 )
1064 _newframe = i
1042 _newframe = i
1065
1043
1066 self._select_frame(_newframe)
1044 self._select_frame(_newframe)
1067
1045
1068 do_d = do_down
1046 do_d = do_down
1069 do_u = do_up
1047 do_u = do_up
1070
1048
1071 def do_context(self, context):
1049 def do_context(self, context):
1072 """context number_of_lines
1050 """context number_of_lines
1073 Set the number of lines of source code to show when displaying
1051 Set the number of lines of source code to show when displaying
1074 stacktrace information.
1052 stacktrace information.
1075 """
1053 """
1076 try:
1054 try:
1077 new_context = int(context)
1055 new_context = int(context)
1078 if new_context <= 0:
1056 if new_context <= 0:
1079 raise ValueError()
1057 raise ValueError()
1080 self.context = new_context
1058 self.context = new_context
1081 except ValueError:
1059 except ValueError:
1082 self.error("The 'context' command requires a positive integer argument.")
1060 self.error("The 'context' command requires a positive integer argument.")
1083
1061
1084
1062
1085 class InterruptiblePdb(Pdb):
1063 class InterruptiblePdb(Pdb):
1086 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1064 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1087
1065
1088 def cmdloop(self, intro=None):
1066 def cmdloop(self, intro=None):
1089 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1067 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1090 try:
1068 try:
1091 return OldPdb.cmdloop(self, intro=intro)
1069 return OldPdb.cmdloop(self, intro=intro)
1092 except KeyboardInterrupt:
1070 except KeyboardInterrupt:
1093 self.stop_here = lambda frame: False
1071 self.stop_here = lambda frame: False
1094 self.do_quit("")
1072 self.do_quit("")
1095 sys.settrace(None)
1073 sys.settrace(None)
1096 self.quitting = False
1074 self.quitting = False
1097 raise
1075 raise
1098
1076
1099 def _cmdloop(self):
1077 def _cmdloop(self):
1100 while True:
1078 while True:
1101 try:
1079 try:
1102 # keyboard interrupts allow for an easy way to cancel
1080 # keyboard interrupts allow for an easy way to cancel
1103 # the current command, so allow them during interactive input
1081 # the current command, so allow them during interactive input
1104 self.allow_kbdint = True
1082 self.allow_kbdint = True
1105 self.cmdloop()
1083 self.cmdloop()
1106 self.allow_kbdint = False
1084 self.allow_kbdint = False
1107 break
1085 break
1108 except KeyboardInterrupt:
1086 except KeyboardInterrupt:
1109 self.message('--KeyboardInterrupt--')
1087 self.message('--KeyboardInterrupt--')
1110 raise
1088 raise
1111
1089
1112
1090
1113 def set_trace(frame=None, header=None):
1091 def set_trace(frame=None, header=None):
1114 """
1092 """
1115 Start debugging from `frame`.
1093 Start debugging from `frame`.
1116
1094
1117 If frame is not specified, debugging starts from caller's frame.
1095 If frame is not specified, debugging starts from caller's frame.
1118 """
1096 """
1119 pdb = Pdb()
1097 pdb = Pdb()
1120 if header is not None:
1098 if header is not None:
1121 pdb.message(header)
1099 pdb.message(header)
1122 pdb.set_trace(frame or sys._getframe().f_back)
1100 pdb.set_trace(frame or sys._getframe().f_back)
@@ -1,175 +1,192 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Color schemes for exception handling code in IPython.
3 Color schemes for exception handling code in IPython.
4 """
4 """
5
5
6 import os
6 import os
7
7
8 #*****************************************************************************
8 #*****************************************************************************
9 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
9 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
13 #*****************************************************************************
14
14
15 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
15 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
16
16
17 def exception_colors():
17 def exception_colors():
18 """Return a color table with fields for exception reporting.
18 """Return a color table with fields for exception reporting.
19
19
20 The table is an instance of ColorSchemeTable with schemes added for
20 The table is an instance of ColorSchemeTable with schemes added for
21 'Neutral', 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
21 'Neutral', 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
22 in.
22 in.
23
23
24 Examples:
24 Examples:
25
25
26 >>> ec = exception_colors()
26 >>> ec = exception_colors()
27 >>> ec.active_scheme_name
27 >>> ec.active_scheme_name
28 ''
28 ''
29 >>> print(ec.active_colors)
29 >>> print(ec.active_colors)
30 None
30 None
31
31
32 Now we activate a color scheme:
32 Now we activate a color scheme:
33 >>> ec.set_active_scheme('NoColor')
33 >>> ec.set_active_scheme('NoColor')
34 >>> ec.active_scheme_name
34 >>> ec.active_scheme_name
35 'NoColor'
35 'NoColor'
36 >>> sorted(ec.active_colors.keys())
36 >>> sorted(ec.active_colors.keys())
37 ['Normal', 'caret', 'em', 'excName', 'filename', 'filenameEm', 'line',
37 ['Normal', 'breakpoint_disabled', 'breakpoint_enabled', 'caret', 'em',
38 'lineno', 'linenoEm', 'name', 'nameEm', 'normalEm', 'topline', 'vName',
38 'excName', 'filename', 'filenameEm', 'line', 'lineno', 'linenoEm', 'name',
39 'val', 'valEm']
39 'nameEm', 'normalEm', 'prompt', 'topline', 'vName', 'val', 'valEm']
40
40 """
41 """
41
42
42 ex_colors = ColorSchemeTable()
43 ex_colors = ColorSchemeTable()
43
44
44 # Populate it with color schemes
45 # Populate it with color schemes
45 C = TermColors # shorthand and local lookup
46 C = TermColors # shorthand and local lookup
46 ex_colors.add_scheme(
47 ex_colors.add_scheme(
47 ColorScheme(
48 ColorScheme(
48 "NoColor",
49 "NoColor",
49 {
50 {
50 # The color to be used for the top line
51 # The color to be used for the top line
51 "topline": C.NoColor,
52 "topline": C.NoColor,
52
53
53 # The colors to be used in the traceback
54 # The colors to be used in the traceback
54 "filename": C.NoColor,
55 "filename": C.NoColor,
55 "lineno": C.NoColor,
56 "lineno": C.NoColor,
56 "name": C.NoColor,
57 "name": C.NoColor,
57 "vName": C.NoColor,
58 "vName": C.NoColor,
58 "val": C.NoColor,
59 "val": C.NoColor,
59 "em": C.NoColor,
60 "em": C.NoColor,
60
61
61 # Emphasized colors for the last frame of the traceback
62 # Emphasized colors for the last frame of the traceback
62 "normalEm": C.NoColor,
63 "normalEm": C.NoColor,
63 "filenameEm": C.NoColor,
64 "filenameEm": C.NoColor,
64 "linenoEm": C.NoColor,
65 "linenoEm": C.NoColor,
65 "nameEm": C.NoColor,
66 "nameEm": C.NoColor,
66 "valEm": C.NoColor,
67 "valEm": C.NoColor,
67
68
68 # Colors for printing the exception
69 # Colors for printing the exception
69 "excName": C.NoColor,
70 "excName": C.NoColor,
70 "line": C.NoColor,
71 "line": C.NoColor,
71 "caret": C.NoColor,
72 "caret": C.NoColor,
72 "Normal": C.NoColor,
73 "Normal": C.NoColor,
74 # debugger
75 "prompt": C.NoColor,
76 "breakpoint_enabled": C.NoColor,
77 "breakpoint_disabled": C.NoColor,
73 },
78 },
74 )
79 )
75 )
80 )
76
81
77 # make some schemes as instances so we can copy them for modification easily
82 # make some schemes as instances so we can copy them for modification easily
78 ex_colors.add_scheme(
83 ex_colors.add_scheme(
79 ColorScheme(
84 ColorScheme(
80 "Linux",
85 "Linux",
81 {
86 {
82 # The color to be used for the top line
87 # The color to be used for the top line
83 "topline": C.LightRed,
88 "topline": C.LightRed,
84 # The colors to be used in the traceback
89 # The colors to be used in the traceback
85 "filename": C.Green,
90 "filename": C.Green,
86 "lineno": C.Green,
91 "lineno": C.Green,
87 "name": C.Purple,
92 "name": C.Purple,
88 "vName": C.Cyan,
93 "vName": C.Cyan,
89 "val": C.Green,
94 "val": C.Green,
90 "em": C.LightCyan,
95 "em": C.LightCyan,
91 # Emphasized colors for the last frame of the traceback
96 # Emphasized colors for the last frame of the traceback
92 "normalEm": C.LightCyan,
97 "normalEm": C.LightCyan,
93 "filenameEm": C.LightGreen,
98 "filenameEm": C.LightGreen,
94 "linenoEm": C.LightGreen,
99 "linenoEm": C.LightGreen,
95 "nameEm": C.LightPurple,
100 "nameEm": C.LightPurple,
96 "valEm": C.LightBlue,
101 "valEm": C.LightBlue,
97 # Colors for printing the exception
102 # Colors for printing the exception
98 "excName": C.LightRed,
103 "excName": C.LightRed,
99 "line": C.Yellow,
104 "line": C.Yellow,
100 "caret": C.White,
105 "caret": C.White,
101 "Normal": C.Normal,
106 "Normal": C.Normal,
107 # debugger
108 "prompt": C.Green,
109 "breakpoint_enabled": C.LightRed,
110 "breakpoint_disabled": C.Red,
102 },
111 },
103 )
112 )
104 )
113 )
105
114
106 # For light backgrounds, swap dark/light colors
115 # For light backgrounds, swap dark/light colors
107 ex_colors.add_scheme(
116 ex_colors.add_scheme(
108 ColorScheme(
117 ColorScheme(
109 "LightBG",
118 "LightBG",
110 {
119 {
111 # The color to be used for the top line
120 # The color to be used for the top line
112 "topline": C.Red,
121 "topline": C.Red,
113
122
114 # The colors to be used in the traceback
123 # The colors to be used in the traceback
115 "filename": C.LightGreen,
124 "filename": C.LightGreen,
116 "lineno": C.LightGreen,
125 "lineno": C.LightGreen,
117 "name": C.LightPurple,
126 "name": C.LightPurple,
118 "vName": C.Cyan,
127 "vName": C.Cyan,
119 "val": C.LightGreen,
128 "val": C.LightGreen,
120 "em": C.Cyan,
129 "em": C.Cyan,
121
130
122 # Emphasized colors for the last frame of the traceback
131 # Emphasized colors for the last frame of the traceback
123 "normalEm": C.Cyan,
132 "normalEm": C.Cyan,
124 "filenameEm": C.Green,
133 "filenameEm": C.Green,
125 "linenoEm": C.Green,
134 "linenoEm": C.Green,
126 "nameEm": C.Purple,
135 "nameEm": C.Purple,
127 "valEm": C.Blue,
136 "valEm": C.Blue,
128
137
129 # Colors for printing the exception
138 # Colors for printing the exception
130 "excName": C.Red,
139 "excName": C.Red,
131 # "line": C.Brown, # brown often is displayed as yellow
140 # "line": C.Brown, # brown often is displayed as yellow
132 "line": C.Red,
141 "line": C.Red,
133 "caret": C.Normal,
142 "caret": C.Normal,
134 "Normal": C.Normal,
143 "Normal": C.Normal,
144 # debugger
145 "prompt": C.Blue,
146 "breakpoint_enabled": C.LightRed,
147 "breakpoint_disabled": C.Red,
135 },
148 },
136 )
149 )
137 )
150 )
138
151
139 ex_colors.add_scheme(
152 ex_colors.add_scheme(
140 ColorScheme(
153 ColorScheme(
141 "Neutral",
154 "Neutral",
142 {
155 {
143 # The color to be used for the top line
156 # The color to be used for the top line
144 "topline": C.Red,
157 "topline": C.Red,
145 # The colors to be used in the traceback
158 # The colors to be used in the traceback
146 "filename": C.LightGreen,
159 "filename": C.LightGreen,
147 "lineno": C.LightGreen,
160 "lineno": C.LightGreen,
148 "name": C.LightPurple,
161 "name": C.LightPurple,
149 "vName": C.Cyan,
162 "vName": C.Cyan,
150 "val": C.LightGreen,
163 "val": C.LightGreen,
151 "em": C.Cyan,
164 "em": C.Cyan,
152 # Emphasized colors for the last frame of the traceback
165 # Emphasized colors for the last frame of the traceback
153 "normalEm": C.Cyan,
166 "normalEm": C.Cyan,
154 "filenameEm": C.Green,
167 "filenameEm": C.Green,
155 "linenoEm": C.Green,
168 "linenoEm": C.Green,
156 "nameEm": C.Purple,
169 "nameEm": C.Purple,
157 "valEm": C.Blue,
170 "valEm": C.Blue,
158 # Colors for printing the exception
171 # Colors for printing the exception
159 "excName": C.Red,
172 "excName": C.Red,
160 # line = C.Brown, # brown often is displayed as yellow
173 # line = C.Brown, # brown often is displayed as yellow
161 "line": C.Red,
174 "line": C.Red,
162 "caret": C.Normal,
175 "caret": C.Normal,
163 "Normal": C.Normal,
176 "Normal": C.Normal,
177 # debugger
178 "prompt": C.Blue,
179 "breakpoint_enabled": C.LightRed,
180 "breakpoint_disabled": C.Red,
164 },
181 },
165 )
182 )
166 )
183 )
167
184
168 # Hack: the 'neutral' colours are not very visible on a dark background on
185 # Hack: the 'neutral' colours are not very visible on a dark background on
169 # Windows. Since Windows command prompts have a dark background by default, and
186 # Windows. Since Windows command prompts have a dark background by default, and
170 # relatively few users are likely to alter that, we will use the 'Linux' colours,
187 # relatively few users are likely to alter that, we will use the 'Linux' colours,
171 # designed for a dark background, as the default on Windows.
188 # designed for a dark background, as the default on Windows.
172 if os.name == "nt":
189 if os.name == "nt":
173 ex_colors.add_scheme(ex_colors['Linux'].copy('Neutral'))
190 ex_colors.add_scheme(ex_colors['Linux'].copy('Neutral'))
174
191
175 return ex_colors
192 return ex_colors
@@ -1,221 +1,247 b''
1 """Tools for coloring text in ANSI terminals.
1 """Tools for coloring text in ANSI terminals.
2 """
2 """
3
3
4 #*****************************************************************************
4 #*****************************************************************************
5 # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu>
5 # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu>
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #*****************************************************************************
9 #*****************************************************************************
10
10
11
11
12 import os
12 import os
13 import warnings
13 import warnings
14
14
15 from IPython.utils.ipstruct import Struct
15 from IPython.utils.ipstruct import Struct
16
16
17 __all__ = ["TermColors", "InputTermColors", "ColorScheme", "ColorSchemeTable"]
17 __all__ = ["TermColors", "InputTermColors", "ColorScheme", "ColorSchemeTable"]
18
18
19 _sentinel = object()
19 _sentinel = object()
20
20
21 color_templates = (
21 color_templates = (
22 # Dark colors
22 # Dark colors
23 ("Black" , "0;30"),
23 ("Black" , "0;30"),
24 ("Red" , "0;31"),
24 ("Red" , "0;31"),
25 ("Green" , "0;32"),
25 ("Green" , "0;32"),
26 ("Brown" , "0;33"),
26 ("Brown" , "0;33"),
27 ("Blue" , "0;34"),
27 ("Blue" , "0;34"),
28 ("Purple" , "0;35"),
28 ("Purple" , "0;35"),
29 ("Cyan" , "0;36"),
29 ("Cyan" , "0;36"),
30 ("LightGray" , "0;37"),
30 ("LightGray" , "0;37"),
31 # Light colors
31 # Light colors
32 ("DarkGray" , "1;30"),
32 ("DarkGray" , "1;30"),
33 ("LightRed" , "1;31"),
33 ("LightRed" , "1;31"),
34 ("LightGreen" , "1;32"),
34 ("LightGreen" , "1;32"),
35 ("Yellow" , "1;33"),
35 ("Yellow" , "1;33"),
36 ("LightBlue" , "1;34"),
36 ("LightBlue" , "1;34"),
37 ("LightPurple" , "1;35"),
37 ("LightPurple" , "1;35"),
38 ("LightCyan" , "1;36"),
38 ("LightCyan" , "1;36"),
39 ("White" , "1;37"),
39 ("White" , "1;37"),
40 # Blinking colors. Probably should not be used in anything serious.
40 # Blinking colors. Probably should not be used in anything serious.
41 ("BlinkBlack" , "5;30"),
41 ("BlinkBlack" , "5;30"),
42 ("BlinkRed" , "5;31"),
42 ("BlinkRed" , "5;31"),
43 ("BlinkGreen" , "5;32"),
43 ("BlinkGreen" , "5;32"),
44 ("BlinkYellow" , "5;33"),
44 ("BlinkYellow" , "5;33"),
45 ("BlinkBlue" , "5;34"),
45 ("BlinkBlue" , "5;34"),
46 ("BlinkPurple" , "5;35"),
46 ("BlinkPurple" , "5;35"),
47 ("BlinkCyan" , "5;36"),
47 ("BlinkCyan" , "5;36"),
48 ("BlinkLightGray", "5;37"),
48 ("BlinkLightGray", "5;37"),
49 )
49 )
50
50
51 def make_color_table(in_class):
51 def make_color_table(in_class):
52 """Build a set of color attributes in a class.
52 """Build a set of color attributes in a class.
53
53
54 Helper function for building the :class:`TermColors` and
54 Helper function for building the :class:`TermColors` and
55 :class`InputTermColors`.
55 :class`InputTermColors`.
56 """
56 """
57 for name,value in color_templates:
57 for name,value in color_templates:
58 setattr(in_class,name,in_class._base % value)
58 setattr(in_class,name,in_class._base % value)
59
59
60 class TermColors:
60 class TermColors:
61 """Color escape sequences.
61 """Color escape sequences.
62
62
63 This class defines the escape sequences for all the standard (ANSI?)
63 This class defines the escape sequences for all the standard (ANSI?)
64 colors in terminals. Also defines a NoColor escape which is just the null
64 colors in terminals. Also defines a NoColor escape which is just the null
65 string, suitable for defining 'dummy' color schemes in terminals which get
65 string, suitable for defining 'dummy' color schemes in terminals which get
66 confused by color escapes.
66 confused by color escapes.
67
67
68 This class should be used as a mixin for building color schemes."""
68 This class should be used as a mixin for building color schemes."""
69
69
70 NoColor = '' # for color schemes in color-less terminals.
70 NoColor = '' # for color schemes in color-less terminals.
71 Normal = '\033[0m' # Reset normal coloring
71 Normal = '\033[0m' # Reset normal coloring
72 _base = '\033[%sm' # Template for all other colors
72 _base = '\033[%sm' # Template for all other colors
73
73
74 # Build the actual color table as a set of class attributes:
74 Black = "0;30"
75 make_color_table(TermColors)
75 Red = "0;31"
76 Green = "0;32"
77 Brown = "0;33"
78 Blue = "0;34"
79 Purple = "0;35"
80 Cyan = "0;36"
81 LightGray = "0;37"
82 # Light colors
83 DarkGray = "1;31"
84 LightRed = "1;32"
85 LightGreen = "1;33"
86 Yellow = "1;34"
87 LightBlue = "1;35"
88 LightPurple = "1;36"
89 LightCyan = "1;37"
90 White = "1;38"
91 # Blinking colors. Probably should not be used in anything serious.
92 BlinkBlack = "5;30"
93 BlinkRed = "5;31"
94 BlinkGreen = "5;32"
95 BlinkYellow = "5;33"
96 BlinkBlue = "5;34"
97 BlinkPurple = "5;35"
98 BlinkCyan = "5;36"
99 BlinkLightGray = "5;37"
100
101
76
102
77 class InputTermColors:
103 class InputTermColors:
78 """Color escape sequences for input prompts.
104 """Color escape sequences for input prompts.
79
105
80 This class is similar to TermColors, but the escapes are wrapped in \\001
106 This class is similar to TermColors, but the escapes are wrapped in \\001
81 and \\002 so that readline can properly know the length of each line and
107 and \\002 so that readline can properly know the length of each line and
82 can wrap lines accordingly. Use this class for any colored text which
108 can wrap lines accordingly. Use this class for any colored text which
83 needs to be used in input prompts, such as in calls to raw_input().
109 needs to be used in input prompts, such as in calls to raw_input().
84
110
85 This class defines the escape sequences for all the standard (ANSI?)
111 This class defines the escape sequences for all the standard (ANSI?)
86 colors in terminals. Also defines a NoColor escape which is just the null
112 colors in terminals. Also defines a NoColor escape which is just the null
87 string, suitable for defining 'dummy' color schemes in terminals which get
113 string, suitable for defining 'dummy' color schemes in terminals which get
88 confused by color escapes.
114 confused by color escapes.
89
115
90 This class should be used as a mixin for building color schemes."""
116 This class should be used as a mixin for building color schemes."""
91
117
92 NoColor = '' # for color schemes in color-less terminals.
118 NoColor = '' # for color schemes in color-less terminals.
93
119
94 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
120 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
95 # (X)emacs on W32 gets confused with \001 and \002 so we remove them
121 # (X)emacs on W32 gets confused with \001 and \002 so we remove them
96 Normal = '\033[0m' # Reset normal coloring
122 Normal = '\033[0m' # Reset normal coloring
97 _base = '\033[%sm' # Template for all other colors
123 _base = '\033[%sm' # Template for all other colors
98 else:
124 else:
99 Normal = '\001\033[0m\002' # Reset normal coloring
125 Normal = '\001\033[0m\002' # Reset normal coloring
100 _base = '\001\033[%sm\002' # Template for all other colors
126 _base = '\001\033[%sm\002' # Template for all other colors
101
127
102 # Build the actual color table as a set of class attributes:
128 # Build the actual color table as a set of class attributes:
103 make_color_table(InputTermColors)
129 make_color_table(InputTermColors)
104
130
105 class NoColors:
131 class NoColors:
106 """This defines all the same names as the colour classes, but maps them to
132 """This defines all the same names as the colour classes, but maps them to
107 empty strings, so it can easily be substituted to turn off colours."""
133 empty strings, so it can easily be substituted to turn off colours."""
108 NoColor = ''
134 NoColor = ''
109 Normal = ''
135 Normal = ''
110
136
111 for name, value in color_templates:
137 for name, value in color_templates:
112 setattr(NoColors, name, '')
138 setattr(NoColors, name, '')
113
139
114 class ColorScheme:
140 class ColorScheme:
115 """Generic color scheme class. Just a name and a Struct."""
141 """Generic color scheme class. Just a name and a Struct."""
116
142
117 name: str
143 name: str
118 colors: Struct
144 colors: Struct
119
145
120 def __init__(self, __scheme_name_, colordict=None, **colormap):
146 def __init__(self, __scheme_name_, colordict=None, **colormap):
121 self.name = __scheme_name_
147 self.name = __scheme_name_
122 if colormap:
148 if colormap:
123 warnings.warn(
149 warnings.warn(
124 "Passing each colors as a kwarg to ColorScheme is "
150 "Passing each colors as a kwarg to ColorScheme is "
125 "considered for deprecation. Please pass a "
151 "considered for deprecation. Please pass a "
126 "a single dict as second parameter. If you are using this"
152 "a single dict as second parameter. If you are using this"
127 "feature, please comment an subscribe to issue "
153 "feature, please comment an subscribe to issue "
128 "https://github.com/ipython/ipython/issues/14304",
154 "https://github.com/ipython/ipython/issues/14304",
129 PendingDeprecationWarning,
155 PendingDeprecationWarning,
130 stacklevel=2,
156 stacklevel=2,
131 )
157 )
132 if colordict is None:
158 if colordict is None:
133 self.colors = Struct(**colormap)
159 self.colors = Struct(**colormap)
134 else:
160 else:
135 self.colors = Struct(colordict)
161 self.colors = Struct(colordict)
136
162
137 def copy(self,name=None):
163 def copy(self,name=None):
138 """Return a full copy of the object, optionally renaming it."""
164 """Return a full copy of the object, optionally renaming it."""
139 if name is None:
165 if name is None:
140 name = self.name
166 name = self.name
141 return ColorScheme(name, self.colors.dict())
167 return ColorScheme(name, self.colors.dict())
142
168
143 class ColorSchemeTable(dict):
169 class ColorSchemeTable(dict):
144 """General class to handle tables of color schemes.
170 """General class to handle tables of color schemes.
145
171
146 It's basically a dict of color schemes with a couple of shorthand
172 It's basically a dict of color schemes with a couple of shorthand
147 attributes and some convenient methods.
173 attributes and some convenient methods.
148
174
149 active_scheme_name -> obvious
175 active_scheme_name -> obvious
150 active_colors -> actual color table of the active scheme"""
176 active_colors -> actual color table of the active scheme"""
151
177
152 def __init__(self, scheme_list=None, default_scheme=''):
178 def __init__(self, scheme_list=None, default_scheme=''):
153 """Create a table of color schemes.
179 """Create a table of color schemes.
154
180
155 The table can be created empty and manually filled or it can be
181 The table can be created empty and manually filled or it can be
156 created with a list of valid color schemes AND the specification for
182 created with a list of valid color schemes AND the specification for
157 the default active scheme.
183 the default active scheme.
158 """
184 """
159
185
160 # create object attributes to be set later
186 # create object attributes to be set later
161 self.active_scheme_name = ''
187 self.active_scheme_name = ''
162 self.active_colors = None
188 self.active_colors = None
163
189
164 if scheme_list:
190 if scheme_list:
165 if default_scheme == '':
191 if default_scheme == '':
166 raise ValueError('you must specify the default color scheme')
192 raise ValueError('you must specify the default color scheme')
167 for scheme in scheme_list:
193 for scheme in scheme_list:
168 self.add_scheme(scheme)
194 self.add_scheme(scheme)
169 self.set_active_scheme(default_scheme)
195 self.set_active_scheme(default_scheme)
170
196
171 def copy(self):
197 def copy(self):
172 """Return full copy of object"""
198 """Return full copy of object"""
173 return ColorSchemeTable(self.values(),self.active_scheme_name)
199 return ColorSchemeTable(self.values(),self.active_scheme_name)
174
200
175 def __setitem__(self, key: str, value: ColorScheme):
201 def __setitem__(self, key: str, value: ColorScheme):
176 assert isinstance(key, str)
202 assert isinstance(key, str)
177 assert isinstance(value, ColorScheme)
203 assert isinstance(value, ColorScheme)
178 super().__setitem__(key, value)
204 super().__setitem__(key, value)
179
205
180 def add_scheme(self, new_scheme):
206 def add_scheme(self, new_scheme):
181 """Add a new color scheme to the table."""
207 """Add a new color scheme to the table."""
182 if not isinstance(new_scheme, ColorScheme):
208 if not isinstance(new_scheme, ColorScheme):
183 raise ValueError('ColorSchemeTable only accepts ColorScheme instances')
209 raise ValueError('ColorSchemeTable only accepts ColorScheme instances')
184 self[new_scheme.name] = new_scheme
210 self[new_scheme.name] = new_scheme
185
211
186 def set_active_scheme(self, scheme, case_sensitive=_sentinel):
212 def set_active_scheme(self, scheme, case_sensitive=_sentinel):
187 """Set the currently active scheme.
213 """Set the currently active scheme.
188
214
189 Names are by default compared in a case-insensitive way, but this can
215 Names are by default compared in a case-insensitive way, but this can
190 be changed by setting the parameter case_sensitive to true."""
216 be changed by setting the parameter case_sensitive to true."""
191
217
192 if case_sensitive is _sentinel:
218 if case_sensitive is _sentinel:
193 case_sensitive = False
219 case_sensitive = False
194 else:
220 else:
195 warnings.warn(
221 warnings.warn(
196 "set_active_scheme(case_sensitive=...) is Pending "
222 "set_active_scheme(case_sensitive=...) is Pending "
197 "deprecation. Please comment on "
223 "deprecation. Please comment on "
198 "https://github.com/ipython/ipython/issues/14306 "
224 "https://github.com/ipython/ipython/issues/14306 "
199 "to let the ipython maintainer that you are affected.",
225 "to let the ipython maintainer that you are affected.",
200 PendingDeprecationWarning,
226 PendingDeprecationWarning,
201 stacklevel=2,
227 stacklevel=2,
202 )
228 )
203
229
204 scheme_names = list(self.keys())
230 scheme_names = list(self.keys())
205 if case_sensitive:
231 if case_sensitive:
206 valid_schemes = scheme_names
232 valid_schemes = scheme_names
207 scheme_test = scheme
233 scheme_test = scheme
208 else:
234 else:
209 valid_schemes = [s.lower() for s in scheme_names]
235 valid_schemes = [s.lower() for s in scheme_names]
210 scheme_test = scheme.lower()
236 scheme_test = scheme.lower()
211 try:
237 try:
212 scheme_idx = valid_schemes.index(scheme_test)
238 scheme_idx = valid_schemes.index(scheme_test)
213 except ValueError as e:
239 except ValueError as e:
214 raise ValueError('Unrecognized color scheme: ' + scheme + \
240 raise ValueError('Unrecognized color scheme: ' + scheme + \
215 '\nValid schemes: '+str(scheme_names).replace("'', ",'')) from e
241 '\nValid schemes: '+str(scheme_names).replace("'', ",'')) from e
216 else:
242 else:
217 active = scheme_names[scheme_idx]
243 active = scheme_names[scheme_idx]
218 self.active_scheme_name = active
244 self.active_scheme_name = active
219 self.active_colors = self[active].colors
245 self.active_colors = self[active].colors
220 # Now allow using '' as an index for the current active scheme
246 # Now allow using '' as an index for the current active scheme
221 self[''] = self[active]
247 self[''] = self[active]
General Comments 0
You need to be logged in to leave comments. Login now