##// END OF EJS Templates
Backport PR #9947: Add set_trace to core debugger....
Thomas Kluyver -
Show More
@@ -1,623 +1,632 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 http://www.python.org/2.2.3/license.html"""
16 http://www.python.org/2.2.3/license.html"""
17
17
18 #*****************************************************************************
18 #*****************************************************************************
19 #
19 #
20 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
21 #
21 #
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 #
24 #
25 #
25 #
26 #*****************************************************************************
26 #*****************************************************************************
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import sys
32 import sys
33 import warnings
33 import warnings
34
34
35 from IPython import get_ipython
35 from IPython import get_ipython
36 from IPython.utils import PyColorize, ulinecache
36 from IPython.utils import PyColorize, ulinecache
37 from IPython.utils import coloransi, py3compat
37 from IPython.utils import coloransi, py3compat
38 from IPython.core.excolors import exception_colors
38 from IPython.core.excolors import exception_colors
39 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.testing.skipdoctest import skip_doctest
40
40
41
41
42 prompt = 'ipdb> '
42 prompt = 'ipdb> '
43
43
44 #We have to check this directly from sys.argv, config struct not yet available
44 #We have to check this directly from sys.argv, config struct not yet available
45 from pdb import Pdb as OldPdb
45 from pdb import Pdb as OldPdb
46
46
47 # Allow the set_trace code to operate outside of an ipython instance, even if
47 # Allow the set_trace code to operate outside of an ipython instance, even if
48 # it does so with some limitations. The rest of this support is implemented in
48 # it does so with some limitations. The rest of this support is implemented in
49 # the Tracer constructor.
49 # the Tracer constructor.
50
50
51 def make_arrow(pad):
51 def make_arrow(pad):
52 """generate the leading arrow in front of traceback or debugger"""
52 """generate the leading arrow in front of traceback or debugger"""
53 if pad >= 2:
53 if pad >= 2:
54 return '-'*(pad-2) + '> '
54 return '-'*(pad-2) + '> '
55 elif pad == 1:
55 elif pad == 1:
56 return '>'
56 return '>'
57 return ''
57 return ''
58
58
59
59
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
61 """Exception hook which handles `BdbQuit` exceptions.
61 """Exception hook which handles `BdbQuit` exceptions.
62
62
63 All other exceptions are processed using the `excepthook`
63 All other exceptions are processed using the `excepthook`
64 parameter.
64 parameter.
65 """
65 """
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
67 DeprecationWarning)
67 DeprecationWarning)
68 if et==bdb.BdbQuit:
68 if et==bdb.BdbQuit:
69 print('Exiting Debugger.')
69 print('Exiting Debugger.')
70 elif excepthook is not None:
70 elif excepthook is not None:
71 excepthook(et, ev, tb)
71 excepthook(et, ev, tb)
72 else:
72 else:
73 # Backwards compatibility. Raise deprecation warning?
73 # Backwards compatibility. Raise deprecation warning?
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
75
75
76
76
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 warnings.warn(
78 warnings.warn(
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
80 DeprecationWarning)
80 DeprecationWarning)
81 print('Exiting Debugger.')
81 print('Exiting Debugger.')
82
82
83
83
84 class Tracer(object):
84 class Tracer(object):
85 """
85 """
86 DEPRECATED
86 DEPRECATED
87
87
88 Class for local debugging, similar to pdb.set_trace.
88 Class for local debugging, similar to pdb.set_trace.
89
89
90 Instances of this class, when called, behave like pdb.set_trace, but
90 Instances of this class, when called, behave like pdb.set_trace, but
91 providing IPython's enhanced capabilities.
91 providing IPython's enhanced capabilities.
92
92
93 This is implemented as a class which must be initialized in your own code
93 This is implemented as a class which must be initialized in your own code
94 and not as a standalone function because we need to detect at runtime
94 and not as a standalone function because we need to detect at runtime
95 whether IPython is already active or not. That detection is done in the
95 whether IPython is already active or not. That detection is done in the
96 constructor, ensuring that this code plays nicely with a running IPython,
96 constructor, ensuring that this code plays nicely with a running IPython,
97 while functioning acceptably (though with limitations) if outside of it.
97 while functioning acceptably (though with limitations) if outside of it.
98 """
98 """
99
99
100 @skip_doctest
100 @skip_doctest
101 def __init__(self, colors=None):
101 def __init__(self, colors=None):
102 """
102 """
103 DEPRECATED
103 DEPRECATED
104
104
105 Create a local debugger instance.
105 Create a local debugger instance.
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109
109
110 colors : str, optional
110 colors : str, optional
111 The name of the color scheme to use, it must be one of IPython's
111 The name of the color scheme to use, it must be one of IPython's
112 valid color schemes. If not given, the function will default to
112 valid color schemes. If not given, the function will default to
113 the current IPython scheme when running inside IPython, and to
113 the current IPython scheme when running inside IPython, and to
114 'NoColor' otherwise.
114 'NoColor' otherwise.
115
115
116 Examples
116 Examples
117 --------
117 --------
118 ::
118 ::
119
119
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
121
121
122 Later in your code::
122 Later in your code::
123
123
124 debug_here() # -> will open up the debugger at that point.
124 debug_here() # -> will open up the debugger at that point.
125
125
126 Once the debugger activates, you can use all of its regular commands to
126 Once the debugger activates, you can use all of its regular commands to
127 step through code, set breakpoints, etc. See the pdb documentation
127 step through code, set breakpoints, etc. See the pdb documentation
128 from the Python standard library for usage details.
128 from the Python standard library for usage details.
129 """
129 """
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
131 "`IPython.core.debugger.Pdb.set_trace()`",
131 "`IPython.core.debugger.Pdb.set_trace()`",
132 DeprecationWarning)
132 DeprecationWarning)
133
133
134 ip = get_ipython()
134 ip = get_ipython()
135 if ip is None:
135 if ip is None:
136 # Outside of ipython, we set our own exception hook manually
136 # Outside of ipython, we set our own exception hook manually
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
138 excepthook=sys.excepthook)
138 excepthook=sys.excepthook)
139 def_colors = 'NoColor'
139 def_colors = 'NoColor'
140 else:
140 else:
141 # In ipython, we use its custom exception handler mechanism
141 # In ipython, we use its custom exception handler mechanism
142 def_colors = ip.colors
142 def_colors = ip.colors
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
144
144
145 if colors is None:
145 if colors is None:
146 colors = def_colors
146 colors = def_colors
147
147
148 # The stdlib debugger internally uses a modified repr from the `repr`
148 # The stdlib debugger internally uses a modified repr from the `repr`
149 # module, that limits the length of printed strings to a hardcoded
149 # module, that limits the length of printed strings to a hardcoded
150 # limit of 30 characters. That much trimming is too aggressive, let's
150 # limit of 30 characters. That much trimming is too aggressive, let's
151 # at least raise that limit to 80 chars, which should be enough for
151 # at least raise that limit to 80 chars, which should be enough for
152 # most interactive uses.
152 # most interactive uses.
153 try:
153 try:
154 try:
154 try:
155 from reprlib import aRepr # Py 3
155 from reprlib import aRepr # Py 3
156 except ImportError:
156 except ImportError:
157 from repr import aRepr # Py 2
157 from repr import aRepr # Py 2
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 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
178 """Make new_fn have old_fn's doc string. This is particularly useful
178 """Make new_fn have old_fn's doc string. This is particularly useful
179 for the ``do_...`` commands that hook into the help system.
179 for the ``do_...`` commands that hook into the help system.
180 Adapted from from a comp.lang.python posting
180 Adapted from from a comp.lang.python posting
181 by Duncan Booth."""
181 by Duncan Booth."""
182 def wrapper(*args, **kw):
182 def wrapper(*args, **kw):
183 return new_fn(*args, **kw)
183 return new_fn(*args, **kw)
184 if old_fn.__doc__:
184 if old_fn.__doc__:
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
186 return wrapper
186 return wrapper
187
187
188
188
189 def _file_lines(fname):
189 def _file_lines(fname):
190 """Return the contents of a named file as a list of lines.
190 """Return the contents of a named file as a list of lines.
191
191
192 This function never raises an IOError exception: if the file can't be
192 This function never raises an IOError exception: if the file can't be
193 read, it simply returns an empty list."""
193 read, it simply returns an empty list."""
194
194
195 try:
195 try:
196 outfile = open(fname)
196 outfile = open(fname)
197 except IOError:
197 except IOError:
198 return []
198 return []
199 else:
199 else:
200 out = outfile.readlines()
200 out = outfile.readlines()
201 outfile.close()
201 outfile.close()
202 return out
202 return out
203
203
204
204
205 class Pdb(OldPdb, object):
205 class Pdb(OldPdb, object):
206 """Modified Pdb class, does not load readline.
206 """Modified Pdb class, does not load readline.
207
207
208 for a standalone version that uses prompt_toolkit, see
208 for a standalone version that uses prompt_toolkit, see
209 `IPython.terminal.debugger.TerminalPdb` and
209 `IPython.terminal.debugger.TerminalPdb` and
210 `IPython.terminal.debugger.set_trace()`
210 `IPython.terminal.debugger.set_trace()`
211 """
211 """
212
212
213 def __init__(self, color_scheme=None, completekey=None,
213 def __init__(self, color_scheme=None, completekey=None,
214 stdin=None, stdout=None, context=5):
214 stdin=None, stdout=None, context=5):
215
215
216 # Parent constructor:
216 # Parent constructor:
217 try:
217 try:
218 self.context = int(context)
218 self.context = int(context)
219 if self.context <= 0:
219 if self.context <= 0:
220 raise ValueError("Context must be a positive integer")
220 raise ValueError("Context must be a positive integer")
221 except (TypeError, ValueError):
221 except (TypeError, ValueError):
222 raise ValueError("Context must be a positive integer")
222 raise ValueError("Context must be a positive integer")
223
223
224 OldPdb.__init__(self, completekey, stdin, stdout)
224 OldPdb.__init__(self, completekey, stdin, stdout)
225
225
226 # IPython changes...
226 # IPython changes...
227 self.shell = get_ipython()
227 self.shell = get_ipython()
228
228
229 if self.shell is None:
229 if self.shell is None:
230 # No IPython instance running, we must create one
230 # No IPython instance running, we must create one
231 from IPython.terminal.interactiveshell import \
231 from IPython.terminal.interactiveshell import \
232 TerminalInteractiveShell
232 TerminalInteractiveShell
233 self.shell = TerminalInteractiveShell.instance()
233 self.shell = TerminalInteractiveShell.instance()
234
234
235 if color_scheme is not None:
235 if color_scheme is not None:
236 warnings.warn(
236 warnings.warn(
237 "The `color_scheme` argument is deprecated since version 5.1",
237 "The `color_scheme` argument is deprecated since version 5.1",
238 DeprecationWarning)
238 DeprecationWarning)
239 else:
239 else:
240 color_scheme = self.shell.colors
240 color_scheme = self.shell.colors
241
241
242 self.aliases = {}
242 self.aliases = {}
243
243
244 # Create color table: we copy the default one from the traceback
244 # Create color table: we copy the default one from the traceback
245 # module and add a few attributes needed for debugging
245 # module and add a few attributes needed for debugging
246 self.color_scheme_table = exception_colors()
246 self.color_scheme_table = exception_colors()
247
247
248 # shorthands
248 # shorthands
249 C = coloransi.TermColors
249 C = coloransi.TermColors
250 cst = self.color_scheme_table
250 cst = self.color_scheme_table
251
251
252 cst['NoColor'].colors.prompt = C.NoColor
252 cst['NoColor'].colors.prompt = C.NoColor
253 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
253 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
254 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
254 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
255
255
256 cst['Linux'].colors.prompt = C.Green
256 cst['Linux'].colors.prompt = C.Green
257 cst['Linux'].colors.breakpoint_enabled = C.LightRed
257 cst['Linux'].colors.breakpoint_enabled = C.LightRed
258 cst['Linux'].colors.breakpoint_disabled = C.Red
258 cst['Linux'].colors.breakpoint_disabled = C.Red
259
259
260 cst['LightBG'].colors.prompt = C.Blue
260 cst['LightBG'].colors.prompt = C.Blue
261 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
261 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
262 cst['LightBG'].colors.breakpoint_disabled = C.Red
262 cst['LightBG'].colors.breakpoint_disabled = C.Red
263
263
264 cst['Neutral'].colors.prompt = C.Blue
264 cst['Neutral'].colors.prompt = C.Blue
265 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
265 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
266 cst['Neutral'].colors.breakpoint_disabled = C.Red
266 cst['Neutral'].colors.breakpoint_disabled = C.Red
267
267
268 self.set_colors(color_scheme)
268 self.set_colors(color_scheme)
269
269
270 # Add a python parser so we can syntax highlight source while
270 # Add a python parser so we can syntax highlight source while
271 # debugging.
271 # debugging.
272 self.parser = PyColorize.Parser()
272 self.parser = PyColorize.Parser()
273
273
274 # Set the prompt - the default prompt is '(Pdb)'
274 # Set the prompt - the default prompt is '(Pdb)'
275 self.prompt = prompt
275 self.prompt = prompt
276
276
277 def set_colors(self, scheme):
277 def set_colors(self, scheme):
278 """Shorthand access to the color table scheme selector method."""
278 """Shorthand access to the color table scheme selector method."""
279 self.color_scheme_table.set_active_scheme(scheme)
279 self.color_scheme_table.set_active_scheme(scheme)
280
280
281 def trace_dispatch(self, frame, event, arg):
281 def trace_dispatch(self, frame, event, arg):
282 try:
282 try:
283 return super(Pdb, self).trace_dispatch(frame, event, arg)
283 return super(Pdb, self).trace_dispatch(frame, event, arg)
284 except bdb.BdbQuit:
284 except bdb.BdbQuit:
285 pass
285 pass
286
286
287 def interaction(self, frame, traceback):
287 def interaction(self, frame, traceback):
288 try:
288 try:
289 OldPdb.interaction(self, frame, traceback)
289 OldPdb.interaction(self, frame, traceback)
290 except KeyboardInterrupt:
290 except KeyboardInterrupt:
291 sys.stdout.write('\n' + self.shell.get_exception_only())
291 sys.stdout.write('\n' + self.shell.get_exception_only())
292
292
293 def parseline(self, line):
293 def parseline(self, line):
294 if line.startswith("!!"):
294 if line.startswith("!!"):
295 # Force standard behavior.
295 # Force standard behavior.
296 return super(Pdb, self).parseline(line[2:])
296 return super(Pdb, self).parseline(line[2:])
297 # "Smart command mode" from pdb++: don't execute commands if a variable
297 # "Smart command mode" from pdb++: don't execute commands if a variable
298 # with the same name exists.
298 # with the same name exists.
299 cmd, arg, newline = super(Pdb, self).parseline(line)
299 cmd, arg, newline = super(Pdb, self).parseline(line)
300 # Fix for #9611: Do not trigger smart command if the command is `exit`
300 # Fix for #9611: Do not trigger smart command if the command is `exit`
301 # or `quit` and it would resolve to their *global* value (the
301 # or `quit` and it would resolve to their *global* value (the
302 # `ExitAutocall` object). Just checking that it is not present in the
302 # `ExitAutocall` object). Just checking that it is not present in the
303 # locals dict is not enough as locals and globals match at the
303 # locals dict is not enough as locals and globals match at the
304 # toplevel.
304 # toplevel.
305 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
305 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
306 and not (cmd in ["exit", "quit"]
306 and not (cmd in ["exit", "quit"]
307 and (self.curframe.f_locals is self.curframe.f_globals
307 and (self.curframe.f_locals is self.curframe.f_globals
308 or cmd not in self.curframe.f_locals))):
308 or cmd not in self.curframe.f_locals))):
309 return super(Pdb, self).parseline("!" + line)
309 return super(Pdb, self).parseline("!" + line)
310 return super(Pdb, self).parseline(line)
310 return super(Pdb, self).parseline(line)
311
311
312 def new_do_up(self, arg):
312 def new_do_up(self, arg):
313 OldPdb.do_up(self, arg)
313 OldPdb.do_up(self, arg)
314 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
314 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
315
315
316 def new_do_down(self, arg):
316 def new_do_down(self, arg):
317 OldPdb.do_down(self, arg)
317 OldPdb.do_down(self, arg)
318
318
319 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
319 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
320
320
321 def new_do_frame(self, arg):
321 def new_do_frame(self, arg):
322 OldPdb.do_frame(self, arg)
322 OldPdb.do_frame(self, arg)
323
323
324 def new_do_quit(self, arg):
324 def new_do_quit(self, arg):
325
325
326 if hasattr(self, 'old_all_completions'):
326 if hasattr(self, 'old_all_completions'):
327 self.shell.Completer.all_completions=self.old_all_completions
327 self.shell.Completer.all_completions=self.old_all_completions
328
328
329 return OldPdb.do_quit(self, arg)
329 return OldPdb.do_quit(self, arg)
330
330
331 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
331 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
332
332
333 def new_do_restart(self, arg):
333 def new_do_restart(self, arg):
334 """Restart command. In the context of ipython this is exactly the same
334 """Restart command. In the context of ipython this is exactly the same
335 thing as 'quit'."""
335 thing as 'quit'."""
336 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
336 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
337 return self.do_quit(arg)
337 return self.do_quit(arg)
338
338
339 def print_stack_trace(self, context=None):
339 def print_stack_trace(self, context=None):
340 if context is None:
340 if context is None:
341 context = self.context
341 context = self.context
342 try:
342 try:
343 context=int(context)
343 context=int(context)
344 if context <= 0:
344 if context <= 0:
345 raise ValueError("Context must be a positive integer")
345 raise ValueError("Context must be a positive integer")
346 except (TypeError, ValueError):
346 except (TypeError, ValueError):
347 raise ValueError("Context must be a positive integer")
347 raise ValueError("Context must be a positive integer")
348 try:
348 try:
349 for frame_lineno in self.stack:
349 for frame_lineno in self.stack:
350 self.print_stack_entry(frame_lineno, context=context)
350 self.print_stack_entry(frame_lineno, context=context)
351 except KeyboardInterrupt:
351 except KeyboardInterrupt:
352 pass
352 pass
353
353
354 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
354 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
355 context=None):
355 context=None):
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):
362 except (TypeError, ValueError):
363 raise ValueError("Context must be a positive integer")
363 raise ValueError("Context must be a positive integer")
364 print(self.format_stack_entry(frame_lineno, '', context))
364 print(self.format_stack_entry(frame_lineno, '', context))
365
365
366 # vds: >>
366 # vds: >>
367 frame, lineno = frame_lineno
367 frame, lineno = frame_lineno
368 filename = frame.f_code.co_filename
368 filename = frame.f_code.co_filename
369 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
369 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
370 # vds: <<
370 # vds: <<
371
371
372 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
372 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
373 if context is None:
373 if context is None:
374 context = self.context
374 context = self.context
375 try:
375 try:
376 context=int(context)
376 context=int(context)
377 if context <= 0:
377 if context <= 0:
378 print("Context must be a positive integer")
378 print("Context must be a positive integer")
379 except (TypeError, ValueError):
379 except (TypeError, ValueError):
380 print("Context must be a positive integer")
380 print("Context must be a positive integer")
381 try:
381 try:
382 import reprlib # Py 3
382 import reprlib # Py 3
383 except ImportError:
383 except ImportError:
384 import repr as reprlib # Py 2
384 import repr as reprlib # Py 2
385
385
386 ret = []
386 ret = []
387
387
388 Colors = self.color_scheme_table.active_colors
388 Colors = self.color_scheme_table.active_colors
389 ColorsNormal = Colors.Normal
389 ColorsNormal = Colors.Normal
390 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
390 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
391 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
391 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
392 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
392 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
393 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
393 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
394 ColorsNormal)
394 ColorsNormal)
395
395
396 frame, lineno = frame_lineno
396 frame, lineno = frame_lineno
397
397
398 return_value = ''
398 return_value = ''
399 if '__return__' in frame.f_locals:
399 if '__return__' in frame.f_locals:
400 rv = frame.f_locals['__return__']
400 rv = frame.f_locals['__return__']
401 #return_value += '->'
401 #return_value += '->'
402 return_value += reprlib.repr(rv) + '\n'
402 return_value += reprlib.repr(rv) + '\n'
403 ret.append(return_value)
403 ret.append(return_value)
404
404
405 #s = filename + '(' + `lineno` + ')'
405 #s = filename + '(' + `lineno` + ')'
406 filename = self.canonic(frame.f_code.co_filename)
406 filename = self.canonic(frame.f_code.co_filename)
407 link = tpl_link % py3compat.cast_unicode(filename)
407 link = tpl_link % py3compat.cast_unicode(filename)
408
408
409 if frame.f_code.co_name:
409 if frame.f_code.co_name:
410 func = frame.f_code.co_name
410 func = frame.f_code.co_name
411 else:
411 else:
412 func = "<lambda>"
412 func = "<lambda>"
413
413
414 call = ''
414 call = ''
415 if func != '?':
415 if func != '?':
416 if '__args__' in frame.f_locals:
416 if '__args__' in frame.f_locals:
417 args = reprlib.repr(frame.f_locals['__args__'])
417 args = reprlib.repr(frame.f_locals['__args__'])
418 else:
418 else:
419 args = '()'
419 args = '()'
420 call = tpl_call % (func, args)
420 call = tpl_call % (func, args)
421
421
422 # The level info should be generated in the same format pdb uses, to
422 # The level info should be generated in the same format pdb uses, to
423 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
423 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
424 if frame is self.curframe:
424 if frame is self.curframe:
425 ret.append('> ')
425 ret.append('> ')
426 else:
426 else:
427 ret.append(' ')
427 ret.append(' ')
428 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
428 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
429
429
430 start = lineno - 1 - context//2
430 start = lineno - 1 - context//2
431 lines = ulinecache.getlines(filename)
431 lines = ulinecache.getlines(filename)
432 start = min(start, len(lines) - context)
432 start = min(start, len(lines) - context)
433 start = max(start, 0)
433 start = max(start, 0)
434 lines = lines[start : start + context]
434 lines = lines[start : start + context]
435
435
436 for i,line in enumerate(lines):
436 for i,line in enumerate(lines):
437 show_arrow = (start + 1 + i == lineno)
437 show_arrow = (start + 1 + i == lineno)
438 linetpl = (frame is self.curframe or show_arrow) \
438 linetpl = (frame is self.curframe or show_arrow) \
439 and tpl_line_em \
439 and tpl_line_em \
440 or tpl_line
440 or tpl_line
441 ret.append(self.__format_line(linetpl, filename,
441 ret.append(self.__format_line(linetpl, filename,
442 start + 1 + i, line,
442 start + 1 + i, line,
443 arrow = show_arrow) )
443 arrow = show_arrow) )
444 return ''.join(ret)
444 return ''.join(ret)
445
445
446 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
446 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
447 bp_mark = ""
447 bp_mark = ""
448 bp_mark_color = ""
448 bp_mark_color = ""
449
449
450 scheme = self.color_scheme_table.active_scheme_name
450 scheme = self.color_scheme_table.active_scheme_name
451 new_line, err = self.parser.format2(line, 'str', scheme)
451 new_line, err = self.parser.format2(line, 'str', scheme)
452 if not err: line = new_line
452 if not err: line = new_line
453
453
454 bp = None
454 bp = None
455 if lineno in self.get_file_breaks(filename):
455 if lineno in self.get_file_breaks(filename):
456 bps = self.get_breaks(filename, lineno)
456 bps = self.get_breaks(filename, lineno)
457 bp = bps[-1]
457 bp = bps[-1]
458
458
459 if bp:
459 if bp:
460 Colors = self.color_scheme_table.active_colors
460 Colors = self.color_scheme_table.active_colors
461 bp_mark = str(bp.number)
461 bp_mark = str(bp.number)
462 bp_mark_color = Colors.breakpoint_enabled
462 bp_mark_color = Colors.breakpoint_enabled
463 if not bp.enabled:
463 if not bp.enabled:
464 bp_mark_color = Colors.breakpoint_disabled
464 bp_mark_color = Colors.breakpoint_disabled
465
465
466 numbers_width = 7
466 numbers_width = 7
467 if arrow:
467 if arrow:
468 # This is the line with the error
468 # This is the line with the error
469 pad = numbers_width - len(str(lineno)) - len(bp_mark)
469 pad = numbers_width - len(str(lineno)) - len(bp_mark)
470 num = '%s%s' % (make_arrow(pad), str(lineno))
470 num = '%s%s' % (make_arrow(pad), str(lineno))
471 else:
471 else:
472 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
472 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
473
473
474 return tpl_line % (bp_mark_color + bp_mark, num, line)
474 return tpl_line % (bp_mark_color + bp_mark, num, line)
475
475
476
476
477 def print_list_lines(self, filename, first, last):
477 def print_list_lines(self, filename, first, last):
478 """The printing (as opposed to the parsing part of a 'list'
478 """The printing (as opposed to the parsing part of a 'list'
479 command."""
479 command."""
480 try:
480 try:
481 Colors = self.color_scheme_table.active_colors
481 Colors = self.color_scheme_table.active_colors
482 ColorsNormal = Colors.Normal
482 ColorsNormal = Colors.Normal
483 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
483 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
484 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
484 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
485 src = []
485 src = []
486 if filename == "<string>" and hasattr(self, "_exec_filename"):
486 if filename == "<string>" and hasattr(self, "_exec_filename"):
487 filename = self._exec_filename
487 filename = self._exec_filename
488
488
489 for lineno in range(first, last+1):
489 for lineno in range(first, last+1):
490 line = ulinecache.getline(filename, lineno)
490 line = ulinecache.getline(filename, lineno)
491 if not line:
491 if not line:
492 break
492 break
493
493
494 if lineno == self.curframe.f_lineno:
494 if lineno == self.curframe.f_lineno:
495 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
495 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
496 else:
496 else:
497 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
497 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
498
498
499 src.append(line)
499 src.append(line)
500 self.lineno = lineno
500 self.lineno = lineno
501
501
502 print(''.join(src))
502 print(''.join(src))
503
503
504 except KeyboardInterrupt:
504 except KeyboardInterrupt:
505 pass
505 pass
506
506
507 def do_list(self, arg):
507 def do_list(self, arg):
508 self.lastcmd = 'list'
508 self.lastcmd = 'list'
509 last = None
509 last = None
510 if arg:
510 if arg:
511 try:
511 try:
512 x = eval(arg, {}, {})
512 x = eval(arg, {}, {})
513 if type(x) == type(()):
513 if type(x) == type(()):
514 first, last = x
514 first, last = x
515 first = int(first)
515 first = int(first)
516 last = int(last)
516 last = int(last)
517 if last < first:
517 if last < first:
518 # Assume it's a count
518 # Assume it's a count
519 last = first + last
519 last = first + last
520 else:
520 else:
521 first = max(1, int(x) - 5)
521 first = max(1, int(x) - 5)
522 except:
522 except:
523 print('*** Error in argument:', repr(arg))
523 print('*** Error in argument:', repr(arg))
524 return
524 return
525 elif self.lineno is None:
525 elif self.lineno is None:
526 first = max(1, self.curframe.f_lineno - 5)
526 first = max(1, self.curframe.f_lineno - 5)
527 else:
527 else:
528 first = self.lineno + 1
528 first = self.lineno + 1
529 if last is None:
529 if last is None:
530 last = first + 10
530 last = first + 10
531 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
531 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
532
532
533 # vds: >>
533 # vds: >>
534 lineno = first
534 lineno = first
535 filename = self.curframe.f_code.co_filename
535 filename = self.curframe.f_code.co_filename
536 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
536 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
537 # vds: <<
537 # vds: <<
538
538
539 do_l = do_list
539 do_l = do_list
540
540
541 def getsourcelines(self, obj):
541 def getsourcelines(self, obj):
542 lines, lineno = inspect.findsource(obj)
542 lines, lineno = inspect.findsource(obj)
543 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
543 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
544 # must be a module frame: do not try to cut a block out of it
544 # must be a module frame: do not try to cut a block out of it
545 return lines, 1
545 return lines, 1
546 elif inspect.ismodule(obj):
546 elif inspect.ismodule(obj):
547 return lines, 1
547 return lines, 1
548 return inspect.getblock(lines[lineno:]), lineno+1
548 return inspect.getblock(lines[lineno:]), lineno+1
549
549
550 def do_longlist(self, arg):
550 def do_longlist(self, arg):
551 self.lastcmd = 'longlist'
551 self.lastcmd = 'longlist'
552 try:
552 try:
553 lines, lineno = self.getsourcelines(self.curframe)
553 lines, lineno = self.getsourcelines(self.curframe)
554 except OSError as err:
554 except OSError as err:
555 self.error(err)
555 self.error(err)
556 return
556 return
557 last = lineno + len(lines)
557 last = lineno + len(lines)
558 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
558 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
559 do_ll = do_longlist
559 do_ll = do_longlist
560
560
561 def do_pdef(self, arg):
561 def do_pdef(self, arg):
562 """Print the call signature for any callable object.
562 """Print the call signature for any callable object.
563
563
564 The debugger interface to %pdef"""
564 The debugger interface to %pdef"""
565 namespaces = [('Locals', self.curframe.f_locals),
565 namespaces = [('Locals', self.curframe.f_locals),
566 ('Globals', self.curframe.f_globals)]
566 ('Globals', self.curframe.f_globals)]
567 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
567 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
568
568
569 def do_pdoc(self, arg):
569 def do_pdoc(self, arg):
570 """Print the docstring for an object.
570 """Print the docstring for an object.
571
571
572 The debugger interface to %pdoc."""
572 The debugger interface to %pdoc."""
573 namespaces = [('Locals', self.curframe.f_locals),
573 namespaces = [('Locals', self.curframe.f_locals),
574 ('Globals', self.curframe.f_globals)]
574 ('Globals', self.curframe.f_globals)]
575 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
575 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
576
576
577 def do_pfile(self, arg):
577 def do_pfile(self, arg):
578 """Print (or run through pager) the file where an object is defined.
578 """Print (or run through pager) the file where an object is defined.
579
579
580 The debugger interface to %pfile.
580 The debugger interface to %pfile.
581 """
581 """
582 namespaces = [('Locals', self.curframe.f_locals),
582 namespaces = [('Locals', self.curframe.f_locals),
583 ('Globals', self.curframe.f_globals)]
583 ('Globals', self.curframe.f_globals)]
584 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
584 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
585
585
586 def do_pinfo(self, arg):
586 def do_pinfo(self, arg):
587 """Provide detailed information about an object.
587 """Provide detailed information about an object.
588
588
589 The debugger interface to %pinfo, i.e., obj?."""
589 The debugger interface to %pinfo, i.e., obj?."""
590 namespaces = [('Locals', self.curframe.f_locals),
590 namespaces = [('Locals', self.curframe.f_locals),
591 ('Globals', self.curframe.f_globals)]
591 ('Globals', self.curframe.f_globals)]
592 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
592 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
593
593
594 def do_pinfo2(self, arg):
594 def do_pinfo2(self, arg):
595 """Provide extra detailed information about an object.
595 """Provide extra detailed information about an object.
596
596
597 The debugger interface to %pinfo2, i.e., obj??."""
597 The debugger interface to %pinfo2, i.e., obj??."""
598 namespaces = [('Locals', self.curframe.f_locals),
598 namespaces = [('Locals', self.curframe.f_locals),
599 ('Globals', self.curframe.f_globals)]
599 ('Globals', self.curframe.f_globals)]
600 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
600 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
601
601
602 def do_psource(self, arg):
602 def do_psource(self, arg):
603 """Print (or run through pager) the source code for an object."""
603 """Print (or run through pager) the source code for an object."""
604 namespaces = [('Locals', self.curframe.f_locals),
604 namespaces = [('Locals', self.curframe.f_locals),
605 ('Globals', self.curframe.f_globals)]
605 ('Globals', self.curframe.f_globals)]
606 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
606 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
607
607
608 if sys.version_info > (3, ):
608 if sys.version_info > (3, ):
609 def do_where(self, arg):
609 def do_where(self, arg):
610 """w(here)
610 """w(here)
611 Print a stack trace, with the most recent frame at the bottom.
611 Print a stack trace, with the most recent frame at the bottom.
612 An arrow indicates the "current frame", which determines the
612 An arrow indicates the "current frame", which determines the
613 context of most commands. 'bt' is an alias for this command.
613 context of most commands. 'bt' is an alias for this command.
614
614
615 Take a number as argument as an (optional) number of context line to
615 Take a number as argument as an (optional) number of context line to
616 print"""
616 print"""
617 if arg:
617 if arg:
618 context = int(arg)
618 context = int(arg)
619 self.print_stack_trace(context)
619 self.print_stack_trace(context)
620 else:
620 else:
621 self.print_stack_trace()
621 self.print_stack_trace()
622
622
623 do_w = do_where
623 do_w = do_where
624
625
626 def set_trace(frame=None):
627 """
628 Start debugging from `frame`.
629
630 If frame is not specified, debugging starts from caller's frame.
631 """
632 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,77 +1,85 b''
1 from IPython.core.debugger import Pdb
1 from IPython.core.debugger import Pdb
2
2
3 from IPython.core.completer import IPCompleter
3 from IPython.core.completer import IPCompleter
4 from .ptutils import IPythonPTCompleter
4 from .ptutils import IPythonPTCompleter
5
5
6 from prompt_toolkit.token import Token
6 from prompt_toolkit.token import Token
7 from prompt_toolkit.shortcuts import create_prompt_application
7 from prompt_toolkit.shortcuts import create_prompt_application
8 from prompt_toolkit.interface import CommandLineInterface
8 from prompt_toolkit.interface import CommandLineInterface
9 from prompt_toolkit.enums import EditingMode
9 from prompt_toolkit.enums import EditingMode
10 import sys
11
10
12
11 class TerminalPdb(Pdb):
13 class TerminalPdb(Pdb):
12 def __init__(self, *args, **kwargs):
14 def __init__(self, *args, **kwargs):
13 Pdb.__init__(self, *args, **kwargs)
15 Pdb.__init__(self, *args, **kwargs)
14 self._ptcomp = None
16 self._ptcomp = None
15 self.pt_init()
17 self.pt_init()
16
18
17 def pt_init(self):
19 def pt_init(self):
18 def get_prompt_tokens(cli):
20 def get_prompt_tokens(cli):
19 return [(Token.Prompt, self.prompt)]
21 return [(Token.Prompt, self.prompt)]
20
22
21 if self._ptcomp is None:
23 if self._ptcomp is None:
22 compl = IPCompleter(shell=self.shell,
24 compl = IPCompleter(shell=self.shell,
23 namespace={},
25 namespace={},
24 global_namespace={},
26 global_namespace={},
25 use_readline=False,
27 use_readline=False,
26 parent=self.shell,
28 parent=self.shell,
27 )
29 )
28 self._ptcomp = IPythonPTCompleter(compl)
30 self._ptcomp = IPythonPTCompleter(compl)
29
31
30 self._pt_app = create_prompt_application(
32 self._pt_app = create_prompt_application(
31 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
33 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
32 history=self.shell.debugger_history,
34 history=self.shell.debugger_history,
33 completer= self._ptcomp,
35 completer= self._ptcomp,
34 enable_history_search=True,
36 enable_history_search=True,
35 mouse_support=self.shell.mouse_support,
37 mouse_support=self.shell.mouse_support,
36 get_prompt_tokens=get_prompt_tokens
38 get_prompt_tokens=get_prompt_tokens
37 )
39 )
38 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
40 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
39
41
40 def cmdloop(self, intro=None):
42 def cmdloop(self, intro=None):
41 """Repeatedly issue a prompt, accept input, parse an initial prefix
43 """Repeatedly issue a prompt, accept input, parse an initial prefix
42 off the received input, and dispatch to action methods, passing them
44 off the received input, and dispatch to action methods, passing them
43 the remainder of the line as argument.
45 the remainder of the line as argument.
44
46
45 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
47 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
46 """
48 """
47 if not self.use_rawinput:
49 if not self.use_rawinput:
48 raise ValueError('Sorry ipdb does not support use_rawinput=False')
50 raise ValueError('Sorry ipdb does not support use_rawinput=False')
49
51
50 self.preloop()
52 self.preloop()
51
53
52 try:
54 try:
53 if intro is not None:
55 if intro is not None:
54 self.intro = intro
56 self.intro = intro
55 if self.intro:
57 if self.intro:
56 self.stdout.write(str(self.intro)+"\n")
58 self.stdout.write(str(self.intro)+"\n")
57 stop = None
59 stop = None
58 while not stop:
60 while not stop:
59 if self.cmdqueue:
61 if self.cmdqueue:
60 line = self.cmdqueue.pop(0)
62 line = self.cmdqueue.pop(0)
61 else:
63 else:
62 self._ptcomp.ipy_completer.namespace = self.curframe_locals
64 self._ptcomp.ipy_completer.namespace = self.curframe_locals
63 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
65 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
64 try:
66 try:
65 line = self.pt_cli.run(reset_current_buffer=True).text
67 line = self.pt_cli.run(reset_current_buffer=True).text
66 except EOFError:
68 except EOFError:
67 line = 'EOF'
69 line = 'EOF'
68 line = self.precmd(line)
70 line = self.precmd(line)
69 stop = self.onecmd(line)
71 stop = self.onecmd(line)
70 stop = self.postcmd(stop, line)
72 stop = self.postcmd(stop, line)
71 self.postloop()
73 self.postloop()
72 except Exception:
74 except Exception:
73 raise
75 raise
74
76
75 def set_trace():
77
76 TerminalPdb().set_trace()
78 def set_trace(frame=None):
79 """
80 Start debugging from `frame`.
81
82 If frame is not specified, debugging starts from caller's frame.
83 """
84 TerminalPdb().set_trace(frame or sys._getframe().f_back)
77
85
General Comments 0
You need to be logged in to leave comments. Login now