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