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