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