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