##// END OF EJS Templates
Backport PR #2717: One liner to fix debugger printing stack traces when lines of context are larger than source....
MinRK -
Show More
@@ -1,526 +1,526 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
27
28 import bdb
28 import bdb
29 import linecache
29 import linecache
30 import sys
30 import sys
31
31
32 from IPython.utils import PyColorize
32 from IPython.utils import PyColorize
33 from IPython.core import ipapi
33 from IPython.core import ipapi
34 from IPython.utils import coloransi, io
34 from IPython.utils import coloransi, io
35 from IPython.core.excolors import exception_colors
35 from IPython.core.excolors import exception_colors
36
36
37 # See if we can use pydb.
37 # See if we can use pydb.
38 has_pydb = False
38 has_pydb = False
39 prompt = 'ipdb> '
39 prompt = 'ipdb> '
40 #We have to check this directly from sys.argv, config struct not yet available
40 #We have to check this directly from sys.argv, config struct not yet available
41 if '--pydb' in sys.argv:
41 if '--pydb' in sys.argv:
42 try:
42 try:
43 import pydb
43 import pydb
44 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
44 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
45 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
45 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
46 # better protect against it.
46 # better protect against it.
47 has_pydb = True
47 has_pydb = True
48 except ImportError:
48 except ImportError:
49 print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
49 print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
50
50
51 if has_pydb:
51 if has_pydb:
52 from pydb import Pdb as OldPdb
52 from pydb import Pdb as OldPdb
53 #print "Using pydb for %run -d and post-mortem" #dbg
53 #print "Using pydb for %run -d and post-mortem" #dbg
54 prompt = 'ipydb> '
54 prompt = 'ipydb> '
55 else:
55 else:
56 from pdb import Pdb as OldPdb
56 from pdb import Pdb as OldPdb
57
57
58 # Allow the set_trace code to operate outside of an ipython instance, even if
58 # Allow the set_trace code to operate outside of an ipython instance, even if
59 # it does so with some limitations. The rest of this support is implemented in
59 # it does so with some limitations. The rest of this support is implemented in
60 # the Tracer constructor.
60 # the Tracer constructor.
61 def BdbQuit_excepthook(et,ev,tb):
61 def BdbQuit_excepthook(et,ev,tb):
62 if et==bdb.BdbQuit:
62 if et==bdb.BdbQuit:
63 print 'Exiting Debugger.'
63 print 'Exiting Debugger.'
64 else:
64 else:
65 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
65 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
66
66
67 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
67 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
68 print 'Exiting Debugger.'
68 print 'Exiting Debugger.'
69
69
70
70
71 class Tracer(object):
71 class Tracer(object):
72 """Class for local debugging, similar to pdb.set_trace.
72 """Class for local debugging, similar to pdb.set_trace.
73
73
74 Instances of this class, when called, behave like pdb.set_trace, but
74 Instances of this class, when called, behave like pdb.set_trace, but
75 providing IPython's enhanced capabilities.
75 providing IPython's enhanced capabilities.
76
76
77 This is implemented as a class which must be initialized in your own code
77 This is implemented as a class which must be initialized in your own code
78 and not as a standalone function because we need to detect at runtime
78 and not as a standalone function because we need to detect at runtime
79 whether IPython is already active or not. That detection is done in the
79 whether IPython is already active or not. That detection is done in the
80 constructor, ensuring that this code plays nicely with a running IPython,
80 constructor, ensuring that this code plays nicely with a running IPython,
81 while functioning acceptably (though with limitations) if outside of it.
81 while functioning acceptably (though with limitations) if outside of it.
82 """
82 """
83
83
84 def __init__(self,colors=None):
84 def __init__(self,colors=None):
85 """Create a local debugger instance.
85 """Create a local debugger instance.
86
86
87 :Parameters:
87 :Parameters:
88
88
89 - `colors` (None): a string containing the name of the color scheme to
89 - `colors` (None): a string containing the name of the color scheme to
90 use, it must be one of IPython's valid color schemes. If not given, the
90 use, it must be one of IPython's valid color schemes. If not given, the
91 function will default to the current IPython scheme when running inside
91 function will default to the current IPython scheme when running inside
92 IPython, and to 'NoColor' otherwise.
92 IPython, and to 'NoColor' otherwise.
93
93
94 Usage example:
94 Usage example:
95
95
96 from IPython.core.debugger import Tracer; debug_here = Tracer()
96 from IPython.core.debugger import Tracer; debug_here = Tracer()
97
97
98 ... later in your code
98 ... later in your code
99 debug_here() # -> will open up the debugger at that point.
99 debug_here() # -> will open up the debugger at that point.
100
100
101 Once the debugger activates, you can use all of its regular commands to
101 Once the debugger activates, you can use all of its regular commands to
102 step through code, set breakpoints, etc. See the pdb documentation
102 step through code, set breakpoints, etc. See the pdb documentation
103 from the Python standard library for usage details.
103 from the Python standard library for usage details.
104 """
104 """
105
105
106 try:
106 try:
107 ip = get_ipython()
107 ip = get_ipython()
108 except NameError:
108 except NameError:
109 # Outside of ipython, we set our own exception hook manually
109 # Outside of ipython, we set our own exception hook manually
110 BdbQuit_excepthook.excepthook_ori = sys.excepthook
110 BdbQuit_excepthook.excepthook_ori = sys.excepthook
111 sys.excepthook = BdbQuit_excepthook
111 sys.excepthook = BdbQuit_excepthook
112 def_colors = 'NoColor'
112 def_colors = 'NoColor'
113 try:
113 try:
114 # Limited tab completion support
114 # Limited tab completion support
115 import readline
115 import readline
116 readline.parse_and_bind('tab: complete')
116 readline.parse_and_bind('tab: complete')
117 except ImportError:
117 except ImportError:
118 pass
118 pass
119 else:
119 else:
120 # In ipython, we use its custom exception handler mechanism
120 # In ipython, we use its custom exception handler mechanism
121 def_colors = ip.colors
121 def_colors = ip.colors
122 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
122 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
123
123
124 if colors is None:
124 if colors is None:
125 colors = def_colors
125 colors = def_colors
126
126
127 # The stdlib debugger internally uses a modified repr from the `repr`
127 # The stdlib debugger internally uses a modified repr from the `repr`
128 # module, that limits the length of printed strings to a hardcoded
128 # module, that limits the length of printed strings to a hardcoded
129 # limit of 30 characters. That much trimming is too aggressive, let's
129 # limit of 30 characters. That much trimming is too aggressive, let's
130 # at least raise that limit to 80 chars, which should be enough for
130 # at least raise that limit to 80 chars, which should be enough for
131 # most interactive uses.
131 # most interactive uses.
132 try:
132 try:
133 from repr import aRepr
133 from repr import aRepr
134 aRepr.maxstring = 80
134 aRepr.maxstring = 80
135 except:
135 except:
136 # This is only a user-facing convenience, so any error we encounter
136 # This is only a user-facing convenience, so any error we encounter
137 # here can be warned about but can be otherwise ignored. These
137 # here can be warned about but can be otherwise ignored. These
138 # printouts will tell us about problems if this API changes
138 # printouts will tell us about problems if this API changes
139 import traceback
139 import traceback
140 traceback.print_exc()
140 traceback.print_exc()
141
141
142 self.debugger = Pdb(colors)
142 self.debugger = Pdb(colors)
143
143
144 def __call__(self):
144 def __call__(self):
145 """Starts an interactive debugger at the point where called.
145 """Starts an interactive debugger at the point where called.
146
146
147 This is similar to the pdb.set_trace() function from the std lib, but
147 This is similar to the pdb.set_trace() function from the std lib, but
148 using IPython's enhanced debugger."""
148 using IPython's enhanced debugger."""
149
149
150 self.debugger.set_trace(sys._getframe().f_back)
150 self.debugger.set_trace(sys._getframe().f_back)
151
151
152
152
153 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
153 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
154 """Make new_fn have old_fn's doc string. This is particularly useful
154 """Make new_fn have old_fn's doc string. This is particularly useful
155 for the do_... commands that hook into the help system.
155 for the do_... commands that hook into the help system.
156 Adapted from from a comp.lang.python posting
156 Adapted from from a comp.lang.python posting
157 by Duncan Booth."""
157 by Duncan Booth."""
158 def wrapper(*args, **kw):
158 def wrapper(*args, **kw):
159 return new_fn(*args, **kw)
159 return new_fn(*args, **kw)
160 if old_fn.__doc__:
160 if old_fn.__doc__:
161 wrapper.__doc__ = old_fn.__doc__ + additional_text
161 wrapper.__doc__ = old_fn.__doc__ + additional_text
162 return wrapper
162 return wrapper
163
163
164
164
165 def _file_lines(fname):
165 def _file_lines(fname):
166 """Return the contents of a named file as a list of lines.
166 """Return the contents of a named file as a list of lines.
167
167
168 This function never raises an IOError exception: if the file can't be
168 This function never raises an IOError exception: if the file can't be
169 read, it simply returns an empty list."""
169 read, it simply returns an empty list."""
170
170
171 try:
171 try:
172 outfile = open(fname)
172 outfile = open(fname)
173 except IOError:
173 except IOError:
174 return []
174 return []
175 else:
175 else:
176 out = outfile.readlines()
176 out = outfile.readlines()
177 outfile.close()
177 outfile.close()
178 return out
178 return out
179
179
180
180
181 class Pdb(OldPdb):
181 class Pdb(OldPdb):
182 """Modified Pdb class, does not load readline."""
182 """Modified Pdb class, does not load readline."""
183
183
184 def __init__(self,color_scheme='NoColor',completekey=None,
184 def __init__(self,color_scheme='NoColor',completekey=None,
185 stdin=None, stdout=None):
185 stdin=None, stdout=None):
186
186
187 # Parent constructor:
187 # Parent constructor:
188 if has_pydb and completekey is None:
188 if has_pydb and completekey is None:
189 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
189 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
190 else:
190 else:
191 OldPdb.__init__(self,completekey,stdin,stdout)
191 OldPdb.__init__(self,completekey,stdin,stdout)
192
192
193 self.prompt = prompt # The default prompt is '(Pdb)'
193 self.prompt = prompt # The default prompt is '(Pdb)'
194
194
195 # IPython changes...
195 # IPython changes...
196 self.is_pydb = has_pydb
196 self.is_pydb = has_pydb
197
197
198 self.shell = ipapi.get()
198 self.shell = ipapi.get()
199
199
200 if self.is_pydb:
200 if self.is_pydb:
201
201
202 # interactiveshell.py's ipalias seems to want pdb's checkline
202 # interactiveshell.py's ipalias seems to want pdb's checkline
203 # which located in pydb.fn
203 # which located in pydb.fn
204 import pydb.fns
204 import pydb.fns
205 self.checkline = lambda filename, lineno: \
205 self.checkline = lambda filename, lineno: \
206 pydb.fns.checkline(self, filename, lineno)
206 pydb.fns.checkline(self, filename, lineno)
207
207
208 self.curframe = None
208 self.curframe = None
209 self.do_restart = self.new_do_restart
209 self.do_restart = self.new_do_restart
210
210
211 self.old_all_completions = self.shell.Completer.all_completions
211 self.old_all_completions = self.shell.Completer.all_completions
212 self.shell.Completer.all_completions=self.all_completions
212 self.shell.Completer.all_completions=self.all_completions
213
213
214 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
214 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
215 OldPdb.do_list)
215 OldPdb.do_list)
216 self.do_l = self.do_list
216 self.do_l = self.do_list
217 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
217 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
218 OldPdb.do_frame)
218 OldPdb.do_frame)
219
219
220 self.aliases = {}
220 self.aliases = {}
221
221
222 # Create color table: we copy the default one from the traceback
222 # Create color table: we copy the default one from the traceback
223 # module and add a few attributes needed for debugging
223 # module and add a few attributes needed for debugging
224 self.color_scheme_table = exception_colors()
224 self.color_scheme_table = exception_colors()
225
225
226 # shorthands
226 # shorthands
227 C = coloransi.TermColors
227 C = coloransi.TermColors
228 cst = self.color_scheme_table
228 cst = self.color_scheme_table
229
229
230 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
230 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
231 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
231 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
232
232
233 cst['Linux'].colors.breakpoint_enabled = C.LightRed
233 cst['Linux'].colors.breakpoint_enabled = C.LightRed
234 cst['Linux'].colors.breakpoint_disabled = C.Red
234 cst['Linux'].colors.breakpoint_disabled = C.Red
235
235
236 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
236 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
237 cst['LightBG'].colors.breakpoint_disabled = C.Red
237 cst['LightBG'].colors.breakpoint_disabled = C.Red
238
238
239 self.set_colors(color_scheme)
239 self.set_colors(color_scheme)
240
240
241 # Add a python parser so we can syntax highlight source while
241 # Add a python parser so we can syntax highlight source while
242 # debugging.
242 # debugging.
243 self.parser = PyColorize.Parser()
243 self.parser = PyColorize.Parser()
244
244
245 def set_colors(self, scheme):
245 def set_colors(self, scheme):
246 """Shorthand access to the color table scheme selector method."""
246 """Shorthand access to the color table scheme selector method."""
247 self.color_scheme_table.set_active_scheme(scheme)
247 self.color_scheme_table.set_active_scheme(scheme)
248
248
249 def interaction(self, frame, traceback):
249 def interaction(self, frame, traceback):
250 self.shell.set_completer_frame(frame)
250 self.shell.set_completer_frame(frame)
251 OldPdb.interaction(self, frame, traceback)
251 OldPdb.interaction(self, frame, traceback)
252
252
253 def new_do_up(self, arg):
253 def new_do_up(self, arg):
254 OldPdb.do_up(self, arg)
254 OldPdb.do_up(self, arg)
255 self.shell.set_completer_frame(self.curframe)
255 self.shell.set_completer_frame(self.curframe)
256 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
256 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
257
257
258 def new_do_down(self, arg):
258 def new_do_down(self, arg):
259 OldPdb.do_down(self, arg)
259 OldPdb.do_down(self, arg)
260 self.shell.set_completer_frame(self.curframe)
260 self.shell.set_completer_frame(self.curframe)
261
261
262 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
262 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
263
263
264 def new_do_frame(self, arg):
264 def new_do_frame(self, arg):
265 OldPdb.do_frame(self, arg)
265 OldPdb.do_frame(self, arg)
266 self.shell.set_completer_frame(self.curframe)
266 self.shell.set_completer_frame(self.curframe)
267
267
268 def new_do_quit(self, arg):
268 def new_do_quit(self, arg):
269
269
270 if hasattr(self, 'old_all_completions'):
270 if hasattr(self, 'old_all_completions'):
271 self.shell.Completer.all_completions=self.old_all_completions
271 self.shell.Completer.all_completions=self.old_all_completions
272
272
273
273
274 return OldPdb.do_quit(self, arg)
274 return OldPdb.do_quit(self, arg)
275
275
276 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
276 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
277
277
278 def new_do_restart(self, arg):
278 def new_do_restart(self, arg):
279 """Restart command. In the context of ipython this is exactly the same
279 """Restart command. In the context of ipython this is exactly the same
280 thing as 'quit'."""
280 thing as 'quit'."""
281 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
281 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
282 return self.do_quit(arg)
282 return self.do_quit(arg)
283
283
284 def postloop(self):
284 def postloop(self):
285 self.shell.set_completer_frame(None)
285 self.shell.set_completer_frame(None)
286
286
287 def print_stack_trace(self):
287 def print_stack_trace(self):
288 try:
288 try:
289 for frame_lineno in self.stack:
289 for frame_lineno in self.stack:
290 self.print_stack_entry(frame_lineno, context = 5)
290 self.print_stack_entry(frame_lineno, context = 5)
291 except KeyboardInterrupt:
291 except KeyboardInterrupt:
292 pass
292 pass
293
293
294 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
294 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
295 context = 3):
295 context = 3):
296 #frame, lineno = frame_lineno
296 #frame, lineno = frame_lineno
297 print >>io.stdout, self.format_stack_entry(frame_lineno, '', context)
297 print >>io.stdout, self.format_stack_entry(frame_lineno, '', context)
298
298
299 # vds: >>
299 # vds: >>
300 frame, lineno = frame_lineno
300 frame, lineno = frame_lineno
301 filename = frame.f_code.co_filename
301 filename = frame.f_code.co_filename
302 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
302 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
303 # vds: <<
303 # vds: <<
304
304
305 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
305 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
306 import linecache, repr
306 import linecache, repr
307
307
308 ret = []
308 ret = []
309
309
310 Colors = self.color_scheme_table.active_colors
310 Colors = self.color_scheme_table.active_colors
311 ColorsNormal = Colors.Normal
311 ColorsNormal = Colors.Normal
312 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
312 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
313 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
313 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
314 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
314 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
315 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
315 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
316 ColorsNormal)
316 ColorsNormal)
317
317
318 frame, lineno = frame_lineno
318 frame, lineno = frame_lineno
319
319
320 return_value = ''
320 return_value = ''
321 if '__return__' in frame.f_locals:
321 if '__return__' in frame.f_locals:
322 rv = frame.f_locals['__return__']
322 rv = frame.f_locals['__return__']
323 #return_value += '->'
323 #return_value += '->'
324 return_value += repr.repr(rv) + '\n'
324 return_value += repr.repr(rv) + '\n'
325 ret.append(return_value)
325 ret.append(return_value)
326
326
327 #s = filename + '(' + `lineno` + ')'
327 #s = filename + '(' + `lineno` + ')'
328 filename = self.canonic(frame.f_code.co_filename)
328 filename = self.canonic(frame.f_code.co_filename)
329 link = tpl_link % filename
329 link = tpl_link % filename
330
330
331 if frame.f_code.co_name:
331 if frame.f_code.co_name:
332 func = frame.f_code.co_name
332 func = frame.f_code.co_name
333 else:
333 else:
334 func = "<lambda>"
334 func = "<lambda>"
335
335
336 call = ''
336 call = ''
337 if func != '?':
337 if func != '?':
338 if '__args__' in frame.f_locals:
338 if '__args__' in frame.f_locals:
339 args = repr.repr(frame.f_locals['__args__'])
339 args = repr.repr(frame.f_locals['__args__'])
340 else:
340 else:
341 args = '()'
341 args = '()'
342 call = tpl_call % (func, args)
342 call = tpl_call % (func, args)
343
343
344 # The level info should be generated in the same format pdb uses, to
344 # The level info should be generated in the same format pdb uses, to
345 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
345 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
346 if frame is self.curframe:
346 if frame is self.curframe:
347 ret.append('> ')
347 ret.append('> ')
348 else:
348 else:
349 ret.append(' ')
349 ret.append(' ')
350 ret.append('%s(%s)%s\n' % (link,lineno,call))
350 ret.append('%s(%s)%s\n' % (link,lineno,call))
351
351
352 start = lineno - 1 - context//2
352 start = lineno - 1 - context//2
353 lines = linecache.getlines(filename)
353 lines = linecache.getlines(filename)
354 start = max(start, 0)
355 start = min(start, len(lines) - context)
354 start = min(start, len(lines) - context)
355 start = max(start, 0)
356 lines = lines[start : start + context]
356 lines = lines[start : start + context]
357
357
358 for i,line in enumerate(lines):
358 for i,line in enumerate(lines):
359 show_arrow = (start + 1 + i == lineno)
359 show_arrow = (start + 1 + i == lineno)
360 linetpl = (frame is self.curframe or show_arrow) \
360 linetpl = (frame is self.curframe or show_arrow) \
361 and tpl_line_em \
361 and tpl_line_em \
362 or tpl_line
362 or tpl_line
363 ret.append(self.__format_line(linetpl, filename,
363 ret.append(self.__format_line(linetpl, filename,
364 start + 1 + i, line,
364 start + 1 + i, line,
365 arrow = show_arrow) )
365 arrow = show_arrow) )
366
366
367 return ''.join(ret)
367 return ''.join(ret)
368
368
369 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
369 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
370 bp_mark = ""
370 bp_mark = ""
371 bp_mark_color = ""
371 bp_mark_color = ""
372
372
373 scheme = self.color_scheme_table.active_scheme_name
373 scheme = self.color_scheme_table.active_scheme_name
374 new_line, err = self.parser.format2(line, 'str', scheme)
374 new_line, err = self.parser.format2(line, 'str', scheme)
375 if not err: line = new_line
375 if not err: line = new_line
376
376
377 bp = None
377 bp = None
378 if lineno in self.get_file_breaks(filename):
378 if lineno in self.get_file_breaks(filename):
379 bps = self.get_breaks(filename, lineno)
379 bps = self.get_breaks(filename, lineno)
380 bp = bps[-1]
380 bp = bps[-1]
381
381
382 if bp:
382 if bp:
383 Colors = self.color_scheme_table.active_colors
383 Colors = self.color_scheme_table.active_colors
384 bp_mark = str(bp.number)
384 bp_mark = str(bp.number)
385 bp_mark_color = Colors.breakpoint_enabled
385 bp_mark_color = Colors.breakpoint_enabled
386 if not bp.enabled:
386 if not bp.enabled:
387 bp_mark_color = Colors.breakpoint_disabled
387 bp_mark_color = Colors.breakpoint_disabled
388
388
389 numbers_width = 7
389 numbers_width = 7
390 if arrow:
390 if arrow:
391 # This is the line with the error
391 # This is the line with the error
392 pad = numbers_width - len(str(lineno)) - len(bp_mark)
392 pad = numbers_width - len(str(lineno)) - len(bp_mark)
393 if pad >= 3:
393 if pad >= 3:
394 marker = '-'*(pad-3) + '-> '
394 marker = '-'*(pad-3) + '-> '
395 elif pad == 2:
395 elif pad == 2:
396 marker = '> '
396 marker = '> '
397 elif pad == 1:
397 elif pad == 1:
398 marker = '>'
398 marker = '>'
399 else:
399 else:
400 marker = ''
400 marker = ''
401 num = '%s%s' % (marker, str(lineno))
401 num = '%s%s' % (marker, str(lineno))
402 line = tpl_line % (bp_mark_color + bp_mark, num, line)
402 line = tpl_line % (bp_mark_color + bp_mark, num, line)
403 else:
403 else:
404 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
404 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
405 line = tpl_line % (bp_mark_color + bp_mark, num, line)
405 line = tpl_line % (bp_mark_color + bp_mark, num, line)
406
406
407 return line
407 return line
408
408
409 def list_command_pydb(self, arg):
409 def list_command_pydb(self, arg):
410 """List command to use if we have a newer pydb installed"""
410 """List command to use if we have a newer pydb installed"""
411 filename, first, last = OldPdb.parse_list_cmd(self, arg)
411 filename, first, last = OldPdb.parse_list_cmd(self, arg)
412 if filename is not None:
412 if filename is not None:
413 self.print_list_lines(filename, first, last)
413 self.print_list_lines(filename, first, last)
414
414
415 def print_list_lines(self, filename, first, last):
415 def print_list_lines(self, filename, first, last):
416 """The printing (as opposed to the parsing part of a 'list'
416 """The printing (as opposed to the parsing part of a 'list'
417 command."""
417 command."""
418 try:
418 try:
419 Colors = self.color_scheme_table.active_colors
419 Colors = self.color_scheme_table.active_colors
420 ColorsNormal = Colors.Normal
420 ColorsNormal = Colors.Normal
421 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
421 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
422 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
422 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
423 src = []
423 src = []
424 for lineno in range(first, last+1):
424 for lineno in range(first, last+1):
425 line = linecache.getline(filename, lineno)
425 line = linecache.getline(filename, lineno)
426 if not line:
426 if not line:
427 break
427 break
428
428
429 if lineno == self.curframe.f_lineno:
429 if lineno == self.curframe.f_lineno:
430 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
430 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
431 else:
431 else:
432 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
432 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
433
433
434 src.append(line)
434 src.append(line)
435 self.lineno = lineno
435 self.lineno = lineno
436
436
437 print >>io.stdout, ''.join(src)
437 print >>io.stdout, ''.join(src)
438
438
439 except KeyboardInterrupt:
439 except KeyboardInterrupt:
440 pass
440 pass
441
441
442 def do_list(self, arg):
442 def do_list(self, arg):
443 self.lastcmd = 'list'
443 self.lastcmd = 'list'
444 last = None
444 last = None
445 if arg:
445 if arg:
446 try:
446 try:
447 x = eval(arg, {}, {})
447 x = eval(arg, {}, {})
448 if type(x) == type(()):
448 if type(x) == type(()):
449 first, last = x
449 first, last = x
450 first = int(first)
450 first = int(first)
451 last = int(last)
451 last = int(last)
452 if last < first:
452 if last < first:
453 # Assume it's a count
453 # Assume it's a count
454 last = first + last
454 last = first + last
455 else:
455 else:
456 first = max(1, int(x) - 5)
456 first = max(1, int(x) - 5)
457 except:
457 except:
458 print '*** Error in argument:', `arg`
458 print '*** Error in argument:', `arg`
459 return
459 return
460 elif self.lineno is None:
460 elif self.lineno is None:
461 first = max(1, self.curframe.f_lineno - 5)
461 first = max(1, self.curframe.f_lineno - 5)
462 else:
462 else:
463 first = self.lineno + 1
463 first = self.lineno + 1
464 if last is None:
464 if last is None:
465 last = first + 10
465 last = first + 10
466 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
466 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
467
467
468 # vds: >>
468 # vds: >>
469 lineno = first
469 lineno = first
470 filename = self.curframe.f_code.co_filename
470 filename = self.curframe.f_code.co_filename
471 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
471 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
472 # vds: <<
472 # vds: <<
473
473
474 do_l = do_list
474 do_l = do_list
475
475
476 def do_pdef(self, arg):
476 def do_pdef(self, arg):
477 """The debugger interface to magic_pdef"""
477 """The debugger interface to magic_pdef"""
478 namespaces = [('Locals', self.curframe.f_locals),
478 namespaces = [('Locals', self.curframe.f_locals),
479 ('Globals', self.curframe.f_globals)]
479 ('Globals', self.curframe.f_globals)]
480 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
480 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
481
481
482 def do_pdoc(self, arg):
482 def do_pdoc(self, arg):
483 """The debugger interface to magic_pdoc"""
483 """The debugger interface to magic_pdoc"""
484 namespaces = [('Locals', self.curframe.f_locals),
484 namespaces = [('Locals', self.curframe.f_locals),
485 ('Globals', self.curframe.f_globals)]
485 ('Globals', self.curframe.f_globals)]
486 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
486 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
487
487
488 def do_pinfo(self, arg):
488 def do_pinfo(self, arg):
489 """The debugger equivalant of ?obj"""
489 """The debugger equivalant of ?obj"""
490 namespaces = [('Locals', self.curframe.f_locals),
490 namespaces = [('Locals', self.curframe.f_locals),
491 ('Globals', self.curframe.f_globals)]
491 ('Globals', self.curframe.f_globals)]
492 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
492 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
493 namespaces=namespaces)
493 namespaces=namespaces)
494
494
495 def checkline(self, filename, lineno):
495 def checkline(self, filename, lineno):
496 """Check whether specified line seems to be executable.
496 """Check whether specified line seems to be executable.
497
497
498 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
498 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
499 line or EOF). Warning: testing is not comprehensive.
499 line or EOF). Warning: testing is not comprehensive.
500 """
500 """
501 #######################################################################
501 #######################################################################
502 # XXX Hack! Use python-2.5 compatible code for this call, because with
502 # XXX Hack! Use python-2.5 compatible code for this call, because with
503 # all of our changes, we've drifted from the pdb api in 2.6. For now,
503 # all of our changes, we've drifted from the pdb api in 2.6. For now,
504 # changing:
504 # changing:
505 #
505 #
506 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
506 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
507 # to:
507 # to:
508 #
508 #
509 line = linecache.getline(filename, lineno)
509 line = linecache.getline(filename, lineno)
510 #
510 #
511 # does the trick. But in reality, we need to fix this by reconciling
511 # does the trick. But in reality, we need to fix this by reconciling
512 # our updates with the new Pdb APIs in Python 2.6.
512 # our updates with the new Pdb APIs in Python 2.6.
513 #
513 #
514 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
514 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
515 #######################################################################
515 #######################################################################
516
516
517 if not line:
517 if not line:
518 print >>self.stdout, 'End of file'
518 print >>self.stdout, 'End of file'
519 return 0
519 return 0
520 line = line.strip()
520 line = line.strip()
521 # Don't allow setting breakpoint at a blank line
521 # Don't allow setting breakpoint at a blank line
522 if (not line or (line[0] == '#') or
522 if (not line or (line[0] == '#') or
523 (line[:3] == '"""') or line[:3] == "'''"):
523 (line[:3] == '"""') or line[:3] == "'''"):
524 print >>self.stdout, '*** Blank or comment'
524 print >>self.stdout, '*** Blank or comment'
525 return 0
525 return 0
526 return lineno
526 return lineno
@@ -1,123 +1,142 b''
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012, The IPython Development Team.
4 # Copyright (c) 2012, The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import sys
15 import sys
16
16
17 # third-party
17 # third-party
18 import nose.tools as nt
18 import nose.tools as nt
19
19
20 # Our own
20 # Our own
21 from IPython.core import debugger
21 from IPython.core import debugger
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Helper classes, from CPython's Pdb test suite
24 # Helper classes, from CPython's Pdb test suite
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 class _FakeInput(object):
27 class _FakeInput(object):
28 """
28 """
29 A fake input stream for pdb's interactive debugger. Whenever a
29 A fake input stream for pdb's interactive debugger. Whenever a
30 line is read, print it (to simulate the user typing it), and then
30 line is read, print it (to simulate the user typing it), and then
31 return it. The set of lines to return is specified in the
31 return it. The set of lines to return is specified in the
32 constructor; they should not have trailing newlines.
32 constructor; they should not have trailing newlines.
33 """
33 """
34 def __init__(self, lines):
34 def __init__(self, lines):
35 self.lines = iter(lines)
35 self.lines = iter(lines)
36
36
37 def readline(self):
37 def readline(self):
38 line = next(self.lines)
38 line = next(self.lines)
39 print line
39 print line
40 return line+'\n'
40 return line+'\n'
41
41
42 class PdbTestInput(object):
42 class PdbTestInput(object):
43 """Context manager that makes testing Pdb in doctests easier."""
43 """Context manager that makes testing Pdb in doctests easier."""
44
44
45 def __init__(self, input):
45 def __init__(self, input):
46 self.input = input
46 self.input = input
47
47
48 def __enter__(self):
48 def __enter__(self):
49 self.real_stdin = sys.stdin
49 self.real_stdin = sys.stdin
50 sys.stdin = _FakeInput(self.input)
50 sys.stdin = _FakeInput(self.input)
51
51
52 def __exit__(self, *exc):
52 def __exit__(self, *exc):
53 sys.stdin = self.real_stdin
53 sys.stdin = self.real_stdin
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Tests
56 # Tests
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 def test_longer_repr():
59 def test_longer_repr():
60 from repr import repr as trepr
60 from repr import repr as trepr
61
61
62 a = '1234567890'* 7
62 a = '1234567890'* 7
63 ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'"
63 ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'"
64 a_trunc = "'123456789012...8901234567890'"
64 a_trunc = "'123456789012...8901234567890'"
65 nt.assert_equals(trepr(a), a_trunc)
65 nt.assert_equals(trepr(a), a_trunc)
66 # The creation of our tracer modifies the repr module's repr function
66 # The creation of our tracer modifies the repr module's repr function
67 # in-place, since that global is used directly by the stdlib's pdb module.
67 # in-place, since that global is used directly by the stdlib's pdb module.
68 t = debugger.Tracer()
68 t = debugger.Tracer()
69 nt.assert_equals(trepr(a), ar)
69 nt.assert_equals(trepr(a), ar)
70
70
71 def test_ipdb_magics():
71 def test_ipdb_magics():
72 '''Test calling some IPython magics from ipdb.
72 '''Test calling some IPython magics from ipdb.
73
73
74 First, set up some test functions and classes which we can inspect.
74 First, set up some test functions and classes which we can inspect.
75
75
76 >>> class ExampleClass(object):
76 >>> class ExampleClass(object):
77 ... """Docstring for ExampleClass."""
77 ... """Docstring for ExampleClass."""
78 ... def __init__(self):
78 ... def __init__(self):
79 ... """Docstring for ExampleClass.__init__"""
79 ... """Docstring for ExampleClass.__init__"""
80 ... pass
80 ... pass
81 ... def __str__(self):
81 ... def __str__(self):
82 ... return "ExampleClass()"
82 ... return "ExampleClass()"
83
83
84 >>> def example_function(x, y, z="hello"):
84 >>> def example_function(x, y, z="hello"):
85 ... """Docstring for example_function."""
85 ... """Docstring for example_function."""
86 ... pass
86 ... pass
87
87
88 Create a function which triggers ipdb.
88 Create a function which triggers ipdb.
89
89
90 >>> def trigger_ipdb():
90 >>> def trigger_ipdb():
91 ... a = ExampleClass()
91 ... a = ExampleClass()
92 ... debugger.Pdb().set_trace()
92 ... debugger.Pdb().set_trace()
93
93
94 >>> with PdbTestInput([
94 >>> with PdbTestInput([
95 ... 'pdef example_function',
95 ... 'pdef example_function',
96 ... 'pdoc ExampleClass',
96 ... 'pdoc ExampleClass',
97 ... 'pinfo a',
97 ... 'pinfo a',
98 ... 'continue',
98 ... 'continue',
99 ... ]):
99 ... ]):
100 ... trigger_ipdb()
100 ... trigger_ipdb()
101 --Return--
101 --Return--
102 None
102 None
103 > <doctest ...>(3)trigger_ipdb()
103 > <doctest ...>(3)trigger_ipdb()
104 1 def trigger_ipdb():
104 1 def trigger_ipdb():
105 2 a = ExampleClass()
105 2 a = ExampleClass()
106 ----> 3 debugger.Pdb().set_trace()
106 ----> 3 debugger.Pdb().set_trace()
107 <BLANKLINE>
107 <BLANKLINE>
108 ipdb> pdef example_function
108 ipdb> pdef example_function
109 example_function(x, y, z='hello')
109 example_function(x, y, z='hello')
110 ipdb> pdoc ExampleClass
110 ipdb> pdoc ExampleClass
111 Class Docstring:
111 Class Docstring:
112 Docstring for ExampleClass.
112 Docstring for ExampleClass.
113 Constructor Docstring:
113 Constructor Docstring:
114 Docstring for ExampleClass.__init__
114 Docstring for ExampleClass.__init__
115 ipdb> pinfo a
115 ipdb> pinfo a
116 Type: ExampleClass
116 Type: ExampleClass
117 String Form:ExampleClass()
117 String Form:ExampleClass()
118 Namespace: Locals
118 Namespace: Locals
119 File: ...
119 File: ...
120 Docstring: Docstring for ExampleClass.
120 Docstring: Docstring for ExampleClass.
121 Constructor Docstring:Docstring for ExampleClass.__init__
121 Constructor Docstring:Docstring for ExampleClass.__init__
122 ipdb> continue
122 ipdb> continue
123 '''
123 '''
124
125 def test_ipdb_magics():
126 '''Test ipdb with a very short function.
127
128 >>> def bar():
129 ... pass
130
131 Run ipdb.
132
133 >>> with PdbTestInput([
134 ... 'continue',
135 ... ]):
136 ... debugger.Pdb().runcall(bar)
137 > <doctest ...>(2)bar()
138 1 def bar():
139 ----> 2 pass
140 <BLANKLINE>
141 ipdb> continue
142 '''
General Comments 0
You need to be logged in to leave comments. Login now