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