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