##// END OF EJS Templates
Backport PR #13000: Allow to control if reporting hidden frames
Matthias Bussonnier -
Show More
@@ -1,946 +1,949 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 https://docs.python.org/2/license.html
16 https://docs.python.org/2/license.html
17 """
17 """
18
18
19 #*****************************************************************************
19 #*****************************************************************************
20 #
20 #
21 # This file is licensed under the PSF license.
21 # This file is licensed under the PSF license.
22 #
22 #
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
25 #
25 #
26 #
26 #
27 #*****************************************************************************
27 #*****************************************************************************
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import linecache
32 import linecache
33 import sys
33 import sys
34 import warnings
34 import warnings
35 import re
35 import re
36 import os
36 import os
37
37
38 from IPython import get_ipython
38 from IPython import get_ipython
39 from IPython.utils import PyColorize
39 from IPython.utils import PyColorize
40 from IPython.utils import coloransi, py3compat
40 from IPython.utils import coloransi, py3compat
41 from IPython.core.excolors import exception_colors
41 from IPython.core.excolors import exception_colors
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43
43
44
44
45 prompt = 'ipdb> '
45 prompt = 'ipdb> '
46
46
47 # We have to check this directly from sys.argv, config struct not yet available
47 # We have to check this directly from sys.argv, config struct not yet available
48 from pdb import Pdb as OldPdb
48 from pdb import Pdb as OldPdb
49
49
50 # Allow the set_trace code to operate outside of an ipython instance, even if
50 # Allow the set_trace code to operate outside of an ipython instance, even if
51 # it does so with some limitations. The rest of this support is implemented in
51 # it does so with some limitations. The rest of this support is implemented in
52 # the Tracer constructor.
52 # the Tracer constructor.
53
53
54
54
55 def make_arrow(pad):
55 def make_arrow(pad):
56 """generate the leading arrow in front of traceback or debugger"""
56 """generate the leading arrow in front of traceback or debugger"""
57 if pad >= 2:
57 if pad >= 2:
58 return '-'*(pad-2) + '> '
58 return '-'*(pad-2) + '> '
59 elif pad == 1:
59 elif pad == 1:
60 return '>'
60 return '>'
61 return ''
61 return ''
62
62
63
63
64 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
64 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
65 """Exception hook which handles `BdbQuit` exceptions.
65 """Exception hook which handles `BdbQuit` exceptions.
66
66
67 All other exceptions are processed using the `excepthook`
67 All other exceptions are processed using the `excepthook`
68 parameter.
68 parameter.
69 """
69 """
70 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
70 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
71 DeprecationWarning, stacklevel=2)
71 DeprecationWarning, stacklevel=2)
72 if et == bdb.BdbQuit:
72 if et == bdb.BdbQuit:
73 print('Exiting Debugger.')
73 print('Exiting Debugger.')
74 elif excepthook is not None:
74 elif excepthook is not None:
75 excepthook(et, ev, tb)
75 excepthook(et, ev, tb)
76 else:
76 else:
77 # Backwards compatibility. Raise deprecation warning?
77 # Backwards compatibility. Raise deprecation warning?
78 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
78 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
79
79
80
80
81 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
81 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
82 warnings.warn(
82 warnings.warn(
83 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
83 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
84 DeprecationWarning, stacklevel=2)
84 DeprecationWarning, stacklevel=2)
85 print('Exiting Debugger.')
85 print('Exiting Debugger.')
86
86
87
87
88 class Tracer(object):
88 class Tracer(object):
89 """
89 """
90 DEPRECATED
90 DEPRECATED
91
91
92 Class for local debugging, similar to pdb.set_trace.
92 Class for local debugging, similar to pdb.set_trace.
93
93
94 Instances of this class, when called, behave like pdb.set_trace, but
94 Instances of this class, when called, behave like pdb.set_trace, but
95 providing IPython's enhanced capabilities.
95 providing IPython's enhanced capabilities.
96
96
97 This is implemented as a class which must be initialized in your own code
97 This is implemented as a class which must be initialized in your own code
98 and not as a standalone function because we need to detect at runtime
98 and not as a standalone function because we need to detect at runtime
99 whether IPython is already active or not. That detection is done in the
99 whether IPython is already active or not. That detection is done in the
100 constructor, ensuring that this code plays nicely with a running IPython,
100 constructor, ensuring that this code plays nicely with a running IPython,
101 while functioning acceptably (though with limitations) if outside of it.
101 while functioning acceptably (though with limitations) if outside of it.
102 """
102 """
103
103
104 @skip_doctest
104 @skip_doctest
105 def __init__(self, colors=None):
105 def __init__(self, colors=None):
106 """
106 """
107 DEPRECATED
107 DEPRECATED
108
108
109 Create a local debugger instance.
109 Create a local debugger instance.
110
110
111 Parameters
111 Parameters
112 ----------
112 ----------
113 colors : str, optional
113 colors : str, optional
114 The name of the color scheme to use, it must be one of IPython's
114 The name of the color scheme to use, it must be one of IPython's
115 valid color schemes. If not given, the function will default to
115 valid color schemes. If not given, the function will default to
116 the current IPython scheme when running inside IPython, and to
116 the current IPython scheme when running inside IPython, and to
117 'NoColor' otherwise.
117 'NoColor' otherwise.
118
118
119 Examples
119 Examples
120 --------
120 --------
121 ::
121 ::
122
122
123 from IPython.core.debugger import Tracer; debug_here = Tracer()
123 from IPython.core.debugger import Tracer; debug_here = Tracer()
124
124
125 Later in your code::
125 Later in your code::
126
126
127 debug_here() # -> will open up the debugger at that point.
127 debug_here() # -> will open up the debugger at that point.
128
128
129 Once the debugger activates, you can use all of its regular commands to
129 Once the debugger activates, you can use all of its regular commands to
130 step through code, set breakpoints, etc. See the pdb documentation
130 step through code, set breakpoints, etc. See the pdb documentation
131 from the Python standard library for usage details.
131 from the Python standard library for usage details.
132 """
132 """
133 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
133 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
134 "`IPython.core.debugger.Pdb.set_trace()`",
134 "`IPython.core.debugger.Pdb.set_trace()`",
135 DeprecationWarning, stacklevel=2)
135 DeprecationWarning, stacklevel=2)
136
136
137 ip = get_ipython()
137 ip = get_ipython()
138 if ip is None:
138 if ip is None:
139 # Outside of ipython, we set our own exception hook manually
139 # Outside of ipython, we set our own exception hook manually
140 sys.excepthook = functools.partial(BdbQuit_excepthook,
140 sys.excepthook = functools.partial(BdbQuit_excepthook,
141 excepthook=sys.excepthook)
141 excepthook=sys.excepthook)
142 def_colors = 'NoColor'
142 def_colors = 'NoColor'
143 else:
143 else:
144 # In ipython, we use its custom exception handler mechanism
144 # In ipython, we use its custom exception handler mechanism
145 def_colors = ip.colors
145 def_colors = ip.colors
146 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
146 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
147
147
148 if colors is None:
148 if colors is None:
149 colors = def_colors
149 colors = def_colors
150
150
151 # The stdlib debugger internally uses a modified repr from the `repr`
151 # The stdlib debugger internally uses a modified repr from the `repr`
152 # module, that limits the length of printed strings to a hardcoded
152 # module, that limits the length of printed strings to a hardcoded
153 # limit of 30 characters. That much trimming is too aggressive, let's
153 # limit of 30 characters. That much trimming is too aggressive, let's
154 # at least raise that limit to 80 chars, which should be enough for
154 # at least raise that limit to 80 chars, which should be enough for
155 # most interactive uses.
155 # most interactive uses.
156 try:
156 try:
157 from reprlib import aRepr
157 from reprlib import aRepr
158 aRepr.maxstring = 80
158 aRepr.maxstring = 80
159 except:
159 except:
160 # This is only a user-facing convenience, so any error we encounter
160 # This is only a user-facing convenience, so any error we encounter
161 # here can be warned about but can be otherwise ignored. These
161 # here can be warned about but can be otherwise ignored. These
162 # printouts will tell us about problems if this API changes
162 # printouts will tell us about problems if this API changes
163 import traceback
163 import traceback
164 traceback.print_exc()
164 traceback.print_exc()
165
165
166 self.debugger = Pdb(colors)
166 self.debugger = Pdb(colors)
167
167
168 def __call__(self):
168 def __call__(self):
169 """Starts an interactive debugger at the point where called.
169 """Starts an interactive debugger at the point where called.
170
170
171 This is similar to the pdb.set_trace() function from the std lib, but
171 This is similar to the pdb.set_trace() function from the std lib, but
172 using IPython's enhanced debugger."""
172 using IPython's enhanced debugger."""
173
173
174 self.debugger.set_trace(sys._getframe().f_back)
174 self.debugger.set_trace(sys._getframe().f_back)
175
175
176
176
177 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
177 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
178
178
179
179
180 def strip_indentation(multiline_string):
180 def strip_indentation(multiline_string):
181 return RGX_EXTRA_INDENT.sub('', multiline_string)
181 return RGX_EXTRA_INDENT.sub('', multiline_string)
182
182
183
183
184 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
184 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
185 """Make new_fn have old_fn's doc string. This is particularly useful
185 """Make new_fn have old_fn's doc string. This is particularly useful
186 for the ``do_...`` commands that hook into the help system.
186 for the ``do_...`` commands that hook into the help system.
187 Adapted from from a comp.lang.python posting
187 Adapted from from a comp.lang.python posting
188 by Duncan Booth."""
188 by Duncan Booth."""
189 def wrapper(*args, **kw):
189 def wrapper(*args, **kw):
190 return new_fn(*args, **kw)
190 return new_fn(*args, **kw)
191 if old_fn.__doc__:
191 if old_fn.__doc__:
192 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
192 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
193 return wrapper
193 return wrapper
194
194
195
195
196 class Pdb(OldPdb):
196 class Pdb(OldPdb):
197 """Modified Pdb class, does not load readline.
197 """Modified Pdb class, does not load readline.
198
198
199 for a standalone version that uses prompt_toolkit, see
199 for a standalone version that uses prompt_toolkit, see
200 `IPython.terminal.debugger.TerminalPdb` and
200 `IPython.terminal.debugger.TerminalPdb` and
201 `IPython.terminal.debugger.set_trace()`
201 `IPython.terminal.debugger.set_trace()`
202
202
203
203
204 This debugger can hide and skip frames that are tagged according to some predicates.
204 This debugger can hide and skip frames that are tagged according to some predicates.
205 See the `skip_predicates` commands.
205 See the `skip_predicates` commands.
206
206
207 """
207 """
208
208
209 default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True}
209 default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True}
210
210
211 def __init__(self, color_scheme=None, completekey=None,
211 def __init__(self, color_scheme=None, completekey=None,
212 stdin=None, stdout=None, context=5, **kwargs):
212 stdin=None, stdout=None, context=5, **kwargs):
213 """Create a new IPython debugger.
213 """Create a new IPython debugger.
214
214
215 Parameters
215 Parameters
216 ----------
216 ----------
217 color_scheme : default None
217 color_scheme : default None
218 Deprecated, do not use.
218 Deprecated, do not use.
219 completekey : default None
219 completekey : default None
220 Passed to pdb.Pdb.
220 Passed to pdb.Pdb.
221 stdin : default None
221 stdin : default None
222 Passed to pdb.Pdb.
222 Passed to pdb.Pdb.
223 stdout : default None
223 stdout : default None
224 Passed to pdb.Pdb.
224 Passed to pdb.Pdb.
225 context : int
225 context : int
226 Number of lines of source code context to show when
226 Number of lines of source code context to show when
227 displaying stacktrace information.
227 displaying stacktrace information.
228 **kwargs
228 **kwargs
229 Passed to pdb.Pdb.
229 Passed to pdb.Pdb.
230
230
231 Notes
231 Notes
232 -----
232 -----
233 The possibilities are python version dependent, see the python
233 The possibilities are python version dependent, see the python
234 docs for more info.
234 docs for more info.
235 """
235 """
236
236
237 # Parent constructor:
237 # Parent constructor:
238 try:
238 try:
239 self.context = int(context)
239 self.context = int(context)
240 if self.context <= 0:
240 if self.context <= 0:
241 raise ValueError("Context must be a positive integer")
241 raise ValueError("Context must be a positive integer")
242 except (TypeError, ValueError) as e:
242 except (TypeError, ValueError) as e:
243 raise ValueError("Context must be a positive integer") from e
243 raise ValueError("Context must be a positive integer") from e
244
244
245 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
245 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
246 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
246 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
247
247
248 # IPython changes...
248 # IPython changes...
249 self.shell = get_ipython()
249 self.shell = get_ipython()
250
250
251 if self.shell is None:
251 if self.shell is None:
252 save_main = sys.modules['__main__']
252 save_main = sys.modules['__main__']
253 # No IPython instance running, we must create one
253 # No IPython instance running, we must create one
254 from IPython.terminal.interactiveshell import \
254 from IPython.terminal.interactiveshell import \
255 TerminalInteractiveShell
255 TerminalInteractiveShell
256 self.shell = TerminalInteractiveShell.instance()
256 self.shell = TerminalInteractiveShell.instance()
257 # needed by any code which calls __import__("__main__") after
257 # needed by any code which calls __import__("__main__") after
258 # the debugger was entered. See also #9941.
258 # the debugger was entered. See also #9941.
259 sys.modules["__main__"] = save_main
259 sys.modules["__main__"] = save_main
260
260
261 if color_scheme is not None:
261 if color_scheme is not None:
262 warnings.warn(
262 warnings.warn(
263 "The `color_scheme` argument is deprecated since version 5.1",
263 "The `color_scheme` argument is deprecated since version 5.1",
264 DeprecationWarning, stacklevel=2)
264 DeprecationWarning, stacklevel=2)
265 else:
265 else:
266 color_scheme = self.shell.colors
266 color_scheme = self.shell.colors
267
267
268 self.aliases = {}
268 self.aliases = {}
269
269
270 # Create color table: we copy the default one from the traceback
270 # Create color table: we copy the default one from the traceback
271 # module and add a few attributes needed for debugging
271 # module and add a few attributes needed for debugging
272 self.color_scheme_table = exception_colors()
272 self.color_scheme_table = exception_colors()
273
273
274 # shorthands
274 # shorthands
275 C = coloransi.TermColors
275 C = coloransi.TermColors
276 cst = self.color_scheme_table
276 cst = self.color_scheme_table
277
277
278 cst['NoColor'].colors.prompt = C.NoColor
278 cst['NoColor'].colors.prompt = C.NoColor
279 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
279 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
280 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
280 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
281
281
282 cst['Linux'].colors.prompt = C.Green
282 cst['Linux'].colors.prompt = C.Green
283 cst['Linux'].colors.breakpoint_enabled = C.LightRed
283 cst['Linux'].colors.breakpoint_enabled = C.LightRed
284 cst['Linux'].colors.breakpoint_disabled = C.Red
284 cst['Linux'].colors.breakpoint_disabled = C.Red
285
285
286 cst['LightBG'].colors.prompt = C.Blue
286 cst['LightBG'].colors.prompt = C.Blue
287 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
287 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
288 cst['LightBG'].colors.breakpoint_disabled = C.Red
288 cst['LightBG'].colors.breakpoint_disabled = C.Red
289
289
290 cst['Neutral'].colors.prompt = C.Blue
290 cst['Neutral'].colors.prompt = C.Blue
291 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
291 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
292 cst['Neutral'].colors.breakpoint_disabled = C.Red
292 cst['Neutral'].colors.breakpoint_disabled = C.Red
293
293
294 # Add a python parser so we can syntax highlight source while
294 # Add a python parser so we can syntax highlight source while
295 # debugging.
295 # debugging.
296 self.parser = PyColorize.Parser(style=color_scheme)
296 self.parser = PyColorize.Parser(style=color_scheme)
297 self.set_colors(color_scheme)
297 self.set_colors(color_scheme)
298
298
299 # Set the prompt - the default prompt is '(Pdb)'
299 # Set the prompt - the default prompt is '(Pdb)'
300 self.prompt = prompt
300 self.prompt = prompt
301 self.skip_hidden = True
301 self.skip_hidden = True
302 self.report_skipped = True
302
303
303 # list of predicates we use to skip frames
304 # list of predicates we use to skip frames
304 self._predicates = self.default_predicates
305 self._predicates = self.default_predicates
305
306
306 def set_colors(self, scheme):
307 def set_colors(self, scheme):
307 """Shorthand access to the color table scheme selector method."""
308 """Shorthand access to the color table scheme selector method."""
308 self.color_scheme_table.set_active_scheme(scheme)
309 self.color_scheme_table.set_active_scheme(scheme)
309 self.parser.style = scheme
310 self.parser.style = scheme
310
311
311 def set_trace(self, frame=None):
312 def set_trace(self, frame=None):
312 if frame is None:
313 if frame is None:
313 frame = sys._getframe().f_back
314 frame = sys._getframe().f_back
314 self.initial_frame = frame
315 self.initial_frame = frame
315 return super().set_trace(frame)
316 return super().set_trace(frame)
316
317
317 def _hidden_predicate(self, frame):
318 def _hidden_predicate(self, frame):
318 """
319 """
319 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.
320 """
321 """
321
322
322 if self._predicates["readonly"]:
323 if self._predicates["readonly"]:
323 fname = frame.f_code.co_filename
324 fname = frame.f_code.co_filename
324 # we need to check for file existence and interactively define
325 # we need to check for file existence and interactively define
325 # function would otherwise appear as RO.
326 # function would otherwise appear as RO.
326 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):
327 return True
328 return True
328
329
329 if self._predicates["tbhide"]:
330 if self._predicates["tbhide"]:
330 if frame in (self.curframe, getattr(self, "initial_frame", None)):
331 if frame in (self.curframe, getattr(self, "initial_frame", None)):
331 return False
332 return False
332 else:
333 else:
333 return frame.f_locals.get("__tracebackhide__", False)
334 return frame.f_locals.get("__tracebackhide__", False)
334
335
335 return False
336 return False
336
337
337 def hidden_frames(self, stack):
338 def hidden_frames(self, stack):
338 """
339 """
339 Given an index in the stack return whether it should be skipped.
340 Given an index in the stack return whether it should be skipped.
340
341
341 This is used in up/down and where to skip frames.
342 This is used in up/down and where to skip frames.
342 """
343 """
343 # The f_locals dictionary is updated from the actual frame
344 # The f_locals dictionary is updated from the actual frame
344 # locals whenever the .f_locals accessor is called, so we
345 # locals whenever the .f_locals accessor is called, so we
345 # avoid calling it here to preserve self.curframe_locals.
346 # avoid calling it here to preserve self.curframe_locals.
346 # Futhermore, there is no good reason to hide the current frame.
347 # Futhermore, there is no good reason to hide the current frame.
347 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
348 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
348 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
349 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
349 if ip_start and self._predicates["ipython_internal"]:
350 if ip_start and self._predicates["ipython_internal"]:
350 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
351 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
351 return ip_hide
352 return ip_hide
352
353
353 def interaction(self, frame, traceback):
354 def interaction(self, frame, traceback):
354 try:
355 try:
355 OldPdb.interaction(self, frame, traceback)
356 OldPdb.interaction(self, frame, traceback)
356 except KeyboardInterrupt:
357 except KeyboardInterrupt:
357 self.stdout.write("\n" + self.shell.get_exception_only())
358 self.stdout.write("\n" + self.shell.get_exception_only())
358
359
359 def precmd(self, line):
360 def precmd(self, line):
360 """Perform useful escapes on the command before it is executed."""
361 """Perform useful escapes on the command before it is executed."""
361
362
362 if line.endswith("??"):
363 if line.endswith("??"):
363 line = "pinfo2 " + line[:-2]
364 line = "pinfo2 " + line[:-2]
364 elif line.endswith("?"):
365 elif line.endswith("?"):
365 line = "pinfo " + line[:-1]
366 line = "pinfo " + line[:-1]
366
367
367 line = super().precmd(line)
368 line = super().precmd(line)
368
369
369 return line
370 return line
370
371
371 def new_do_frame(self, arg):
372 def new_do_frame(self, arg):
372 OldPdb.do_frame(self, arg)
373 OldPdb.do_frame(self, arg)
373
374
374 def new_do_quit(self, arg):
375 def new_do_quit(self, arg):
375
376
376 if hasattr(self, 'old_all_completions'):
377 if hasattr(self, 'old_all_completions'):
377 self.shell.Completer.all_completions = self.old_all_completions
378 self.shell.Completer.all_completions = self.old_all_completions
378
379
379 return OldPdb.do_quit(self, arg)
380 return OldPdb.do_quit(self, arg)
380
381
381 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
382 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
382
383
383 def new_do_restart(self, arg):
384 def new_do_restart(self, arg):
384 """Restart command. In the context of ipython this is exactly the same
385 """Restart command. In the context of ipython this is exactly the same
385 thing as 'quit'."""
386 thing as 'quit'."""
386 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
387 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
387 return self.do_quit(arg)
388 return self.do_quit(arg)
388
389
389 def print_stack_trace(self, context=None):
390 def print_stack_trace(self, context=None):
390 Colors = self.color_scheme_table.active_colors
391 Colors = self.color_scheme_table.active_colors
391 ColorsNormal = Colors.Normal
392 ColorsNormal = Colors.Normal
392 if context is None:
393 if context is None:
393 context = self.context
394 context = self.context
394 try:
395 try:
395 context = int(context)
396 context = int(context)
396 if context <= 0:
397 if context <= 0:
397 raise ValueError("Context must be a positive integer")
398 raise ValueError("Context must be a positive integer")
398 except (TypeError, ValueError) as e:
399 except (TypeError, ValueError) as e:
399 raise ValueError("Context must be a positive integer") from e
400 raise ValueError("Context must be a positive integer") from e
400 try:
401 try:
401 skipped = 0
402 skipped = 0
402 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
403 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
403 if hidden and self.skip_hidden:
404 if hidden and self.skip_hidden:
404 skipped += 1
405 skipped += 1
405 continue
406 continue
406 if skipped:
407 if skipped:
407 print(
408 print(
408 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
409 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
409 )
410 )
410 skipped = 0
411 skipped = 0
411 self.print_stack_entry(frame_lineno, context=context)
412 self.print_stack_entry(frame_lineno, context=context)
412 if skipped:
413 if skipped:
413 print(
414 print(
414 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
415 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
415 )
416 )
416 except KeyboardInterrupt:
417 except KeyboardInterrupt:
417 pass
418 pass
418
419
419 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
420 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
420 context=None):
421 context=None):
421 if context is None:
422 if context is None:
422 context = self.context
423 context = self.context
423 try:
424 try:
424 context = int(context)
425 context = int(context)
425 if context <= 0:
426 if context <= 0:
426 raise ValueError("Context must be a positive integer")
427 raise ValueError("Context must be a positive integer")
427 except (TypeError, ValueError) as e:
428 except (TypeError, ValueError) as e:
428 raise ValueError("Context must be a positive integer") from e
429 raise ValueError("Context must be a positive integer") from e
429 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
430 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
430
431
431 # vds: >>
432 # vds: >>
432 frame, lineno = frame_lineno
433 frame, lineno = frame_lineno
433 filename = frame.f_code.co_filename
434 filename = frame.f_code.co_filename
434 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
435 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
435 # vds: <<
436 # vds: <<
436
437
437 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
438 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
438 if context is None:
439 if context is None:
439 context = self.context
440 context = self.context
440 try:
441 try:
441 context = int(context)
442 context = int(context)
442 if context <= 0:
443 if context <= 0:
443 print("Context must be a positive integer", file=self.stdout)
444 print("Context must be a positive integer", file=self.stdout)
444 except (TypeError, ValueError):
445 except (TypeError, ValueError):
445 print("Context must be a positive integer", file=self.stdout)
446 print("Context must be a positive integer", file=self.stdout)
446
447
447 import reprlib
448 import reprlib
448
449
449 ret = []
450 ret = []
450
451
451 Colors = self.color_scheme_table.active_colors
452 Colors = self.color_scheme_table.active_colors
452 ColorsNormal = Colors.Normal
453 ColorsNormal = Colors.Normal
453 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
454 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
454 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
455 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
455 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
456 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
456 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
457 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
457
458
458 frame, lineno = frame_lineno
459 frame, lineno = frame_lineno
459
460
460 return_value = ''
461 return_value = ''
461 if '__return__' in frame.f_locals:
462 if '__return__' in frame.f_locals:
462 rv = frame.f_locals['__return__']
463 rv = frame.f_locals['__return__']
463 #return_value += '->'
464 #return_value += '->'
464 return_value += reprlib.repr(rv) + '\n'
465 return_value += reprlib.repr(rv) + '\n'
465 ret.append(return_value)
466 ret.append(return_value)
466
467
467 #s = filename + '(' + `lineno` + ')'
468 #s = filename + '(' + `lineno` + ')'
468 filename = self.canonic(frame.f_code.co_filename)
469 filename = self.canonic(frame.f_code.co_filename)
469 link = tpl_link % py3compat.cast_unicode(filename)
470 link = tpl_link % py3compat.cast_unicode(filename)
470
471
471 if frame.f_code.co_name:
472 if frame.f_code.co_name:
472 func = frame.f_code.co_name
473 func = frame.f_code.co_name
473 else:
474 else:
474 func = "<lambda>"
475 func = "<lambda>"
475
476
476 call = ''
477 call = ''
477 if func != '?':
478 if func != '?':
478 if '__args__' in frame.f_locals:
479 if '__args__' in frame.f_locals:
479 args = reprlib.repr(frame.f_locals['__args__'])
480 args = reprlib.repr(frame.f_locals['__args__'])
480 else:
481 else:
481 args = '()'
482 args = '()'
482 call = tpl_call % (func, args)
483 call = tpl_call % (func, args)
483
484
484 # The level info should be generated in the same format pdb uses, to
485 # The level info should be generated in the same format pdb uses, to
485 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
486 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
486 if frame is self.curframe:
487 if frame is self.curframe:
487 ret.append('> ')
488 ret.append('> ')
488 else:
489 else:
489 ret.append(" ")
490 ret.append(" ")
490 ret.append("%s(%s)%s\n" % (link, lineno, call))
491 ret.append("%s(%s)%s\n" % (link, lineno, call))
491
492
492 start = lineno - 1 - context//2
493 start = lineno - 1 - context//2
493 lines = linecache.getlines(filename)
494 lines = linecache.getlines(filename)
494 start = min(start, len(lines) - context)
495 start = min(start, len(lines) - context)
495 start = max(start, 0)
496 start = max(start, 0)
496 lines = lines[start : start + context]
497 lines = lines[start : start + context]
497
498
498 for i, line in enumerate(lines):
499 for i, line in enumerate(lines):
499 show_arrow = start + 1 + i == lineno
500 show_arrow = start + 1 + i == lineno
500 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
501 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
501 ret.append(
502 ret.append(
502 self.__format_line(
503 self.__format_line(
503 linetpl, filename, start + 1 + i, line, arrow=show_arrow
504 linetpl, filename, start + 1 + i, line, arrow=show_arrow
504 )
505 )
505 )
506 )
506 return "".join(ret)
507 return "".join(ret)
507
508
508 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
509 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
509 bp_mark = ""
510 bp_mark = ""
510 bp_mark_color = ""
511 bp_mark_color = ""
511
512
512 new_line, err = self.parser.format2(line, 'str')
513 new_line, err = self.parser.format2(line, 'str')
513 if not err:
514 if not err:
514 line = new_line
515 line = new_line
515
516
516 bp = None
517 bp = None
517 if lineno in self.get_file_breaks(filename):
518 if lineno in self.get_file_breaks(filename):
518 bps = self.get_breaks(filename, lineno)
519 bps = self.get_breaks(filename, lineno)
519 bp = bps[-1]
520 bp = bps[-1]
520
521
521 if bp:
522 if bp:
522 Colors = self.color_scheme_table.active_colors
523 Colors = self.color_scheme_table.active_colors
523 bp_mark = str(bp.number)
524 bp_mark = str(bp.number)
524 bp_mark_color = Colors.breakpoint_enabled
525 bp_mark_color = Colors.breakpoint_enabled
525 if not bp.enabled:
526 if not bp.enabled:
526 bp_mark_color = Colors.breakpoint_disabled
527 bp_mark_color = Colors.breakpoint_disabled
527
528
528 numbers_width = 7
529 numbers_width = 7
529 if arrow:
530 if arrow:
530 # This is the line with the error
531 # This is the line with the error
531 pad = numbers_width - len(str(lineno)) - len(bp_mark)
532 pad = numbers_width - len(str(lineno)) - len(bp_mark)
532 num = '%s%s' % (make_arrow(pad), str(lineno))
533 num = '%s%s' % (make_arrow(pad), str(lineno))
533 else:
534 else:
534 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
535 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
535
536
536 return tpl_line % (bp_mark_color + bp_mark, num, line)
537 return tpl_line % (bp_mark_color + bp_mark, num, line)
537
538
538 def print_list_lines(self, filename, first, last):
539 def print_list_lines(self, filename, first, last):
539 """The printing (as opposed to the parsing part of a 'list'
540 """The printing (as opposed to the parsing part of a 'list'
540 command."""
541 command."""
541 try:
542 try:
542 Colors = self.color_scheme_table.active_colors
543 Colors = self.color_scheme_table.active_colors
543 ColorsNormal = Colors.Normal
544 ColorsNormal = Colors.Normal
544 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
545 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
545 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
546 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
546 src = []
547 src = []
547 if filename == "<string>" and hasattr(self, "_exec_filename"):
548 if filename == "<string>" and hasattr(self, "_exec_filename"):
548 filename = self._exec_filename
549 filename = self._exec_filename
549
550
550 for lineno in range(first, last+1):
551 for lineno in range(first, last+1):
551 line = linecache.getline(filename, lineno)
552 line = linecache.getline(filename, lineno)
552 if not line:
553 if not line:
553 break
554 break
554
555
555 if lineno == self.curframe.f_lineno:
556 if lineno == self.curframe.f_lineno:
556 line = self.__format_line(
557 line = self.__format_line(
557 tpl_line_em, filename, lineno, line, arrow=True
558 tpl_line_em, filename, lineno, line, arrow=True
558 )
559 )
559 else:
560 else:
560 line = self.__format_line(
561 line = self.__format_line(
561 tpl_line, filename, lineno, line, arrow=False
562 tpl_line, filename, lineno, line, arrow=False
562 )
563 )
563
564
564 src.append(line)
565 src.append(line)
565 self.lineno = lineno
566 self.lineno = lineno
566
567
567 print(''.join(src), file=self.stdout)
568 print(''.join(src), file=self.stdout)
568
569
569 except KeyboardInterrupt:
570 except KeyboardInterrupt:
570 pass
571 pass
571
572
572 def do_skip_predicates(self, args):
573 def do_skip_predicates(self, args):
573 """
574 """
574 Turn on/off individual predicates as to whether a frame should be hidden/skip.
575 Turn on/off individual predicates as to whether a frame should be hidden/skip.
575
576
576 The global option to skip (or not) hidden frames is set with skip_hidden
577 The global option to skip (or not) hidden frames is set with skip_hidden
577
578
578 To change the value of a predicate
579 To change the value of a predicate
579
580
580 skip_predicates key [true|false]
581 skip_predicates key [true|false]
581
582
582 Call without arguments to see the current values.
583 Call without arguments to see the current values.
583
584
584 To permanently change the value of an option add the corresponding
585 To permanently change the value of an option add the corresponding
585 command to your ``~/.pdbrc`` file. If you are programmatically using the
586 command to your ``~/.pdbrc`` file. If you are programmatically using the
586 Pdb instance you can also change the ``default_predicates`` class
587 Pdb instance you can also change the ``default_predicates`` class
587 attribute.
588 attribute.
588 """
589 """
589 if not args.strip():
590 if not args.strip():
590 print("current predicates:")
591 print("current predicates:")
591 for (p, v) in self._predicates.items():
592 for (p, v) in self._predicates.items():
592 print(" ", p, ":", v)
593 print(" ", p, ":", v)
593 return
594 return
594 type_value = args.strip().split(" ")
595 type_value = args.strip().split(" ")
595 if len(type_value) != 2:
596 if len(type_value) != 2:
596 print(
597 print(
597 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
598 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
598 )
599 )
599 return
600 return
600
601
601 type_, value = type_value
602 type_, value = type_value
602 if type_ not in self._predicates:
603 if type_ not in self._predicates:
603 print(f"{type_!r} not in {set(self._predicates.keys())}")
604 print(f"{type_!r} not in {set(self._predicates.keys())}")
604 return
605 return
605 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
606 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
606 print(
607 print(
607 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
608 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
608 )
609 )
609 return
610 return
610
611
611 self._predicates[type_] = value.lower() in ("true", "yes", "1")
612 self._predicates[type_] = value.lower() in ("true", "yes", "1")
612 if not any(self._predicates.values()):
613 if not any(self._predicates.values()):
613 print(
614 print(
614 "Warning, all predicates set to False, skip_hidden may not have any effects."
615 "Warning, all predicates set to False, skip_hidden may not have any effects."
615 )
616 )
616
617
617 def do_skip_hidden(self, arg):
618 def do_skip_hidden(self, arg):
618 """
619 """
619 Change whether or not we should skip frames with the
620 Change whether or not we should skip frames with the
620 __tracebackhide__ attribute.
621 __tracebackhide__ attribute.
621 """
622 """
622 if not arg.strip():
623 if not arg.strip():
623 print(
624 print(
624 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
625 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
625 )
626 )
626 elif arg.strip().lower() in ("true", "yes"):
627 elif arg.strip().lower() in ("true", "yes"):
627 self.skip_hidden = True
628 self.skip_hidden = True
628 elif arg.strip().lower() in ("false", "no"):
629 elif arg.strip().lower() in ("false", "no"):
629 self.skip_hidden = False
630 self.skip_hidden = False
630 if not any(self._predicates.values()):
631 if not any(self._predicates.values()):
631 print(
632 print(
632 "Warning, all predicates set to False, skip_hidden may not have any effects."
633 "Warning, all predicates set to False, skip_hidden may not have any effects."
633 )
634 )
634
635
635 def do_list(self, arg):
636 def do_list(self, arg):
636 """Print lines of code from the current stack frame
637 """Print lines of code from the current stack frame
637 """
638 """
638 self.lastcmd = 'list'
639 self.lastcmd = 'list'
639 last = None
640 last = None
640 if arg:
641 if arg:
641 try:
642 try:
642 x = eval(arg, {}, {})
643 x = eval(arg, {}, {})
643 if type(x) == type(()):
644 if type(x) == type(()):
644 first, last = x
645 first, last = x
645 first = int(first)
646 first = int(first)
646 last = int(last)
647 last = int(last)
647 if last < first:
648 if last < first:
648 # Assume it's a count
649 # Assume it's a count
649 last = first + last
650 last = first + last
650 else:
651 else:
651 first = max(1, int(x) - 5)
652 first = max(1, int(x) - 5)
652 except:
653 except:
653 print('*** Error in argument:', repr(arg), file=self.stdout)
654 print('*** Error in argument:', repr(arg), file=self.stdout)
654 return
655 return
655 elif self.lineno is None:
656 elif self.lineno is None:
656 first = max(1, self.curframe.f_lineno - 5)
657 first = max(1, self.curframe.f_lineno - 5)
657 else:
658 else:
658 first = self.lineno + 1
659 first = self.lineno + 1
659 if last is None:
660 if last is None:
660 last = first + 10
661 last = first + 10
661 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
662 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
662
663
663 # vds: >>
664 # vds: >>
664 lineno = first
665 lineno = first
665 filename = self.curframe.f_code.co_filename
666 filename = self.curframe.f_code.co_filename
666 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
667 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
667 # vds: <<
668 # vds: <<
668
669
669 do_l = do_list
670 do_l = do_list
670
671
671 def getsourcelines(self, obj):
672 def getsourcelines(self, obj):
672 lines, lineno = inspect.findsource(obj)
673 lines, lineno = inspect.findsource(obj)
673 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
674 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
674 # must be a module frame: do not try to cut a block out of it
675 # must be a module frame: do not try to cut a block out of it
675 return lines, 1
676 return lines, 1
676 elif inspect.ismodule(obj):
677 elif inspect.ismodule(obj):
677 return lines, 1
678 return lines, 1
678 return inspect.getblock(lines[lineno:]), lineno+1
679 return inspect.getblock(lines[lineno:]), lineno+1
679
680
680 def do_longlist(self, arg):
681 def do_longlist(self, arg):
681 """Print lines of code from the current stack frame.
682 """Print lines of code from the current stack frame.
682
683
683 Shows more lines than 'list' does.
684 Shows more lines than 'list' does.
684 """
685 """
685 self.lastcmd = 'longlist'
686 self.lastcmd = 'longlist'
686 try:
687 try:
687 lines, lineno = self.getsourcelines(self.curframe)
688 lines, lineno = self.getsourcelines(self.curframe)
688 except OSError as err:
689 except OSError as err:
689 self.error(err)
690 self.error(err)
690 return
691 return
691 last = lineno + len(lines)
692 last = lineno + len(lines)
692 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
693 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
693 do_ll = do_longlist
694 do_ll = do_longlist
694
695
695 def do_debug(self, arg):
696 def do_debug(self, arg):
696 """debug code
697 """debug code
697 Enter a recursive debugger that steps through the code
698 Enter a recursive debugger that steps through the code
698 argument (which is an arbitrary expression or statement to be
699 argument (which is an arbitrary expression or statement to be
699 executed in the current environment).
700 executed in the current environment).
700 """
701 """
701 trace_function = sys.gettrace()
702 trace_function = sys.gettrace()
702 sys.settrace(None)
703 sys.settrace(None)
703 globals = self.curframe.f_globals
704 globals = self.curframe.f_globals
704 locals = self.curframe_locals
705 locals = self.curframe_locals
705 p = self.__class__(completekey=self.completekey,
706 p = self.__class__(completekey=self.completekey,
706 stdin=self.stdin, stdout=self.stdout)
707 stdin=self.stdin, stdout=self.stdout)
707 p.use_rawinput = self.use_rawinput
708 p.use_rawinput = self.use_rawinput
708 p.prompt = "(%s) " % self.prompt.strip()
709 p.prompt = "(%s) " % self.prompt.strip()
709 self.message("ENTERING RECURSIVE DEBUGGER")
710 self.message("ENTERING RECURSIVE DEBUGGER")
710 sys.call_tracing(p.run, (arg, globals, locals))
711 sys.call_tracing(p.run, (arg, globals, locals))
711 self.message("LEAVING RECURSIVE DEBUGGER")
712 self.message("LEAVING RECURSIVE DEBUGGER")
712 sys.settrace(trace_function)
713 sys.settrace(trace_function)
713 self.lastcmd = p.lastcmd
714 self.lastcmd = p.lastcmd
714
715
715 def do_pdef(self, arg):
716 def do_pdef(self, arg):
716 """Print the call signature for any callable object.
717 """Print the call signature for any callable object.
717
718
718 The debugger interface to %pdef"""
719 The debugger interface to %pdef"""
719 namespaces = [
720 namespaces = [
720 ("Locals", self.curframe_locals),
721 ("Locals", self.curframe_locals),
721 ("Globals", self.curframe.f_globals),
722 ("Globals", self.curframe.f_globals),
722 ]
723 ]
723 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
724 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
724
725
725 def do_pdoc(self, arg):
726 def do_pdoc(self, arg):
726 """Print the docstring for an object.
727 """Print the docstring for an object.
727
728
728 The debugger interface to %pdoc."""
729 The debugger interface to %pdoc."""
729 namespaces = [
730 namespaces = [
730 ("Locals", self.curframe_locals),
731 ("Locals", self.curframe_locals),
731 ("Globals", self.curframe.f_globals),
732 ("Globals", self.curframe.f_globals),
732 ]
733 ]
733 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
734 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
734
735
735 def do_pfile(self, arg):
736 def do_pfile(self, arg):
736 """Print (or run through pager) the file where an object is defined.
737 """Print (or run through pager) the file where an object is defined.
737
738
738 The debugger interface to %pfile.
739 The debugger interface to %pfile.
739 """
740 """
740 namespaces = [
741 namespaces = [
741 ("Locals", self.curframe_locals),
742 ("Locals", self.curframe_locals),
742 ("Globals", self.curframe.f_globals),
743 ("Globals", self.curframe.f_globals),
743 ]
744 ]
744 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
745 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
745
746
746 def do_pinfo(self, arg):
747 def do_pinfo(self, arg):
747 """Provide detailed information about an object.
748 """Provide detailed information about an object.
748
749
749 The debugger interface to %pinfo, i.e., obj?."""
750 The debugger interface to %pinfo, i.e., obj?."""
750 namespaces = [
751 namespaces = [
751 ("Locals", self.curframe_locals),
752 ("Locals", self.curframe_locals),
752 ("Globals", self.curframe.f_globals),
753 ("Globals", self.curframe.f_globals),
753 ]
754 ]
754 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
755 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
755
756
756 def do_pinfo2(self, arg):
757 def do_pinfo2(self, arg):
757 """Provide extra detailed information about an object.
758 """Provide extra detailed information about an object.
758
759
759 The debugger interface to %pinfo2, i.e., obj??."""
760 The debugger interface to %pinfo2, i.e., obj??."""
760 namespaces = [
761 namespaces = [
761 ("Locals", self.curframe_locals),
762 ("Locals", self.curframe_locals),
762 ("Globals", self.curframe.f_globals),
763 ("Globals", self.curframe.f_globals),
763 ]
764 ]
764 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
765 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
765
766
766 def do_psource(self, arg):
767 def do_psource(self, arg):
767 """Print (or run through pager) the source code for an object."""
768 """Print (or run through pager) the source code for an object."""
768 namespaces = [
769 namespaces = [
769 ("Locals", self.curframe_locals),
770 ("Locals", self.curframe_locals),
770 ("Globals", self.curframe.f_globals),
771 ("Globals", self.curframe.f_globals),
771 ]
772 ]
772 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
773 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
773
774
774 def do_where(self, arg):
775 def do_where(self, arg):
775 """w(here)
776 """w(here)
776 Print a stack trace, with the most recent frame at the bottom.
777 Print a stack trace, with the most recent frame at the bottom.
777 An arrow indicates the "current frame", which determines the
778 An arrow indicates the "current frame", which determines the
778 context of most commands. 'bt' is an alias for this command.
779 context of most commands. 'bt' is an alias for this command.
779
780
780 Take a number as argument as an (optional) number of context line to
781 Take a number as argument as an (optional) number of context line to
781 print"""
782 print"""
782 if arg:
783 if arg:
783 try:
784 try:
784 context = int(arg)
785 context = int(arg)
785 except ValueError as err:
786 except ValueError as err:
786 self.error(err)
787 self.error(err)
787 return
788 return
788 self.print_stack_trace(context)
789 self.print_stack_trace(context)
789 else:
790 else:
790 self.print_stack_trace()
791 self.print_stack_trace()
791
792
792 do_w = do_where
793 do_w = do_where
793
794
794 def stop_here(self, frame):
795 def stop_here(self, frame):
795 hidden = False
796 hidden = False
796 if self.skip_hidden:
797 if self.skip_hidden:
797 hidden = self._hidden_predicate(frame)
798 hidden = self._hidden_predicate(frame)
798 if hidden:
799 if hidden:
799 Colors = self.color_scheme_table.active_colors
800 if self.report_skipped:
800 ColorsNormal = Colors.Normal
801 Colors = self.color_scheme_table.active_colors
801 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
802 ColorsNormal = Colors.Normal
802
803 print(
804 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
805 )
803 return super().stop_here(frame)
806 return super().stop_here(frame)
804
807
805 def do_up(self, arg):
808 def do_up(self, arg):
806 """u(p) [count]
809 """u(p) [count]
807 Move the current frame count (default one) levels up in the
810 Move the current frame count (default one) levels up in the
808 stack trace (to an older frame).
811 stack trace (to an older frame).
809
812
810 Will skip hidden frames.
813 Will skip hidden frames.
811 """
814 """
812 # modified version of upstream that skips
815 # modified version of upstream that skips
813 # frames with __tracebackhide__
816 # frames with __tracebackhide__
814 if self.curindex == 0:
817 if self.curindex == 0:
815 self.error("Oldest frame")
818 self.error("Oldest frame")
816 return
819 return
817 try:
820 try:
818 count = int(arg or 1)
821 count = int(arg or 1)
819 except ValueError:
822 except ValueError:
820 self.error("Invalid frame count (%s)" % arg)
823 self.error("Invalid frame count (%s)" % arg)
821 return
824 return
822 skipped = 0
825 skipped = 0
823 if count < 0:
826 if count < 0:
824 _newframe = 0
827 _newframe = 0
825 else:
828 else:
826 counter = 0
829 counter = 0
827 hidden_frames = self.hidden_frames(self.stack)
830 hidden_frames = self.hidden_frames(self.stack)
828 for i in range(self.curindex - 1, -1, -1):
831 for i in range(self.curindex - 1, -1, -1):
829 if hidden_frames[i] and self.skip_hidden:
832 if hidden_frames[i] and self.skip_hidden:
830 skipped += 1
833 skipped += 1
831 continue
834 continue
832 counter += 1
835 counter += 1
833 if counter >= count:
836 if counter >= count:
834 break
837 break
835 else:
838 else:
836 # if no break occured.
839 # if no break occured.
837 self.error(
840 self.error(
838 "all frames above hidden, use `skip_hidden False` to get get into those."
841 "all frames above hidden, use `skip_hidden False` to get get into those."
839 )
842 )
840 return
843 return
841
844
842 Colors = self.color_scheme_table.active_colors
845 Colors = self.color_scheme_table.active_colors
843 ColorsNormal = Colors.Normal
846 ColorsNormal = Colors.Normal
844 _newframe = i
847 _newframe = i
845 self._select_frame(_newframe)
848 self._select_frame(_newframe)
846 if skipped:
849 if skipped:
847 print(
850 print(
848 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
851 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
849 )
852 )
850
853
851 def do_down(self, arg):
854 def do_down(self, arg):
852 """d(own) [count]
855 """d(own) [count]
853 Move the current frame count (default one) levels down in the
856 Move the current frame count (default one) levels down in the
854 stack trace (to a newer frame).
857 stack trace (to a newer frame).
855
858
856 Will skip hidden frames.
859 Will skip hidden frames.
857 """
860 """
858 if self.curindex + 1 == len(self.stack):
861 if self.curindex + 1 == len(self.stack):
859 self.error("Newest frame")
862 self.error("Newest frame")
860 return
863 return
861 try:
864 try:
862 count = int(arg or 1)
865 count = int(arg or 1)
863 except ValueError:
866 except ValueError:
864 self.error("Invalid frame count (%s)" % arg)
867 self.error("Invalid frame count (%s)" % arg)
865 return
868 return
866 if count < 0:
869 if count < 0:
867 _newframe = len(self.stack) - 1
870 _newframe = len(self.stack) - 1
868 else:
871 else:
869 counter = 0
872 counter = 0
870 skipped = 0
873 skipped = 0
871 hidden_frames = self.hidden_frames(self.stack)
874 hidden_frames = self.hidden_frames(self.stack)
872 for i in range(self.curindex + 1, len(self.stack)):
875 for i in range(self.curindex + 1, len(self.stack)):
873 if hidden_frames[i] and self.skip_hidden:
876 if hidden_frames[i] and self.skip_hidden:
874 skipped += 1
877 skipped += 1
875 continue
878 continue
876 counter += 1
879 counter += 1
877 if counter >= count:
880 if counter >= count:
878 break
881 break
879 else:
882 else:
880 self.error(
883 self.error(
881 "all frames bellow hidden, use `skip_hidden False` to get get into those."
884 "all frames bellow hidden, use `skip_hidden False` to get get into those."
882 )
885 )
883 return
886 return
884
887
885 Colors = self.color_scheme_table.active_colors
888 Colors = self.color_scheme_table.active_colors
886 ColorsNormal = Colors.Normal
889 ColorsNormal = Colors.Normal
887 if skipped:
890 if skipped:
888 print(
891 print(
889 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
892 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
890 )
893 )
891 _newframe = i
894 _newframe = i
892
895
893 self._select_frame(_newframe)
896 self._select_frame(_newframe)
894
897
895 do_d = do_down
898 do_d = do_down
896 do_u = do_up
899 do_u = do_up
897
900
898 def do_context(self, context):
901 def do_context(self, context):
899 """context number_of_lines
902 """context number_of_lines
900 Set the number of lines of source code to show when displaying
903 Set the number of lines of source code to show when displaying
901 stacktrace information.
904 stacktrace information.
902 """
905 """
903 try:
906 try:
904 new_context = int(context)
907 new_context = int(context)
905 if new_context <= 0:
908 if new_context <= 0:
906 raise ValueError()
909 raise ValueError()
907 self.context = new_context
910 self.context = new_context
908 except ValueError:
911 except ValueError:
909 self.error("The 'context' command requires a positive integer argument.")
912 self.error("The 'context' command requires a positive integer argument.")
910
913
911
914
912 class InterruptiblePdb(Pdb):
915 class InterruptiblePdb(Pdb):
913 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
916 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
914
917
915 def cmdloop(self):
918 def cmdloop(self):
916 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
919 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
917 try:
920 try:
918 return OldPdb.cmdloop(self)
921 return OldPdb.cmdloop(self)
919 except KeyboardInterrupt:
922 except KeyboardInterrupt:
920 self.stop_here = lambda frame: False
923 self.stop_here = lambda frame: False
921 self.do_quit("")
924 self.do_quit("")
922 sys.settrace(None)
925 sys.settrace(None)
923 self.quitting = False
926 self.quitting = False
924 raise
927 raise
925
928
926 def _cmdloop(self):
929 def _cmdloop(self):
927 while True:
930 while True:
928 try:
931 try:
929 # keyboard interrupts allow for an easy way to cancel
932 # keyboard interrupts allow for an easy way to cancel
930 # the current command, so allow them during interactive input
933 # the current command, so allow them during interactive input
931 self.allow_kbdint = True
934 self.allow_kbdint = True
932 self.cmdloop()
935 self.cmdloop()
933 self.allow_kbdint = False
936 self.allow_kbdint = False
934 break
937 break
935 except KeyboardInterrupt:
938 except KeyboardInterrupt:
936 self.message('--KeyboardInterrupt--')
939 self.message('--KeyboardInterrupt--')
937 raise
940 raise
938
941
939
942
940 def set_trace(frame=None):
943 def set_trace(frame=None):
941 """
944 """
942 Start debugging from `frame`.
945 Start debugging from `frame`.
943
946
944 If frame is not specified, debugging starts from caller's frame.
947 If frame is not specified, debugging starts from caller's frame.
945 """
948 """
946 Pdb().set_trace(frame or sys._getframe().f_back)
949 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now