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