##// END OF EJS Templates
Darken terminal.debugger and core.debugger
Blazej Michalik -
Show More
@@ -1,840 +1,843 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 = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
417 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
418 tpl_call = u'%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 = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
419 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
420 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
420 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
421 ColorsNormal)
422
421
423 frame, lineno = frame_lineno
422 frame, lineno = frame_lineno
424
423
425 return_value = ''
424 return_value = ''
426 if '__return__' in frame.f_locals:
425 if '__return__' in frame.f_locals:
427 rv = frame.f_locals['__return__']
426 rv = frame.f_locals['__return__']
428 #return_value += '->'
427 #return_value += '->'
429 return_value += reprlib.repr(rv) + '\n'
428 return_value += reprlib.repr(rv) + '\n'
430 ret.append(return_value)
429 ret.append(return_value)
431
430
432 #s = filename + '(' + `lineno` + ')'
431 #s = filename + '(' + `lineno` + ')'
433 filename = self.canonic(frame.f_code.co_filename)
432 filename = self.canonic(frame.f_code.co_filename)
434 link = tpl_link % py3compat.cast_unicode(filename)
433 link = tpl_link % py3compat.cast_unicode(filename)
435
434
436 if frame.f_code.co_name:
435 if frame.f_code.co_name:
437 func = frame.f_code.co_name
436 func = frame.f_code.co_name
438 else:
437 else:
439 func = "<lambda>"
438 func = "<lambda>"
440
439
441 call = ''
440 call = ''
442 if func != '?':
441 if func != '?':
443 if '__args__' in frame.f_locals:
442 if '__args__' in frame.f_locals:
444 args = reprlib.repr(frame.f_locals['__args__'])
443 args = reprlib.repr(frame.f_locals['__args__'])
445 else:
444 else:
446 args = '()'
445 args = '()'
447 call = tpl_call % (func, args)
446 call = tpl_call % (func, args)
448
447
449 # 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
450 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
449 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
451 if frame is self.curframe:
450 if frame is self.curframe:
452 ret.append('> ')
451 ret.append('> ')
453 else:
452 else:
454 ret.append(' ')
453 ret.append(" ")
455 ret.append(u'%s(%s)%s\n' % (link, lineno, call))
454 ret.append("%s(%s)%s\n" % (link, lineno, call))
456
455
457 start = lineno - 1 - context//2
456 start = lineno - 1 - context//2
458 lines = linecache.getlines(filename)
457 lines = linecache.getlines(filename)
459 start = min(start, len(lines) - context)
458 start = min(start, len(lines) - context)
460 start = max(start, 0)
459 start = max(start, 0)
461 lines = lines[start : start + context]
460 lines = lines[start : start + context]
462
461
463 for i, line in enumerate(lines):
462 for i, line in enumerate(lines):
464 show_arrow = (start + 1 + i == lineno)
463 show_arrow = start + 1 + i == lineno
465 linetpl = (frame is self.curframe or show_arrow) \
464 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
466 and tpl_line_em \
465 ret.append(
467 or tpl_line
466 self.__format_line(
468 ret.append(self.__format_line(linetpl, filename,
467 linetpl, filename, start + 1 + i, line, arrow=show_arrow
469 start + 1 + i, line,
468 )
470 arrow=show_arrow))
469 )
471 return ''.join(ret)
470 return "".join(ret)
472
471
473 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
472 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
474 bp_mark = ""
473 bp_mark = ""
475 bp_mark_color = ""
474 bp_mark_color = ""
476
475
477 new_line, err = self.parser.format2(line, 'str')
476 new_line, err = self.parser.format2(line, 'str')
478 if not err:
477 if not err:
479 line = new_line
478 line = new_line
480
479
481 bp = None
480 bp = None
482 if lineno in self.get_file_breaks(filename):
481 if lineno in self.get_file_breaks(filename):
483 bps = self.get_breaks(filename, lineno)
482 bps = self.get_breaks(filename, lineno)
484 bp = bps[-1]
483 bp = bps[-1]
485
484
486 if bp:
485 if bp:
487 Colors = self.color_scheme_table.active_colors
486 Colors = self.color_scheme_table.active_colors
488 bp_mark = str(bp.number)
487 bp_mark = str(bp.number)
489 bp_mark_color = Colors.breakpoint_enabled
488 bp_mark_color = Colors.breakpoint_enabled
490 if not bp.enabled:
489 if not bp.enabled:
491 bp_mark_color = Colors.breakpoint_disabled
490 bp_mark_color = Colors.breakpoint_disabled
492
491
493 numbers_width = 7
492 numbers_width = 7
494 if arrow:
493 if arrow:
495 # This is the line with the error
494 # This is the line with the error
496 pad = numbers_width - len(str(lineno)) - len(bp_mark)
495 pad = numbers_width - len(str(lineno)) - len(bp_mark)
497 num = '%s%s' % (make_arrow(pad), str(lineno))
496 num = '%s%s' % (make_arrow(pad), str(lineno))
498 else:
497 else:
499 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
498 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
500
499
501 return tpl_line % (bp_mark_color + bp_mark, num, line)
500 return tpl_line % (bp_mark_color + bp_mark, num, line)
502
501
503 def print_list_lines(self, filename, first, last):
502 def print_list_lines(self, filename, first, last):
504 """The printing (as opposed to the parsing part of a 'list'
503 """The printing (as opposed to the parsing part of a 'list'
505 command."""
504 command."""
506 try:
505 try:
507 Colors = self.color_scheme_table.active_colors
506 Colors = self.color_scheme_table.active_colors
508 ColorsNormal = Colors.Normal
507 ColorsNormal = Colors.Normal
509 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
508 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
510 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)
511 src = []
510 src = []
512 if filename == "<string>" and hasattr(self, "_exec_filename"):
511 if filename == "<string>" and hasattr(self, "_exec_filename"):
513 filename = self._exec_filename
512 filename = self._exec_filename
514
513
515 for lineno in range(first, last+1):
514 for lineno in range(first, last+1):
516 line = linecache.getline(filename, lineno)
515 line = linecache.getline(filename, lineno)
517 if not line:
516 if not line:
518 break
517 break
519
518
520 if lineno == self.curframe.f_lineno:
519 if lineno == self.curframe.f_lineno:
521 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow=True)
520 line = self.__format_line(
521 tpl_line_em, filename, lineno, line, arrow=True
522 )
522 else:
523 else:
523 line = self.__format_line(tpl_line, filename, lineno, line, arrow=False)
524 line = self.__format_line(
525 tpl_line, filename, lineno, line, arrow=False
526 )
524
527
525 src.append(line)
528 src.append(line)
526 self.lineno = lineno
529 self.lineno = lineno
527
530
528 print(''.join(src), file=self.stdout)
531 print(''.join(src), file=self.stdout)
529
532
530 except KeyboardInterrupt:
533 except KeyboardInterrupt:
531 pass
534 pass
532
535
533 def do_skip_hidden(self, arg):
536 def do_skip_hidden(self, arg):
534 """
537 """
535 Change whether or not we should skip frames with the
538 Change whether or not we should skip frames with the
536 __tracebackhide__ attribute.
539 __tracebackhide__ attribute.
537 """
540 """
538 if arg.strip().lower() in ("true", "yes"):
541 if arg.strip().lower() in ("true", "yes"):
539 self.skip_hidden = True
542 self.skip_hidden = True
540 elif arg.strip().lower() in ("false", "no"):
543 elif arg.strip().lower() in ("false", "no"):
541 self.skip_hidden = False
544 self.skip_hidden = False
542
545
543 def do_list(self, arg):
546 def do_list(self, arg):
544 """Print lines of code from the current stack frame
547 """Print lines of code from the current stack frame
545 """
548 """
546 self.lastcmd = 'list'
549 self.lastcmd = 'list'
547 last = None
550 last = None
548 if arg:
551 if arg:
549 try:
552 try:
550 x = eval(arg, {}, {})
553 x = eval(arg, {}, {})
551 if type(x) == type(()):
554 if type(x) == type(()):
552 first, last = x
555 first, last = x
553 first = int(first)
556 first = int(first)
554 last = int(last)
557 last = int(last)
555 if last < first:
558 if last < first:
556 # Assume it's a count
559 # Assume it's a count
557 last = first + last
560 last = first + last
558 else:
561 else:
559 first = max(1, int(x) - 5)
562 first = max(1, int(x) - 5)
560 except:
563 except:
561 print('*** Error in argument:', repr(arg), file=self.stdout)
564 print('*** Error in argument:', repr(arg), file=self.stdout)
562 return
565 return
563 elif self.lineno is None:
566 elif self.lineno is None:
564 first = max(1, self.curframe.f_lineno - 5)
567 first = max(1, self.curframe.f_lineno - 5)
565 else:
568 else:
566 first = self.lineno + 1
569 first = self.lineno + 1
567 if last is None:
570 if last is None:
568 last = first + 10
571 last = first + 10
569 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)
570
573
571 # vds: >>
574 # vds: >>
572 lineno = first
575 lineno = first
573 filename = self.curframe.f_code.co_filename
576 filename = self.curframe.f_code.co_filename
574 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
577 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
575 # vds: <<
578 # vds: <<
576
579
577 do_l = do_list
580 do_l = do_list
578
581
579 def getsourcelines(self, obj):
582 def getsourcelines(self, obj):
580 lines, lineno = inspect.findsource(obj)
583 lines, lineno = inspect.findsource(obj)
581 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:
582 # 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
583 return lines, 1
586 return lines, 1
584 elif inspect.ismodule(obj):
587 elif inspect.ismodule(obj):
585 return lines, 1
588 return lines, 1
586 return inspect.getblock(lines[lineno:]), lineno+1
589 return inspect.getblock(lines[lineno:]), lineno+1
587
590
588 def do_longlist(self, arg):
591 def do_longlist(self, arg):
589 """Print lines of code from the current stack frame.
592 """Print lines of code from the current stack frame.
590
593
591 Shows more lines than 'list' does.
594 Shows more lines than 'list' does.
592 """
595 """
593 self.lastcmd = 'longlist'
596 self.lastcmd = 'longlist'
594 try:
597 try:
595 lines, lineno = self.getsourcelines(self.curframe)
598 lines, lineno = self.getsourcelines(self.curframe)
596 except OSError as err:
599 except OSError as err:
597 self.error(err)
600 self.error(err)
598 return
601 return
599 last = lineno + len(lines)
602 last = lineno + len(lines)
600 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)
601 do_ll = do_longlist
604 do_ll = do_longlist
602
605
603 def do_debug(self, arg):
606 def do_debug(self, arg):
604 """debug code
607 """debug code
605 Enter a recursive debugger that steps through the code
608 Enter a recursive debugger that steps through the code
606 argument (which is an arbitrary expression or statement to be
609 argument (which is an arbitrary expression or statement to be
607 executed in the current environment).
610 executed in the current environment).
608 """
611 """
609 trace_function = sys.gettrace()
612 trace_function = sys.gettrace()
610 sys.settrace(None)
613 sys.settrace(None)
611 globals = self.curframe.f_globals
614 globals = self.curframe.f_globals
612 locals = self.curframe_locals
615 locals = self.curframe_locals
613 p = self.__class__(completekey=self.completekey,
616 p = self.__class__(completekey=self.completekey,
614 stdin=self.stdin, stdout=self.stdout)
617 stdin=self.stdin, stdout=self.stdout)
615 p.use_rawinput = self.use_rawinput
618 p.use_rawinput = self.use_rawinput
616 p.prompt = "(%s) " % self.prompt.strip()
619 p.prompt = "(%s) " % self.prompt.strip()
617 self.message("ENTERING RECURSIVE DEBUGGER")
620 self.message("ENTERING RECURSIVE DEBUGGER")
618 sys.call_tracing(p.run, (arg, globals, locals))
621 sys.call_tracing(p.run, (arg, globals, locals))
619 self.message("LEAVING RECURSIVE DEBUGGER")
622 self.message("LEAVING RECURSIVE DEBUGGER")
620 sys.settrace(trace_function)
623 sys.settrace(trace_function)
621 self.lastcmd = p.lastcmd
624 self.lastcmd = p.lastcmd
622
625
623 def do_pdef(self, arg):
626 def do_pdef(self, arg):
624 """Print the call signature for any callable object.
627 """Print the call signature for any callable object.
625
628
626 The debugger interface to %pdef"""
629 The debugger interface to %pdef"""
627 namespaces = [
630 namespaces = [
628 ("Locals", self.curframe_locals),
631 ("Locals", self.curframe_locals),
629 ("Globals", self.curframe.f_globals),
632 ("Globals", self.curframe.f_globals),
630 ]
633 ]
631 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
634 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
632
635
633 def do_pdoc(self, arg):
636 def do_pdoc(self, arg):
634 """Print the docstring for an object.
637 """Print the docstring for an object.
635
638
636 The debugger interface to %pdoc."""
639 The debugger interface to %pdoc."""
637 namespaces = [
640 namespaces = [
638 ("Locals", self.curframe_locals),
641 ("Locals", self.curframe_locals),
639 ("Globals", self.curframe.f_globals),
642 ("Globals", self.curframe.f_globals),
640 ]
643 ]
641 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
644 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
642
645
643 def do_pfile(self, arg):
646 def do_pfile(self, arg):
644 """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.
645
648
646 The debugger interface to %pfile.
649 The debugger interface to %pfile.
647 """
650 """
648 namespaces = [
651 namespaces = [
649 ("Locals", self.curframe_locals),
652 ("Locals", self.curframe_locals),
650 ("Globals", self.curframe.f_globals),
653 ("Globals", self.curframe.f_globals),
651 ]
654 ]
652 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
655 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
653
656
654 def do_pinfo(self, arg):
657 def do_pinfo(self, arg):
655 """Provide detailed information about an object.
658 """Provide detailed information about an object.
656
659
657 The debugger interface to %pinfo, i.e., obj?."""
660 The debugger interface to %pinfo, i.e., obj?."""
658 namespaces = [
661 namespaces = [
659 ("Locals", self.curframe_locals),
662 ("Locals", self.curframe_locals),
660 ("Globals", self.curframe.f_globals),
663 ("Globals", self.curframe.f_globals),
661 ]
664 ]
662 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
665 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
663
666
664 def do_pinfo2(self, arg):
667 def do_pinfo2(self, arg):
665 """Provide extra detailed information about an object.
668 """Provide extra detailed information about an object.
666
669
667 The debugger interface to %pinfo2, i.e., obj??."""
670 The debugger interface to %pinfo2, i.e., obj??."""
668 namespaces = [
671 namespaces = [
669 ("Locals", self.curframe_locals),
672 ("Locals", self.curframe_locals),
670 ("Globals", self.curframe.f_globals),
673 ("Globals", self.curframe.f_globals),
671 ]
674 ]
672 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
675 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
673
676
674 def do_psource(self, arg):
677 def do_psource(self, arg):
675 """Print (or run through pager) the source code for an object."""
678 """Print (or run through pager) the source code for an object."""
676 namespaces = [
679 namespaces = [
677 ("Locals", self.curframe_locals),
680 ("Locals", self.curframe_locals),
678 ("Globals", self.curframe.f_globals),
681 ("Globals", self.curframe.f_globals),
679 ]
682 ]
680 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
683 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
681
684
682 def do_where(self, arg):
685 def do_where(self, arg):
683 """w(here)
686 """w(here)
684 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.
685 An arrow indicates the "current frame", which determines the
688 An arrow indicates the "current frame", which determines the
686 context of most commands. 'bt' is an alias for this command.
689 context of most commands. 'bt' is an alias for this command.
687
690
688 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
689 print"""
692 print"""
690 if arg:
693 if arg:
691 try:
694 try:
692 context = int(arg)
695 context = int(arg)
693 except ValueError as err:
696 except ValueError as err:
694 self.error(err)
697 self.error(err)
695 return
698 return
696 self.print_stack_trace(context)
699 self.print_stack_trace(context)
697 else:
700 else:
698 self.print_stack_trace()
701 self.print_stack_trace()
699
702
700 do_w = do_where
703 do_w = do_where
701
704
702 def stop_here(self, frame):
705 def stop_here(self, frame):
703 hidden = False
706 hidden = False
704 if self.skip_hidden:
707 if self.skip_hidden:
705 hidden = frame.f_locals.get("__tracebackhide__", False)
708 hidden = frame.f_locals.get("__tracebackhide__", False)
706 if hidden:
709 if hidden:
707 Colors = self.color_scheme_table.active_colors
710 Colors = self.color_scheme_table.active_colors
708 ColorsNormal = Colors.Normal
711 ColorsNormal = Colors.Normal
709 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
712 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
710
713
711 return super().stop_here(frame)
714 return super().stop_here(frame)
712
715
713 def do_up(self, arg):
716 def do_up(self, arg):
714 """u(p) [count]
717 """u(p) [count]
715 Move the current frame count (default one) levels up in the
718 Move the current frame count (default one) levels up in the
716 stack trace (to an older frame).
719 stack trace (to an older frame).
717
720
718 Will skip hidden frames.
721 Will skip hidden frames.
719 """
722 """
720 # modified version of upstream that skips
723 # modified version of upstream that skips
721 # frames with __tracebackide__
724 # frames with __tracebackide__
722 if self.curindex == 0:
725 if self.curindex == 0:
723 self.error("Oldest frame")
726 self.error("Oldest frame")
724 return
727 return
725 try:
728 try:
726 count = int(arg or 1)
729 count = int(arg or 1)
727 except ValueError:
730 except ValueError:
728 self.error("Invalid frame count (%s)" % arg)
731 self.error("Invalid frame count (%s)" % arg)
729 return
732 return
730 skipped = 0
733 skipped = 0
731 if count < 0:
734 if count < 0:
732 _newframe = 0
735 _newframe = 0
733 else:
736 else:
734 counter = 0
737 counter = 0
735 hidden_frames = self.hidden_frames(self.stack)
738 hidden_frames = self.hidden_frames(self.stack)
736 for i in range(self.curindex - 1, -1, -1):
739 for i in range(self.curindex - 1, -1, -1):
737 if hidden_frames[i] and self.skip_hidden:
740 if hidden_frames[i] and self.skip_hidden:
738 skipped += 1
741 skipped += 1
739 continue
742 continue
740 counter += 1
743 counter += 1
741 if counter >= count:
744 if counter >= count:
742 break
745 break
743 else:
746 else:
744 # if no break occured.
747 # if no break occured.
745 self.error(
748 self.error(
746 "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."
747 )
750 )
748 return
751 return
749
752
750 Colors = self.color_scheme_table.active_colors
753 Colors = self.color_scheme_table.active_colors
751 ColorsNormal = Colors.Normal
754 ColorsNormal = Colors.Normal
752 _newframe = i
755 _newframe = i
753 self._select_frame(_newframe)
756 self._select_frame(_newframe)
754 if skipped:
757 if skipped:
755 print(
758 print(
756 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
759 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
757 )
760 )
758
761
759 def do_down(self, arg):
762 def do_down(self, arg):
760 """d(own) [count]
763 """d(own) [count]
761 Move the current frame count (default one) levels down in the
764 Move the current frame count (default one) levels down in the
762 stack trace (to a newer frame).
765 stack trace (to a newer frame).
763
766
764 Will skip hidden frames.
767 Will skip hidden frames.
765 """
768 """
766 if self.curindex + 1 == len(self.stack):
769 if self.curindex + 1 == len(self.stack):
767 self.error("Newest frame")
770 self.error("Newest frame")
768 return
771 return
769 try:
772 try:
770 count = int(arg or 1)
773 count = int(arg or 1)
771 except ValueError:
774 except ValueError:
772 self.error("Invalid frame count (%s)" % arg)
775 self.error("Invalid frame count (%s)" % arg)
773 return
776 return
774 if count < 0:
777 if count < 0:
775 _newframe = len(self.stack) - 1
778 _newframe = len(self.stack) - 1
776 else:
779 else:
777 counter = 0
780 counter = 0
778 skipped = 0
781 skipped = 0
779 hidden_frames = self.hidden_frames(self.stack)
782 hidden_frames = self.hidden_frames(self.stack)
780 for i in range(self.curindex + 1, len(self.stack)):
783 for i in range(self.curindex + 1, len(self.stack)):
781 if hidden_frames[i] and self.skip_hidden:
784 if hidden_frames[i] and self.skip_hidden:
782 skipped += 1
785 skipped += 1
783 continue
786 continue
784 counter += 1
787 counter += 1
785 if counter >= count:
788 if counter >= count:
786 break
789 break
787 else:
790 else:
788 self.error(
791 self.error(
789 "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."
790 )
793 )
791 return
794 return
792
795
793 Colors = self.color_scheme_table.active_colors
796 Colors = self.color_scheme_table.active_colors
794 ColorsNormal = Colors.Normal
797 ColorsNormal = Colors.Normal
795 if skipped:
798 if skipped:
796 print(
799 print(
797 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
800 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
798 )
801 )
799 _newframe = i
802 _newframe = i
800
803
801 self._select_frame(_newframe)
804 self._select_frame(_newframe)
802
805
803 do_d = do_down
806 do_d = do_down
804 do_u = do_up
807 do_u = do_up
805
808
806 class InterruptiblePdb(Pdb):
809 class InterruptiblePdb(Pdb):
807 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
810 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
808
811
809 def cmdloop(self):
812 def cmdloop(self):
810 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
813 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
811 try:
814 try:
812 return OldPdb.cmdloop(self)
815 return OldPdb.cmdloop(self)
813 except KeyboardInterrupt:
816 except KeyboardInterrupt:
814 self.stop_here = lambda frame: False
817 self.stop_here = lambda frame: False
815 self.do_quit("")
818 self.do_quit("")
816 sys.settrace(None)
819 sys.settrace(None)
817 self.quitting = False
820 self.quitting = False
818 raise
821 raise
819
822
820 def _cmdloop(self):
823 def _cmdloop(self):
821 while True:
824 while True:
822 try:
825 try:
823 # keyboard interrupts allow for an easy way to cancel
826 # keyboard interrupts allow for an easy way to cancel
824 # the current command, so allow them during interactive input
827 # the current command, so allow them during interactive input
825 self.allow_kbdint = True
828 self.allow_kbdint = True
826 self.cmdloop()
829 self.cmdloop()
827 self.allow_kbdint = False
830 self.allow_kbdint = False
828 break
831 break
829 except KeyboardInterrupt:
832 except KeyboardInterrupt:
830 self.message('--KeyboardInterrupt--')
833 self.message('--KeyboardInterrupt--')
831 raise
834 raise
832
835
833
836
834 def set_trace(frame=None):
837 def set_trace(frame=None):
835 """
838 """
836 Start debugging from `frame`.
839 Start debugging from `frame`.
837
840
838 If frame is not specified, debugging starts from caller's frame.
841 If frame is not specified, debugging starts from caller's frame.
839 """
842 """
840 Pdb().set_trace(frame or sys._getframe().f_back)
843 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,154 +1,153 b''
1 import asyncio
1 import asyncio
2 import sys
2 import sys
3 import threading
3 import threading
4
4
5 from IPython.core.debugger import Pdb
5 from IPython.core.debugger import Pdb
6
6
7 from IPython.core.completer import IPCompleter
7 from IPython.core.completer import IPCompleter
8 from .ptutils import IPythonPTCompleter
8 from .ptutils import IPythonPTCompleter
9 from .shortcuts import create_ipython_shortcuts
9 from .shortcuts import create_ipython_shortcuts
10
10
11 from pygments.token import Token
11 from pygments.token import Token
12 from prompt_toolkit.shortcuts.prompt import PromptSession
12 from prompt_toolkit.shortcuts.prompt import PromptSession
13 from prompt_toolkit.enums import EditingMode
13 from prompt_toolkit.enums import EditingMode
14 from prompt_toolkit.formatted_text import PygmentsTokens
14 from prompt_toolkit.formatted_text import PygmentsTokens
15
15
16 from prompt_toolkit import __version__ as ptk_version
16 from prompt_toolkit import __version__ as ptk_version
17 PTK3 = ptk_version.startswith('3.')
17 PTK3 = ptk_version.startswith('3.')
18
18
19
19
20 class TerminalPdb(Pdb):
20 class TerminalPdb(Pdb):
21 """Standalone IPython debugger."""
21 """Standalone IPython debugger."""
22
22
23 def __init__(self, *args, pt_session_options=None, **kwargs):
23 def __init__(self, *args, pt_session_options=None, **kwargs):
24 Pdb.__init__(self, *args, **kwargs)
24 Pdb.__init__(self, *args, **kwargs)
25 self._ptcomp = None
25 self._ptcomp = None
26 self.pt_init(pt_session_options)
26 self.pt_init(pt_session_options)
27
27
28 def pt_init(self, pt_session_options=None):
28 def pt_init(self, pt_session_options=None):
29 """Initialize the prompt session and the prompt loop
29 """Initialize the prompt session and the prompt loop
30 and store them in self.pt_app and self.pt_loop.
30 and store them in self.pt_app and self.pt_loop.
31
31
32 Additional keyword arguments for the PromptSession class
32 Additional keyword arguments for the PromptSession class
33 can be specified in pt_session_options.
33 can be specified in pt_session_options.
34 """
34 """
35 if pt_session_options is None:
35 if pt_session_options is None:
36 pt_session_options = {}
36 pt_session_options = {}
37
37
38 def get_prompt_tokens():
38 def get_prompt_tokens():
39 return [(Token.Prompt, self.prompt)]
39 return [(Token.Prompt, self.prompt)]
40
40
41 if self._ptcomp is None:
41 if self._ptcomp is None:
42 compl = IPCompleter(shell=self.shell,
42 compl = IPCompleter(
43 namespace={},
43 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
44 global_namespace={},
44 )
45 parent=self.shell)
46 # add a completer for all the do_ methods
45 # add a completer for all the do_ methods
47 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
46 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
48
47
49 def gen_comp(self, text):
48 def gen_comp(self, text):
50 return [m for m in methods_names if m.startswith(text)]
49 return [m for m in methods_names if m.startswith(text)]
51 import types
50 import types
52 newcomp = types.MethodType(gen_comp, compl)
51 newcomp = types.MethodType(gen_comp, compl)
53 compl.custom_matchers.insert(0, newcomp)
52 compl.custom_matchers.insert(0, newcomp)
54 # end add completer.
53 # end add completer.
55
54
56 self._ptcomp = IPythonPTCompleter(compl)
55 self._ptcomp = IPythonPTCompleter(compl)
57
56
58 options = dict(
57 options = dict(
59 message=(lambda: PygmentsTokens(get_prompt_tokens())),
58 message=(lambda: PygmentsTokens(get_prompt_tokens())),
60 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
59 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
61 key_bindings=create_ipython_shortcuts(self.shell),
60 key_bindings=create_ipython_shortcuts(self.shell),
62 history=self.shell.debugger_history,
61 history=self.shell.debugger_history,
63 completer=self._ptcomp,
62 completer=self._ptcomp,
64 enable_history_search=True,
63 enable_history_search=True,
65 mouse_support=self.shell.mouse_support,
64 mouse_support=self.shell.mouse_support,
66 complete_style=self.shell.pt_complete_style,
65 complete_style=self.shell.pt_complete_style,
67 style=self.shell.style,
66 style=self.shell.style,
68 color_depth=self.shell.color_depth,
67 color_depth=self.shell.color_depth,
69 )
68 )
70
69
71 if not PTK3:
70 if not PTK3:
72 options['inputhook'] = self.shell.inputhook
71 options['inputhook'] = self.shell.inputhook
73 options.update(pt_session_options)
72 options.update(pt_session_options)
74 self.pt_loop = asyncio.new_event_loop()
73 self.pt_loop = asyncio.new_event_loop()
75 self.pt_app = PromptSession(**options)
74 self.pt_app = PromptSession(**options)
76
75
77 def cmdloop(self, intro=None):
76 def cmdloop(self, intro=None):
78 """Repeatedly issue a prompt, accept input, parse an initial prefix
77 """Repeatedly issue a prompt, accept input, parse an initial prefix
79 off the received input, and dispatch to action methods, passing them
78 off the received input, and dispatch to action methods, passing them
80 the remainder of the line as argument.
79 the remainder of the line as argument.
81
80
82 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
81 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
83 """
82 """
84 if not self.use_rawinput:
83 if not self.use_rawinput:
85 raise ValueError('Sorry ipdb does not support use_rawinput=False')
84 raise ValueError('Sorry ipdb does not support use_rawinput=False')
86
85
87 # In order to make sure that prompt, which uses asyncio doesn't
86 # In order to make sure that prompt, which uses asyncio doesn't
88 # interfere with applications in which it's used, we always run the
87 # interfere with applications in which it's used, we always run the
89 # prompt itself in a different thread (we can't start an event loop
88 # prompt itself in a different thread (we can't start an event loop
90 # within an event loop). This new thread won't have any event loop
89 # within an event loop). This new thread won't have any event loop
91 # running, and here we run our prompt-loop.
90 # running, and here we run our prompt-loop.
92
91
93 self.preloop()
92 self.preloop()
94
93
95 try:
94 try:
96 if intro is not None:
95 if intro is not None:
97 self.intro = intro
96 self.intro = intro
98 if self.intro:
97 if self.intro:
99 self.stdout.write(str(self.intro)+"\n")
98 self.stdout.write(str(self.intro)+"\n")
100 stop = None
99 stop = None
101 while not stop:
100 while not stop:
102 if self.cmdqueue:
101 if self.cmdqueue:
103 line = self.cmdqueue.pop(0)
102 line = self.cmdqueue.pop(0)
104 else:
103 else:
105 self._ptcomp.ipy_completer.namespace = self.curframe_locals
104 self._ptcomp.ipy_completer.namespace = self.curframe_locals
106 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
105 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
107
106
108 # Run the prompt in a different thread.
107 # Run the prompt in a different thread.
109 line = ''
108 line = ''
110 keyboard_interrupt = False
109 keyboard_interrupt = False
111
110
112 def in_thread():
111 def in_thread():
113 nonlocal line, keyboard_interrupt
112 nonlocal line, keyboard_interrupt
114 try:
113 try:
115 line = self.pt_app.prompt()
114 line = self.pt_app.prompt()
116 except EOFError:
115 except EOFError:
117 line = 'EOF'
116 line = 'EOF'
118 except KeyboardInterrupt:
117 except KeyboardInterrupt:
119 keyboard_interrupt = True
118 keyboard_interrupt = True
120
119
121 th = threading.Thread(target=in_thread)
120 th = threading.Thread(target=in_thread)
122 th.start()
121 th.start()
123 th.join()
122 th.join()
124
123
125 if keyboard_interrupt:
124 if keyboard_interrupt:
126 raise KeyboardInterrupt
125 raise KeyboardInterrupt
127
126
128 line = self.precmd(line)
127 line = self.precmd(line)
129 stop = self.onecmd(line)
128 stop = self.onecmd(line)
130 stop = self.postcmd(stop, line)
129 stop = self.postcmd(stop, line)
131 self.postloop()
130 self.postloop()
132 except Exception:
131 except Exception:
133 raise
132 raise
134
133
135
134
136 def set_trace(frame=None):
135 def set_trace(frame=None):
137 """
136 """
138 Start debugging from `frame`.
137 Start debugging from `frame`.
139
138
140 If frame is not specified, debugging starts from caller's frame.
139 If frame is not specified, debugging starts from caller's frame.
141 """
140 """
142 TerminalPdb().set_trace(frame or sys._getframe().f_back)
141 TerminalPdb().set_trace(frame or sys._getframe().f_back)
143
142
144
143
145 if __name__ == '__main__':
144 if __name__ == '__main__':
146 import pdb
145 import pdb
147 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
146 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
148 # bdb.BdbQuit. When started through __main__ and an exception
147 # bdb.BdbQuit. When started through __main__ and an exception
149 # happened after hitting "c", this is needed in order to
148 # happened after hitting "c", this is needed in order to
150 # be able to quit the debugging session (see #9950).
149 # be able to quit the debugging session (see #9950).
151 old_trace_dispatch = pdb.Pdb.trace_dispatch
150 old_trace_dispatch = pdb.Pdb.trace_dispatch
152 pdb.Pdb = TerminalPdb
151 pdb.Pdb = TerminalPdb
153 pdb.Pdb.trace_dispatch = old_trace_dispatch
152 pdb.Pdb.trace_dispatch = old_trace_dispatch
154 pdb.main()
153 pdb.main()
General Comments 0
You need to be logged in to leave comments. Login now