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