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