##// END OF EJS Templates
Refactor simplify and share common function.
Matthias Bussonnier -
Show More
@@ -1,632 +1,633 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, 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 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
64 def make_arrow(pad):
65 """generate the leading arrow in front of traceback or debugger"""
66 if pad >= 2:
67 return '-'*(pad-2) + '> '
68 elif pad == 1:
69 return '>'
70 return ''
71
72
63 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
73 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
64 """Exception hook which handles `BdbQuit` exceptions.
74 """Exception hook which handles `BdbQuit` exceptions.
65
75
66 All other exceptions are processed using the `excepthook`
76 All other exceptions are processed using the `excepthook`
67 parameter.
77 parameter.
68 """
78 """
69 if et==bdb.BdbQuit:
79 if et==bdb.BdbQuit:
70 print('Exiting Debugger.')
80 print('Exiting Debugger.')
71 elif excepthook is not None:
81 elif excepthook is not None:
72 excepthook(et, ev, tb)
82 excepthook(et, ev, tb)
73 else:
83 else:
74 # Backwards compatibility. Raise deprecation warning?
84 # Backwards compatibility. Raise deprecation warning?
75 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
85 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
76
86
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
87 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 print('Exiting Debugger.')
88 print('Exiting Debugger.')
79
89
80
90
81 class Tracer(object):
91 class Tracer(object):
82 """Class for local debugging, similar to pdb.set_trace.
92 """Class for local debugging, similar to pdb.set_trace.
83
93
84 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
85 providing IPython's enhanced capabilities.
95 providing IPython's enhanced capabilities.
86
96
87 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
88 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
89 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
90 constructor, ensuring that this code plays nicely with a running IPython,
100 constructor, ensuring that this code plays nicely with a running IPython,
91 while functioning acceptably (though with limitations) if outside of it.
101 while functioning acceptably (though with limitations) if outside of it.
92 """
102 """
93
103
94 @skip_doctest
104 @skip_doctest
95 def __init__(self, colors=None):
105 def __init__(self, colors=None):
96 """Create a local debugger instance.
106 """Create a local debugger instance.
97
107
98 Parameters
108 Parameters
99 ----------
109 ----------
100
110
101 colors : str, optional
111 colors : str, optional
102 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
103 valid color schemes. If not given, the function will default to
113 valid color schemes. If not given, the function will default to
104 the current IPython scheme when running inside IPython, and to
114 the current IPython scheme when running inside IPython, and to
105 'NoColor' otherwise.
115 'NoColor' otherwise.
106
116
107 Examples
117 Examples
108 --------
118 --------
109 ::
119 ::
110
120
111 from IPython.core.debugger import Tracer; debug_here = Tracer()
121 from IPython.core.debugger import Tracer; debug_here = Tracer()
112
122
113 Later in your code::
123 Later in your code::
114
124
115 debug_here() # -> will open up the debugger at that point.
125 debug_here() # -> will open up the debugger at that point.
116
126
117 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
118 step through code, set breakpoints, etc. See the pdb documentation
128 step through code, set breakpoints, etc. See the pdb documentation
119 from the Python standard library for usage details.
129 from the Python standard library for usage details.
120 """
130 """
121
131
122 ip = get_ipython()
132 ip = get_ipython()
123 if ip is None:
133 if ip is None:
124 # Outside of ipython, we set our own exception hook manually
134 # Outside of ipython, we set our own exception hook manually
125 sys.excepthook = functools.partial(BdbQuit_excepthook,
135 sys.excepthook = functools.partial(BdbQuit_excepthook,
126 excepthook=sys.excepthook)
136 excepthook=sys.excepthook)
127 def_colors = 'NoColor'
137 def_colors = 'NoColor'
128 try:
138 try:
129 # Limited tab completion support
139 # Limited tab completion support
130 import readline
140 import readline
131 readline.parse_and_bind('tab: complete')
141 readline.parse_and_bind('tab: complete')
132 except ImportError:
142 except ImportError:
133 pass
143 pass
134 else:
144 else:
135 # In ipython, we use its custom exception handler mechanism
145 # In ipython, we use its custom exception handler mechanism
136 def_colors = ip.colors
146 def_colors = ip.colors
137 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
147 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
138
148
139 if colors is None:
149 if colors is None:
140 colors = def_colors
150 colors = def_colors
141
151
142 # The stdlib debugger internally uses a modified repr from the `repr`
152 # The stdlib debugger internally uses a modified repr from the `repr`
143 # module, that limits the length of printed strings to a hardcoded
153 # module, that limits the length of printed strings to a hardcoded
144 # 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
145 # 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
146 # most interactive uses.
156 # most interactive uses.
147 try:
157 try:
148 try:
158 try:
149 from reprlib import aRepr # Py 3
159 from reprlib import aRepr # Py 3
150 except ImportError:
160 except ImportError:
151 from repr import aRepr # Py 2
161 from repr import aRepr # Py 2
152 aRepr.maxstring = 80
162 aRepr.maxstring = 80
153 except:
163 except:
154 # 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
155 # here can be warned about but can be otherwise ignored. These
165 # here can be warned about but can be otherwise ignored. These
156 # printouts will tell us about problems if this API changes
166 # printouts will tell us about problems if this API changes
157 import traceback
167 import traceback
158 traceback.print_exc()
168 traceback.print_exc()
159
169
160 self.debugger = Pdb(colors)
170 self.debugger = Pdb(colors)
161
171
162 def __call__(self):
172 def __call__(self):
163 """Starts an interactive debugger at the point where called.
173 """Starts an interactive debugger at the point where called.
164
174
165 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
166 using IPython's enhanced debugger."""
176 using IPython's enhanced debugger."""
167
177
168 self.debugger.set_trace(sys._getframe().f_back)
178 self.debugger.set_trace(sys._getframe().f_back)
169
179
170
180
171 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
181 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
172 """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
173 for the ``do_...`` commands that hook into the help system.
183 for the ``do_...`` commands that hook into the help system.
174 Adapted from from a comp.lang.python posting
184 Adapted from from a comp.lang.python posting
175 by Duncan Booth."""
185 by Duncan Booth."""
176 def wrapper(*args, **kw):
186 def wrapper(*args, **kw):
177 return new_fn(*args, **kw)
187 return new_fn(*args, **kw)
178 if old_fn.__doc__:
188 if old_fn.__doc__:
179 wrapper.__doc__ = old_fn.__doc__ + additional_text
189 wrapper.__doc__ = old_fn.__doc__ + additional_text
180 return wrapper
190 return wrapper
181
191
182
192
183 def _file_lines(fname):
193 def _file_lines(fname):
184 """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.
185
195
186 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
187 read, it simply returns an empty list."""
197 read, it simply returns an empty list."""
188
198
189 try:
199 try:
190 outfile = open(fname)
200 outfile = open(fname)
191 except IOError:
201 except IOError:
192 return []
202 return []
193 else:
203 else:
194 out = outfile.readlines()
204 out = outfile.readlines()
195 outfile.close()
205 outfile.close()
196 return out
206 return out
197
207
198
208
199 class Pdb(OldPdb):
209 class Pdb(OldPdb):
200 """Modified Pdb class, does not load readline."""
210 """Modified Pdb class, does not load readline."""
201
211
202 def __init__(self,color_scheme='NoColor',completekey=None,
212 def __init__(self,color_scheme='NoColor',completekey=None,
203 stdin=None, stdout=None, context=5):
213 stdin=None, stdout=None, context=5):
204
214
205 # Parent constructor:
215 # Parent constructor:
206 try:
216 try:
207 self.context=int(context)
217 self.context=int(context)
208 if self.context <= 0:
218 if self.context <= 0:
209 raise ValueError("Context must be a positive integer")
219 raise ValueError("Context must be a positive integer")
210 except (TypeError, ValueError):
220 except (TypeError, ValueError):
211 raise ValueError("Context must be a positive integer")
221 raise ValueError("Context must be a positive integer")
212
222
213 if has_pydb and completekey is None:
223 if has_pydb and completekey is None:
214 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
224 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
215 else:
225 else:
216 OldPdb.__init__(self,completekey,stdin,stdout)
226 OldPdb.__init__(self,completekey,stdin,stdout)
217
227
218 # IPython changes...
228 # IPython changes...
219 self.is_pydb = has_pydb
229 self.is_pydb = has_pydb
220
230
221 self.shell = get_ipython()
231 self.shell = get_ipython()
222
232
223 if self.shell is None:
233 if self.shell is None:
224 # No IPython instance running, we must create one
234 # No IPython instance running, we must create one
225 from IPython.terminal.interactiveshell import \
235 from IPython.terminal.interactiveshell import \
226 TerminalInteractiveShell
236 TerminalInteractiveShell
227 self.shell = TerminalInteractiveShell.instance()
237 self.shell = TerminalInteractiveShell.instance()
228
238
229 if self.is_pydb:
239 if self.is_pydb:
230
240
231 # interactiveshell.py's ipalias seems to want pdb's checkline
241 # interactiveshell.py's ipalias seems to want pdb's checkline
232 # which located in pydb.fn
242 # which located in pydb.fn
233 import pydb.fns
243 import pydb.fns
234 self.checkline = lambda filename, lineno: \
244 self.checkline = lambda filename, lineno: \
235 pydb.fns.checkline(self, filename, lineno)
245 pydb.fns.checkline(self, filename, lineno)
236
246
237 self.curframe = None
247 self.curframe = None
238 self.do_restart = self.new_do_restart
248 self.do_restart = self.new_do_restart
239
249
240 self.old_all_completions = self.shell.Completer.all_completions
250 self.old_all_completions = self.shell.Completer.all_completions
241 self.shell.Completer.all_completions=self.all_completions
251 self.shell.Completer.all_completions=self.all_completions
242
252
243 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
253 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
244 OldPdb.do_list)
254 OldPdb.do_list)
245 self.do_l = self.do_list
255 self.do_l = self.do_list
246 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
256 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
247 OldPdb.do_frame)
257 OldPdb.do_frame)
248
258
249 self.aliases = {}
259 self.aliases = {}
250
260
251 # Create color table: we copy the default one from the traceback
261 # Create color table: we copy the default one from the traceback
252 # module and add a few attributes needed for debugging
262 # module and add a few attributes needed for debugging
253 self.color_scheme_table = exception_colors()
263 self.color_scheme_table = exception_colors()
254
264
255 # shorthands
265 # shorthands
256 C = coloransi.TermColors
266 C = coloransi.TermColors
257 cst = self.color_scheme_table
267 cst = self.color_scheme_table
258
268
259 cst['NoColor'].colors.prompt = C.NoColor
269 cst['NoColor'].colors.prompt = C.NoColor
260 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
270 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
261 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
271 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
262
272
263 cst['Linux'].colors.prompt = C.Green
273 cst['Linux'].colors.prompt = C.Green
264 cst['Linux'].colors.breakpoint_enabled = C.LightRed
274 cst['Linux'].colors.breakpoint_enabled = C.LightRed
265 cst['Linux'].colors.breakpoint_disabled = C.Red
275 cst['Linux'].colors.breakpoint_disabled = C.Red
266
276
267 cst['LightBG'].colors.prompt = C.Blue
277 cst['LightBG'].colors.prompt = C.Blue
268 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
278 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
269 cst['LightBG'].colors.breakpoint_disabled = C.Red
279 cst['LightBG'].colors.breakpoint_disabled = C.Red
270
280
271 self.set_colors(color_scheme)
281 self.set_colors(color_scheme)
272
282
273 # Add a python parser so we can syntax highlight source while
283 # Add a python parser so we can syntax highlight source while
274 # debugging.
284 # debugging.
275 self.parser = PyColorize.Parser()
285 self.parser = PyColorize.Parser()
276
286
277 # Set the prompt
287 # Set the prompt
278 Colors = cst.active_colors
288 Colors = cst.active_colors
279 self.prompt = u'%s%s%s' % (Colors.prompt, prompt, Colors.Normal) # The default prompt is '(Pdb)'
289 self.prompt = u'%s%s%s' % (Colors.prompt, prompt, Colors.Normal) # The default prompt is '(Pdb)'
280
290
281 def set_colors(self, scheme):
291 def set_colors(self, scheme):
282 """Shorthand access to the color table scheme selector method."""
292 """Shorthand access to the color table scheme selector method."""
283 self.color_scheme_table.set_active_scheme(scheme)
293 self.color_scheme_table.set_active_scheme(scheme)
284
294
285 def interaction(self, frame, traceback):
295 def interaction(self, frame, traceback):
286 self.shell.set_completer_frame(frame)
296 self.shell.set_completer_frame(frame)
287 while True:
297 while True:
288 try:
298 try:
289 OldPdb.interaction(self, frame, traceback)
299 OldPdb.interaction(self, frame, traceback)
290 break
300 break
291 except KeyboardInterrupt:
301 except KeyboardInterrupt:
292 self.shell.write('\n' + self.shell.get_exception_only())
302 self.shell.write('\n' + self.shell.get_exception_only())
293 break
303 break
294 finally:
304 finally:
295 # Pdb sets readline delimiters, so set them back to our own
305 # Pdb sets readline delimiters, so set them back to our own
296 if self.shell.readline is not None:
306 if self.shell.readline is not None:
297 self.shell.readline.set_completer_delims(self.shell.readline_delims)
307 self.shell.readline.set_completer_delims(self.shell.readline_delims)
298
308
299 def new_do_up(self, arg):
309 def new_do_up(self, arg):
300 OldPdb.do_up(self, arg)
310 OldPdb.do_up(self, arg)
301 self.shell.set_completer_frame(self.curframe)
311 self.shell.set_completer_frame(self.curframe)
302 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
312 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
303
313
304 def new_do_down(self, arg):
314 def new_do_down(self, arg):
305 OldPdb.do_down(self, arg)
315 OldPdb.do_down(self, arg)
306 self.shell.set_completer_frame(self.curframe)
316 self.shell.set_completer_frame(self.curframe)
307
317
308 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
318 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
309
319
310 def new_do_frame(self, arg):
320 def new_do_frame(self, arg):
311 OldPdb.do_frame(self, arg)
321 OldPdb.do_frame(self, arg)
312 self.shell.set_completer_frame(self.curframe)
322 self.shell.set_completer_frame(self.curframe)
313
323
314 def new_do_quit(self, arg):
324 def new_do_quit(self, arg):
315
325
316 if hasattr(self, 'old_all_completions'):
326 if hasattr(self, 'old_all_completions'):
317 self.shell.Completer.all_completions=self.old_all_completions
327 self.shell.Completer.all_completions=self.old_all_completions
318
328
319 return OldPdb.do_quit(self, arg)
329 return OldPdb.do_quit(self, arg)
320
330
321 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
331 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
322
332
323 def new_do_restart(self, arg):
333 def new_do_restart(self, arg):
324 """Restart command. In the context of ipython this is exactly the same
334 """Restart command. In the context of ipython this is exactly the same
325 thing as 'quit'."""
335 thing as 'quit'."""
326 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
336 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
327 return self.do_quit(arg)
337 return self.do_quit(arg)
328
338
329 def postloop(self):
339 def postloop(self):
330 self.shell.set_completer_frame(None)
340 self.shell.set_completer_frame(None)
331
341
332 def print_stack_trace(self, context=None):
342 def print_stack_trace(self, context=None):
333 if context is None:
343 if context is None:
334 context = self.context
344 context = self.context
335 try:
345 try:
336 context=int(context)
346 context=int(context)
337 if context <= 0:
347 if context <= 0:
338 raise ValueError("Context must be a positive integer")
348 raise ValueError("Context must be a positive integer")
339 except (TypeError, ValueError):
349 except (TypeError, ValueError):
340 raise ValueError("Context must be a positive integer")
350 raise ValueError("Context must be a positive integer")
341 try:
351 try:
342 for frame_lineno in self.stack:
352 for frame_lineno in self.stack:
343 self.print_stack_entry(frame_lineno, context=context)
353 self.print_stack_entry(frame_lineno, context=context)
344 except KeyboardInterrupt:
354 except KeyboardInterrupt:
345 pass
355 pass
346
356
347 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
357 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
348 context=None):
358 context=None):
349 if context is None:
359 if context is None:
350 context = self.context
360 context = self.context
351 try:
361 try:
352 context=int(context)
362 context=int(context)
353 if context <= 0:
363 if context <= 0:
354 raise ValueError("Context must be a positive integer")
364 raise ValueError("Context must be a positive integer")
355 except (TypeError, ValueError):
365 except (TypeError, ValueError):
356 raise ValueError("Context must be a positive integer")
366 raise ValueError("Context must be a positive integer")
357 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
367 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
358
368
359 # vds: >>
369 # vds: >>
360 frame, lineno = frame_lineno
370 frame, lineno = frame_lineno
361 filename = frame.f_code.co_filename
371 filename = frame.f_code.co_filename
362 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
372 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
363 # vds: <<
373 # vds: <<
364
374
365 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
375 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
366 if context is None:
376 if context is None:
367 context = self.context
377 context = self.context
368 try:
378 try:
369 context=int(context)
379 context=int(context)
370 if context <= 0:
380 if context <= 0:
371 print("Context must be a positive integer")
381 print("Context must be a positive integer")
372 except (TypeError, ValueError):
382 except (TypeError, ValueError):
373 print("Context must be a positive integer")
383 print("Context must be a positive integer")
374 try:
384 try:
375 import reprlib # Py 3
385 import reprlib # Py 3
376 except ImportError:
386 except ImportError:
377 import repr as reprlib # Py 2
387 import repr as reprlib # Py 2
378
388
379 ret = []
389 ret = []
380
390
381 Colors = self.color_scheme_table.active_colors
391 Colors = self.color_scheme_table.active_colors
382 ColorsNormal = Colors.Normal
392 ColorsNormal = Colors.Normal
383 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
393 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
384 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
394 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
385 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
395 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
386 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
396 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
387 ColorsNormal)
397 ColorsNormal)
388
398
389 frame, lineno = frame_lineno
399 frame, lineno = frame_lineno
390
400
391 return_value = ''
401 return_value = ''
392 if '__return__' in frame.f_locals:
402 if '__return__' in frame.f_locals:
393 rv = frame.f_locals['__return__']
403 rv = frame.f_locals['__return__']
394 #return_value += '->'
404 #return_value += '->'
395 return_value += reprlib.repr(rv) + '\n'
405 return_value += reprlib.repr(rv) + '\n'
396 ret.append(return_value)
406 ret.append(return_value)
397
407
398 #s = filename + '(' + `lineno` + ')'
408 #s = filename + '(' + `lineno` + ')'
399 filename = self.canonic(frame.f_code.co_filename)
409 filename = self.canonic(frame.f_code.co_filename)
400 link = tpl_link % py3compat.cast_unicode(filename)
410 link = tpl_link % py3compat.cast_unicode(filename)
401
411
402 if frame.f_code.co_name:
412 if frame.f_code.co_name:
403 func = frame.f_code.co_name
413 func = frame.f_code.co_name
404 else:
414 else:
405 func = "<lambda>"
415 func = "<lambda>"
406
416
407 call = ''
417 call = ''
408 if func != '?':
418 if func != '?':
409 if '__args__' in frame.f_locals:
419 if '__args__' in frame.f_locals:
410 args = reprlib.repr(frame.f_locals['__args__'])
420 args = reprlib.repr(frame.f_locals['__args__'])
411 else:
421 else:
412 args = '()'
422 args = '()'
413 call = tpl_call % (func, args)
423 call = tpl_call % (func, args)
414
424
415 # The level info should be generated in the same format pdb uses, to
425 # The level info should be generated in the same format pdb uses, to
416 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
426 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
417 if frame is self.curframe:
427 if frame is self.curframe:
418 ret.append('> ')
428 ret.append('> ')
419 else:
429 else:
420 ret.append(' ')
430 ret.append(' ')
421 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
431 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
422
432
423 start = lineno - 1 - context//2
433 start = lineno - 1 - context//2
424 lines = ulinecache.getlines(filename)
434 lines = ulinecache.getlines(filename)
425 start = min(start, len(lines) - context)
435 start = min(start, len(lines) - context)
426 start = max(start, 0)
436 start = max(start, 0)
427 lines = lines[start : start + context]
437 lines = lines[start : start + context]
428
438
429 for i,line in enumerate(lines):
439 for i,line in enumerate(lines):
430 show_arrow = (start + 1 + i == lineno)
440 show_arrow = (start + 1 + i == lineno)
431 linetpl = (frame is self.curframe or show_arrow) \
441 linetpl = (frame is self.curframe or show_arrow) \
432 and tpl_line_em \
442 and tpl_line_em \
433 or tpl_line
443 or tpl_line
434 ret.append(self.__format_line(linetpl, filename,
444 ret.append(self.__format_line(linetpl, filename,
435 start + 1 + i, line,
445 start + 1 + i, line,
436 arrow = show_arrow) )
446 arrow = show_arrow) )
437 return ''.join(ret)
447 return ''.join(ret)
438
448
439 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
449 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
440 bp_mark = ""
450 bp_mark = ""
441 bp_mark_color = ""
451 bp_mark_color = ""
442
452
443 scheme = self.color_scheme_table.active_scheme_name
453 scheme = self.color_scheme_table.active_scheme_name
444 new_line, err = self.parser.format2(line, 'str', scheme)
454 new_line, err = self.parser.format2(line, 'str', scheme)
445 if not err: line = new_line
455 if not err: line = new_line
446
456
447 bp = None
457 bp = None
448 if lineno in self.get_file_breaks(filename):
458 if lineno in self.get_file_breaks(filename):
449 bps = self.get_breaks(filename, lineno)
459 bps = self.get_breaks(filename, lineno)
450 bp = bps[-1]
460 bp = bps[-1]
451
461
452 if bp:
462 if bp:
453 Colors = self.color_scheme_table.active_colors
463 Colors = self.color_scheme_table.active_colors
454 bp_mark = str(bp.number)
464 bp_mark = str(bp.number)
455 bp_mark_color = Colors.breakpoint_enabled
465 bp_mark_color = Colors.breakpoint_enabled
456 if not bp.enabled:
466 if not bp.enabled:
457 bp_mark_color = Colors.breakpoint_disabled
467 bp_mark_color = Colors.breakpoint_disabled
458
468
459 numbers_width = 7
469 numbers_width = 7
460 if arrow:
470 if arrow:
461 # This is the line with the error
471 # This is the line with the error
462 pad = numbers_width - len(str(lineno)) - len(bp_mark)
472 pad = numbers_width - len(str(lineno)) - len(bp_mark)
463 if pad >= 3:
473 num = '%s%s' % (make_arrow(pad), str(lineno))
464 marker = '-'*(pad-3) + '-> '
465 elif pad == 2:
466 marker = '> '
467 elif pad == 1:
468 marker = '>'
469 else:
470 marker = ''
471 num = '%s%s' % (marker, str(lineno))
472 line = tpl_line % (bp_mark_color + bp_mark, num, line)
473 else:
474 else:
474 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
475 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
475 line = tpl_line % (bp_mark_color + bp_mark, num, line)
476
476
477 return line
477 return tpl_line % (bp_mark_color + bp_mark, num, line)
478
478
479
479 def list_command_pydb(self, arg):
480 def list_command_pydb(self, arg):
480 """List command to use if we have a newer pydb installed"""
481 """List command to use if we have a newer pydb installed"""
481 filename, first, last = OldPdb.parse_list_cmd(self, arg)
482 filename, first, last = OldPdb.parse_list_cmd(self, arg)
482 if filename is not None:
483 if filename is not None:
483 self.print_list_lines(filename, first, last)
484 self.print_list_lines(filename, first, last)
484
485
485 def print_list_lines(self, filename, first, last):
486 def print_list_lines(self, filename, first, last):
486 """The printing (as opposed to the parsing part of a 'list'
487 """The printing (as opposed to the parsing part of a 'list'
487 command."""
488 command."""
488 try:
489 try:
489 Colors = self.color_scheme_table.active_colors
490 Colors = self.color_scheme_table.active_colors
490 ColorsNormal = Colors.Normal
491 ColorsNormal = Colors.Normal
491 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
492 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
492 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
493 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
493 src = []
494 src = []
494 if filename == "<string>" and hasattr(self, "_exec_filename"):
495 if filename == "<string>" and hasattr(self, "_exec_filename"):
495 filename = self._exec_filename
496 filename = self._exec_filename
496
497
497 for lineno in range(first, last+1):
498 for lineno in range(first, last+1):
498 line = ulinecache.getline(filename, lineno)
499 line = ulinecache.getline(filename, lineno)
499 if not line:
500 if not line:
500 break
501 break
501
502
502 if lineno == self.curframe.f_lineno:
503 if lineno == self.curframe.f_lineno:
503 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
504 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
504 else:
505 else:
505 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
506 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
506
507
507 src.append(line)
508 src.append(line)
508 self.lineno = lineno
509 self.lineno = lineno
509
510
510 print(''.join(src), file=io.stdout)
511 print(''.join(src), file=io.stdout)
511
512
512 except KeyboardInterrupt:
513 except KeyboardInterrupt:
513 pass
514 pass
514
515
515 def do_list(self, arg):
516 def do_list(self, arg):
516 self.lastcmd = 'list'
517 self.lastcmd = 'list'
517 last = None
518 last = None
518 if arg:
519 if arg:
519 try:
520 try:
520 x = eval(arg, {}, {})
521 x = eval(arg, {}, {})
521 if type(x) == type(()):
522 if type(x) == type(()):
522 first, last = x
523 first, last = x
523 first = int(first)
524 first = int(first)
524 last = int(last)
525 last = int(last)
525 if last < first:
526 if last < first:
526 # Assume it's a count
527 # Assume it's a count
527 last = first + last
528 last = first + last
528 else:
529 else:
529 first = max(1, int(x) - 5)
530 first = max(1, int(x) - 5)
530 except:
531 except:
531 print('*** Error in argument:', repr(arg))
532 print('*** Error in argument:', repr(arg))
532 return
533 return
533 elif self.lineno is None:
534 elif self.lineno is None:
534 first = max(1, self.curframe.f_lineno - 5)
535 first = max(1, self.curframe.f_lineno - 5)
535 else:
536 else:
536 first = self.lineno + 1
537 first = self.lineno + 1
537 if last is None:
538 if last is None:
538 last = first + 10
539 last = first + 10
539 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
540 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
540
541
541 # vds: >>
542 # vds: >>
542 lineno = first
543 lineno = first
543 filename = self.curframe.f_code.co_filename
544 filename = self.curframe.f_code.co_filename
544 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
545 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
545 # vds: <<
546 # vds: <<
546
547
547 do_l = do_list
548 do_l = do_list
548
549
549 def getsourcelines(self, obj):
550 def getsourcelines(self, obj):
550 lines, lineno = inspect.findsource(obj)
551 lines, lineno = inspect.findsource(obj)
551 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
552 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
552 # must be a module frame: do not try to cut a block out of it
553 # must be a module frame: do not try to cut a block out of it
553 return lines, 1
554 return lines, 1
554 elif inspect.ismodule(obj):
555 elif inspect.ismodule(obj):
555 return lines, 1
556 return lines, 1
556 return inspect.getblock(lines[lineno:]), lineno+1
557 return inspect.getblock(lines[lineno:]), lineno+1
557
558
558 def do_longlist(self, arg):
559 def do_longlist(self, arg):
559 self.lastcmd = 'longlist'
560 self.lastcmd = 'longlist'
560 filename = self.curframe.f_code.co_filename
561 filename = self.curframe.f_code.co_filename
561 try:
562 try:
562 lines, lineno = self.getsourcelines(self.curframe)
563 lines, lineno = self.getsourcelines(self.curframe)
563 except OSError as err:
564 except OSError as err:
564 self.error(err)
565 self.error(err)
565 return
566 return
566 last = lineno + len(lines)
567 last = lineno + len(lines)
567 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
568 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
568 do_ll = do_longlist
569 do_ll = do_longlist
569
570
570 def do_pdef(self, arg):
571 def do_pdef(self, arg):
571 """Print the call signature for any callable object.
572 """Print the call signature for any callable object.
572
573
573 The debugger interface to %pdef"""
574 The debugger interface to %pdef"""
574 namespaces = [('Locals', self.curframe.f_locals),
575 namespaces = [('Locals', self.curframe.f_locals),
575 ('Globals', self.curframe.f_globals)]
576 ('Globals', self.curframe.f_globals)]
576 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
577 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
577
578
578 def do_pdoc(self, arg):
579 def do_pdoc(self, arg):
579 """Print the docstring for an object.
580 """Print the docstring for an object.
580
581
581 The debugger interface to %pdoc."""
582 The debugger interface to %pdoc."""
582 namespaces = [('Locals', self.curframe.f_locals),
583 namespaces = [('Locals', self.curframe.f_locals),
583 ('Globals', self.curframe.f_globals)]
584 ('Globals', self.curframe.f_globals)]
584 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
585 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
585
586
586 def do_pfile(self, arg):
587 def do_pfile(self, arg):
587 """Print (or run through pager) the file where an object is defined.
588 """Print (or run through pager) the file where an object is defined.
588
589
589 The debugger interface to %pfile.
590 The debugger interface to %pfile.
590 """
591 """
591 namespaces = [('Locals', self.curframe.f_locals),
592 namespaces = [('Locals', self.curframe.f_locals),
592 ('Globals', self.curframe.f_globals)]
593 ('Globals', self.curframe.f_globals)]
593 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
594 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
594
595
595 def do_pinfo(self, arg):
596 def do_pinfo(self, arg):
596 """Provide detailed information about an object.
597 """Provide detailed information about an object.
597
598
598 The debugger interface to %pinfo, i.e., obj?."""
599 The debugger interface to %pinfo, i.e., obj?."""
599 namespaces = [('Locals', self.curframe.f_locals),
600 namespaces = [('Locals', self.curframe.f_locals),
600 ('Globals', self.curframe.f_globals)]
601 ('Globals', self.curframe.f_globals)]
601 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
602 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
602
603
603 def do_pinfo2(self, arg):
604 def do_pinfo2(self, arg):
604 """Provide extra detailed information about an object.
605 """Provide extra detailed information about an object.
605
606
606 The debugger interface to %pinfo2, i.e., obj??."""
607 The debugger interface to %pinfo2, i.e., obj??."""
607 namespaces = [('Locals', self.curframe.f_locals),
608 namespaces = [('Locals', self.curframe.f_locals),
608 ('Globals', self.curframe.f_globals)]
609 ('Globals', self.curframe.f_globals)]
609 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
610 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
610
611
611 def do_psource(self, arg):
612 def do_psource(self, arg):
612 """Print (or run through pager) the source code for an object."""
613 """Print (or run through pager) the source code for an object."""
613 namespaces = [('Locals', self.curframe.f_locals),
614 namespaces = [('Locals', self.curframe.f_locals),
614 ('Globals', self.curframe.f_globals)]
615 ('Globals', self.curframe.f_globals)]
615 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
616 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
616
617
617 if sys.version_info > (3, ):
618 if sys.version_info > (3, ):
618 def do_where(self, arg):
619 def do_where(self, arg):
619 """w(here)
620 """w(here)
620 Print a stack trace, with the most recent frame at the bottom.
621 Print a stack trace, with the most recent frame at the bottom.
621 An arrow indicates the "current frame", which determines the
622 An arrow indicates the "current frame", which determines the
622 context of most commands. 'bt' is an alias for this command.
623 context of most commands. 'bt' is an alias for this command.
623
624
624 Take a number as argument as an (optional) number of context line to
625 Take a number as argument as an (optional) number of context line to
625 print"""
626 print"""
626 if arg:
627 if arg:
627 context = int(arg)
628 context = int(arg)
628 self.print_stack_trace(context)
629 self.print_stack_trace(context)
629 else:
630 else:
630 self.print_stack_trace()
631 self.print_stack_trace()
631
632
632 do_w = do_where
633 do_w = do_where
@@ -1,1479 +1,1471 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Verbose and colourful traceback formatting.
3 Verbose and colourful traceback formatting.
4
4
5 **ColorTB**
5 **ColorTB**
6
6
7 I've always found it a bit hard to visually parse tracebacks in Python. The
7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 ColorTB class is a solution to that problem. It colors the different parts of a
8 ColorTB class is a solution to that problem. It colors the different parts of a
9 traceback in a manner similar to what you would expect from a syntax-highlighting
9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 text editor.
10 text editor.
11
11
12 Installation instructions for ColorTB::
12 Installation instructions for ColorTB::
13
13
14 import sys,ultratb
14 import sys,ultratb
15 sys.excepthook = ultratb.ColorTB()
15 sys.excepthook = ultratb.ColorTB()
16
16
17 **VerboseTB**
17 **VerboseTB**
18
18
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 and intended it for CGI programmers, but why should they have all the fun? I
21 and intended it for CGI programmers, but why should they have all the fun? I
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 but kind of neat, and maybe useful for long-running programs that you believe
23 but kind of neat, and maybe useful for long-running programs that you believe
24 are bug-free. If a crash *does* occur in that type of program you want details.
24 are bug-free. If a crash *does* occur in that type of program you want details.
25 Give it a shot--you'll love it or you'll hate it.
25 Give it a shot--you'll love it or you'll hate it.
26
26
27 .. note::
27 .. note::
28
28
29 The Verbose mode prints the variables currently visible where the exception
29 The Verbose mode prints the variables currently visible where the exception
30 happened (shortening their strings if too long). This can potentially be
30 happened (shortening their strings if too long). This can potentially be
31 very slow, if you happen to have a huge data structure whose string
31 very slow, if you happen to have a huge data structure whose string
32 representation is complex to compute. Your computer may appear to freeze for
32 representation is complex to compute. Your computer may appear to freeze for
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 with Ctrl-C (maybe hitting it more than once).
34 with Ctrl-C (maybe hitting it more than once).
35
35
36 If you encounter this kind of situation often, you may want to use the
36 If you encounter this kind of situation often, you may want to use the
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 variables (but otherwise includes the information and context given by
38 variables (but otherwise includes the information and context given by
39 Verbose).
39 Verbose).
40
40
41
41
42 Installation instructions for VerboseTB::
42 Installation instructions for VerboseTB::
43
43
44 import sys,ultratb
44 import sys,ultratb
45 sys.excepthook = ultratb.VerboseTB()
45 sys.excepthook = ultratb.VerboseTB()
46
46
47 Note: Much of the code in this module was lifted verbatim from the standard
47 Note: Much of the code in this module was lifted verbatim from the standard
48 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
48 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
49
49
50 Color schemes
50 Color schemes
51 -------------
51 -------------
52
52
53 The colors are defined in the class TBTools through the use of the
53 The colors are defined in the class TBTools through the use of the
54 ColorSchemeTable class. Currently the following exist:
54 ColorSchemeTable class. Currently the following exist:
55
55
56 - NoColor: allows all of this module to be used in any terminal (the color
56 - NoColor: allows all of this module to be used in any terminal (the color
57 escapes are just dummy blank strings).
57 escapes are just dummy blank strings).
58
58
59 - Linux: is meant to look good in a terminal like the Linux console (black
59 - Linux: is meant to look good in a terminal like the Linux console (black
60 or very dark background).
60 or very dark background).
61
61
62 - LightBG: similar to Linux but swaps dark/light colors to be more readable
62 - LightBG: similar to Linux but swaps dark/light colors to be more readable
63 in light background terminals.
63 in light background terminals.
64
64
65 You can implement other color schemes easily, the syntax is fairly
65 You can implement other color schemes easily, the syntax is fairly
66 self-explanatory. Please send back new schemes you develop to the author for
66 self-explanatory. Please send back new schemes you develop to the author for
67 possible inclusion in future releases.
67 possible inclusion in future releases.
68
68
69 Inheritance diagram:
69 Inheritance diagram:
70
70
71 .. inheritance-diagram:: IPython.core.ultratb
71 .. inheritance-diagram:: IPython.core.ultratb
72 :parts: 3
72 :parts: 3
73 """
73 """
74
74
75 #*****************************************************************************
75 #*****************************************************************************
76 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
76 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
77 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
77 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
78 #
78 #
79 # Distributed under the terms of the BSD License. The full license is in
79 # Distributed under the terms of the BSD License. The full license is in
80 # the file COPYING, distributed as part of this software.
80 # the file COPYING, distributed as part of this software.
81 #*****************************************************************************
81 #*****************************************************************************
82
82
83 from __future__ import unicode_literals
83 from __future__ import unicode_literals
84 from __future__ import print_function
84 from __future__ import print_function
85
85
86 import dis
86 import dis
87 import inspect
87 import inspect
88 import keyword
88 import keyword
89 import linecache
89 import linecache
90 import os
90 import os
91 import pydoc
91 import pydoc
92 import re
92 import re
93 import sys
93 import sys
94 import time
94 import time
95 import tokenize
95 import tokenize
96 import traceback
96 import traceback
97 import types
97 import types
98
98
99 try: # Python 2
99 try: # Python 2
100 generate_tokens = tokenize.generate_tokens
100 generate_tokens = tokenize.generate_tokens
101 except AttributeError: # Python 3
101 except AttributeError: # Python 3
102 generate_tokens = tokenize.tokenize
102 generate_tokens = tokenize.tokenize
103
103
104 # For purposes of monkeypatching inspect to fix a bug in it.
104 # For purposes of monkeypatching inspect to fix a bug in it.
105 from inspect import getsourcefile, getfile, getmodule, \
105 from inspect import getsourcefile, getfile, getmodule, \
106 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
106 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
107
107
108 # IPython's own modules
108 # IPython's own modules
109 # Modified pdb which doesn't damage IPython's readline handling
109 # Modified pdb which doesn't damage IPython's readline handling
110 from IPython import get_ipython
110 from IPython import get_ipython
111 from IPython.core import debugger
111 from IPython.core import debugger
112 from IPython.core.display_trap import DisplayTrap
112 from IPython.core.display_trap import DisplayTrap
113 from IPython.core.excolors import exception_colors
113 from IPython.core.excolors import exception_colors
114 from IPython.utils import PyColorize
114 from IPython.utils import PyColorize
115 from IPython.utils import io
115 from IPython.utils import io
116 from IPython.utils import openpy
116 from IPython.utils import openpy
117 from IPython.utils import path as util_path
117 from IPython.utils import path as util_path
118 from IPython.utils import py3compat
118 from IPython.utils import py3compat
119 from IPython.utils import ulinecache
119 from IPython.utils import ulinecache
120 from IPython.utils.data import uniq_stable
120 from IPython.utils.data import uniq_stable
121 from IPython.utils.warn import info, error
121 from IPython.utils.warn import info, error
122
122
123 # Globals
123 # Globals
124 # amount of space to put line numbers before verbose tracebacks
124 # amount of space to put line numbers before verbose tracebacks
125 INDENT_SIZE = 8
125 INDENT_SIZE = 8
126
126
127 # Default color scheme. This is used, for example, by the traceback
127 # Default color scheme. This is used, for example, by the traceback
128 # formatter. When running in an actual IPython instance, the user's rc.colors
128 # formatter. When running in an actual IPython instance, the user's rc.colors
129 # value is used, but having a module global makes this functionality available
129 # value is used, but having a module global makes this functionality available
130 # to users of ultratb who are NOT running inside ipython.
130 # to users of ultratb who are NOT running inside ipython.
131 DEFAULT_SCHEME = 'NoColor'
131 DEFAULT_SCHEME = 'NoColor'
132
132
133 # ---------------------------------------------------------------------------
133 # ---------------------------------------------------------------------------
134 # Code begins
134 # Code begins
135
135
136 # Utility functions
136 # Utility functions
137 def inspect_error():
137 def inspect_error():
138 """Print a message about internal inspect errors.
138 """Print a message about internal inspect errors.
139
139
140 These are unfortunately quite common."""
140 These are unfortunately quite common."""
141
141
142 error('Internal Python error in the inspect module.\n'
142 error('Internal Python error in the inspect module.\n'
143 'Below is the traceback from this internal error.\n')
143 'Below is the traceback from this internal error.\n')
144
144
145
145
146 # This function is a monkeypatch we apply to the Python inspect module. We have
146 # This function is a monkeypatch we apply to the Python inspect module. We have
147 # now found when it's needed (see discussion on issue gh-1456), and we have a
147 # now found when it's needed (see discussion on issue gh-1456), and we have a
148 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
148 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
149 # the monkeypatch is not applied. TK, Aug 2012.
149 # the monkeypatch is not applied. TK, Aug 2012.
150 def findsource(object):
150 def findsource(object):
151 """Return the entire source file and starting line number for an object.
151 """Return the entire source file and starting line number for an object.
152
152
153 The argument may be a module, class, method, function, traceback, frame,
153 The argument may be a module, class, method, function, traceback, frame,
154 or code object. The source code is returned as a list of all the lines
154 or code object. The source code is returned as a list of all the lines
155 in the file and the line number indexes a line in that list. An IOError
155 in the file and the line number indexes a line in that list. An IOError
156 is raised if the source code cannot be retrieved.
156 is raised if the source code cannot be retrieved.
157
157
158 FIXED version with which we monkeypatch the stdlib to work around a bug."""
158 FIXED version with which we monkeypatch the stdlib to work around a bug."""
159
159
160 file = getsourcefile(object) or getfile(object)
160 file = getsourcefile(object) or getfile(object)
161 # If the object is a frame, then trying to get the globals dict from its
161 # If the object is a frame, then trying to get the globals dict from its
162 # module won't work. Instead, the frame object itself has the globals
162 # module won't work. Instead, the frame object itself has the globals
163 # dictionary.
163 # dictionary.
164 globals_dict = None
164 globals_dict = None
165 if inspect.isframe(object):
165 if inspect.isframe(object):
166 # XXX: can this ever be false?
166 # XXX: can this ever be false?
167 globals_dict = object.f_globals
167 globals_dict = object.f_globals
168 else:
168 else:
169 module = getmodule(object, file)
169 module = getmodule(object, file)
170 if module:
170 if module:
171 globals_dict = module.__dict__
171 globals_dict = module.__dict__
172 lines = linecache.getlines(file, globals_dict)
172 lines = linecache.getlines(file, globals_dict)
173 if not lines:
173 if not lines:
174 raise IOError('could not get source code')
174 raise IOError('could not get source code')
175
175
176 if ismodule(object):
176 if ismodule(object):
177 return lines, 0
177 return lines, 0
178
178
179 if isclass(object):
179 if isclass(object):
180 name = object.__name__
180 name = object.__name__
181 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
181 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
182 # make some effort to find the best matching class definition:
182 # make some effort to find the best matching class definition:
183 # use the one with the least indentation, which is the one
183 # use the one with the least indentation, which is the one
184 # that's most probably not inside a function definition.
184 # that's most probably not inside a function definition.
185 candidates = []
185 candidates = []
186 for i in range(len(lines)):
186 for i in range(len(lines)):
187 match = pat.match(lines[i])
187 match = pat.match(lines[i])
188 if match:
188 if match:
189 # if it's at toplevel, it's already the best one
189 # if it's at toplevel, it's already the best one
190 if lines[i][0] == 'c':
190 if lines[i][0] == 'c':
191 return lines, i
191 return lines, i
192 # else add whitespace to candidate list
192 # else add whitespace to candidate list
193 candidates.append((match.group(1), i))
193 candidates.append((match.group(1), i))
194 if candidates:
194 if candidates:
195 # this will sort by whitespace, and by line number,
195 # this will sort by whitespace, and by line number,
196 # less whitespace first
196 # less whitespace first
197 candidates.sort()
197 candidates.sort()
198 return lines, candidates[0][1]
198 return lines, candidates[0][1]
199 else:
199 else:
200 raise IOError('could not find class definition')
200 raise IOError('could not find class definition')
201
201
202 if ismethod(object):
202 if ismethod(object):
203 object = object.__func__
203 object = object.__func__
204 if isfunction(object):
204 if isfunction(object):
205 object = object.__code__
205 object = object.__code__
206 if istraceback(object):
206 if istraceback(object):
207 object = object.tb_frame
207 object = object.tb_frame
208 if isframe(object):
208 if isframe(object):
209 object = object.f_code
209 object = object.f_code
210 if iscode(object):
210 if iscode(object):
211 if not hasattr(object, 'co_firstlineno'):
211 if not hasattr(object, 'co_firstlineno'):
212 raise IOError('could not find function definition')
212 raise IOError('could not find function definition')
213 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
213 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
214 pmatch = pat.match
214 pmatch = pat.match
215 # fperez - fix: sometimes, co_firstlineno can give a number larger than
215 # fperez - fix: sometimes, co_firstlineno can give a number larger than
216 # the length of lines, which causes an error. Safeguard against that.
216 # the length of lines, which causes an error. Safeguard against that.
217 lnum = min(object.co_firstlineno, len(lines)) - 1
217 lnum = min(object.co_firstlineno, len(lines)) - 1
218 while lnum > 0:
218 while lnum > 0:
219 if pmatch(lines[lnum]): break
219 if pmatch(lines[lnum]): break
220 lnum -= 1
220 lnum -= 1
221
221
222 return lines, lnum
222 return lines, lnum
223 raise IOError('could not find code object')
223 raise IOError('could not find code object')
224
224
225
225
226 # This is a patched version of inspect.getargs that applies the (unmerged)
226 # This is a patched version of inspect.getargs that applies the (unmerged)
227 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
227 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
228 # https://github.com/ipython/ipython/issues/8205 and
228 # https://github.com/ipython/ipython/issues/8205 and
229 # https://github.com/ipython/ipython/issues/8293
229 # https://github.com/ipython/ipython/issues/8293
230 def getargs(co):
230 def getargs(co):
231 """Get information about the arguments accepted by a code object.
231 """Get information about the arguments accepted by a code object.
232
232
233 Three things are returned: (args, varargs, varkw), where 'args' is
233 Three things are returned: (args, varargs, varkw), where 'args' is
234 a list of argument names (possibly containing nested lists), and
234 a list of argument names (possibly containing nested lists), and
235 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
235 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
236 if not iscode(co):
236 if not iscode(co):
237 raise TypeError('{!r} is not a code object'.format(co))
237 raise TypeError('{!r} is not a code object'.format(co))
238
238
239 nargs = co.co_argcount
239 nargs = co.co_argcount
240 names = co.co_varnames
240 names = co.co_varnames
241 args = list(names[:nargs])
241 args = list(names[:nargs])
242 step = 0
242 step = 0
243
243
244 # The following acrobatics are for anonymous (tuple) arguments.
244 # The following acrobatics are for anonymous (tuple) arguments.
245 for i in range(nargs):
245 for i in range(nargs):
246 if args[i][:1] in ('', '.'):
246 if args[i][:1] in ('', '.'):
247 stack, remain, count = [], [], []
247 stack, remain, count = [], [], []
248 while step < len(co.co_code):
248 while step < len(co.co_code):
249 op = ord(co.co_code[step])
249 op = ord(co.co_code[step])
250 step = step + 1
250 step = step + 1
251 if op >= dis.HAVE_ARGUMENT:
251 if op >= dis.HAVE_ARGUMENT:
252 opname = dis.opname[op]
252 opname = dis.opname[op]
253 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
253 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
254 step = step + 2
254 step = step + 2
255 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
255 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
256 remain.append(value)
256 remain.append(value)
257 count.append(value)
257 count.append(value)
258 elif opname in ('STORE_FAST', 'STORE_DEREF'):
258 elif opname in ('STORE_FAST', 'STORE_DEREF'):
259 if op in dis.haslocal:
259 if op in dis.haslocal:
260 stack.append(co.co_varnames[value])
260 stack.append(co.co_varnames[value])
261 elif op in dis.hasfree:
261 elif op in dis.hasfree:
262 stack.append((co.co_cellvars + co.co_freevars)[value])
262 stack.append((co.co_cellvars + co.co_freevars)[value])
263 # Special case for sublists of length 1: def foo((bar))
263 # Special case for sublists of length 1: def foo((bar))
264 # doesn't generate the UNPACK_TUPLE bytecode, so if
264 # doesn't generate the UNPACK_TUPLE bytecode, so if
265 # `remain` is empty here, we have such a sublist.
265 # `remain` is empty here, we have such a sublist.
266 if not remain:
266 if not remain:
267 stack[0] = [stack[0]]
267 stack[0] = [stack[0]]
268 break
268 break
269 else:
269 else:
270 remain[-1] = remain[-1] - 1
270 remain[-1] = remain[-1] - 1
271 while remain[-1] == 0:
271 while remain[-1] == 0:
272 remain.pop()
272 remain.pop()
273 size = count.pop()
273 size = count.pop()
274 stack[-size:] = [stack[-size:]]
274 stack[-size:] = [stack[-size:]]
275 if not remain: break
275 if not remain: break
276 remain[-1] = remain[-1] - 1
276 remain[-1] = remain[-1] - 1
277 if not remain: break
277 if not remain: break
278 args[i] = stack[0]
278 args[i] = stack[0]
279
279
280 varargs = None
280 varargs = None
281 if co.co_flags & inspect.CO_VARARGS:
281 if co.co_flags & inspect.CO_VARARGS:
282 varargs = co.co_varnames[nargs]
282 varargs = co.co_varnames[nargs]
283 nargs = nargs + 1
283 nargs = nargs + 1
284 varkw = None
284 varkw = None
285 if co.co_flags & inspect.CO_VARKEYWORDS:
285 if co.co_flags & inspect.CO_VARKEYWORDS:
286 varkw = co.co_varnames[nargs]
286 varkw = co.co_varnames[nargs]
287 return inspect.Arguments(args, varargs, varkw)
287 return inspect.Arguments(args, varargs, varkw)
288
288
289
289
290 # Monkeypatch inspect to apply our bugfix.
290 # Monkeypatch inspect to apply our bugfix.
291 def with_patch_inspect(f):
291 def with_patch_inspect(f):
292 """decorator for monkeypatching inspect.findsource"""
292 """decorator for monkeypatching inspect.findsource"""
293
293
294 def wrapped(*args, **kwargs):
294 def wrapped(*args, **kwargs):
295 save_findsource = inspect.findsource
295 save_findsource = inspect.findsource
296 save_getargs = inspect.getargs
296 save_getargs = inspect.getargs
297 inspect.findsource = findsource
297 inspect.findsource = findsource
298 inspect.getargs = getargs
298 inspect.getargs = getargs
299 try:
299 try:
300 return f(*args, **kwargs)
300 return f(*args, **kwargs)
301 finally:
301 finally:
302 inspect.findsource = save_findsource
302 inspect.findsource = save_findsource
303 inspect.getargs = save_getargs
303 inspect.getargs = save_getargs
304
304
305 return wrapped
305 return wrapped
306
306
307
307
308 if py3compat.PY3:
308 if py3compat.PY3:
309 fixed_getargvalues = inspect.getargvalues
309 fixed_getargvalues = inspect.getargvalues
310 else:
310 else:
311 # Fixes for https://github.com/ipython/ipython/issues/8293
311 # Fixes for https://github.com/ipython/ipython/issues/8293
312 # and https://github.com/ipython/ipython/issues/8205.
312 # and https://github.com/ipython/ipython/issues/8205.
313 # The relevant bug is caused by failure to correctly handle anonymous tuple
313 # The relevant bug is caused by failure to correctly handle anonymous tuple
314 # unpacking, which only exists in Python 2.
314 # unpacking, which only exists in Python 2.
315 fixed_getargvalues = with_patch_inspect(inspect.getargvalues)
315 fixed_getargvalues = with_patch_inspect(inspect.getargvalues)
316
316
317
317
318 def fix_frame_records_filenames(records):
318 def fix_frame_records_filenames(records):
319 """Try to fix the filenames in each record from inspect.getinnerframes().
319 """Try to fix the filenames in each record from inspect.getinnerframes().
320
320
321 Particularly, modules loaded from within zip files have useless filenames
321 Particularly, modules loaded from within zip files have useless filenames
322 attached to their code object, and inspect.getinnerframes() just uses it.
322 attached to their code object, and inspect.getinnerframes() just uses it.
323 """
323 """
324 fixed_records = []
324 fixed_records = []
325 for frame, filename, line_no, func_name, lines, index in records:
325 for frame, filename, line_no, func_name, lines, index in records:
326 # Look inside the frame's globals dictionary for __file__,
326 # Look inside the frame's globals dictionary for __file__,
327 # which should be better. However, keep Cython filenames since
327 # which should be better. However, keep Cython filenames since
328 # we prefer the source filenames over the compiled .so file.
328 # we prefer the source filenames over the compiled .so file.
329 filename = py3compat.cast_unicode_py2(filename, "utf-8")
329 filename = py3compat.cast_unicode_py2(filename, "utf-8")
330 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
330 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
331 better_fn = frame.f_globals.get('__file__', None)
331 better_fn = frame.f_globals.get('__file__', None)
332 if isinstance(better_fn, str):
332 if isinstance(better_fn, str):
333 # Check the type just in case someone did something weird with
333 # Check the type just in case someone did something weird with
334 # __file__. It might also be None if the error occurred during
334 # __file__. It might also be None if the error occurred during
335 # import.
335 # import.
336 filename = better_fn
336 filename = better_fn
337 fixed_records.append((frame, filename, line_no, func_name, lines, index))
337 fixed_records.append((frame, filename, line_no, func_name, lines, index))
338 return fixed_records
338 return fixed_records
339
339
340
340
341 @with_patch_inspect
341 @with_patch_inspect
342 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
342 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
343 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
343 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
344
344
345 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
345 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
346 # If the error is at the console, don't build any context, since it would
346 # If the error is at the console, don't build any context, since it would
347 # otherwise produce 5 blank lines printed out (there is no file at the
347 # otherwise produce 5 blank lines printed out (there is no file at the
348 # console)
348 # console)
349 rec_check = records[tb_offset:]
349 rec_check = records[tb_offset:]
350 try:
350 try:
351 rname = rec_check[0][1]
351 rname = rec_check[0][1]
352 if rname == '<ipython console>' or rname.endswith('<string>'):
352 if rname == '<ipython console>' or rname.endswith('<string>'):
353 return rec_check
353 return rec_check
354 except IndexError:
354 except IndexError:
355 pass
355 pass
356
356
357 aux = traceback.extract_tb(etb)
357 aux = traceback.extract_tb(etb)
358 assert len(records) == len(aux)
358 assert len(records) == len(aux)
359 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
359 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
360 maybeStart = lnum - 1 - context // 2
360 maybeStart = lnum - 1 - context // 2
361 start = max(maybeStart, 0)
361 start = max(maybeStart, 0)
362 end = start + context
362 end = start + context
363 lines = ulinecache.getlines(file)[start:end]
363 lines = ulinecache.getlines(file)[start:end]
364 buf = list(records[i])
364 buf = list(records[i])
365 buf[LNUM_POS] = lnum
365 buf[LNUM_POS] = lnum
366 buf[INDEX_POS] = lnum - 1 - start
366 buf[INDEX_POS] = lnum - 1 - start
367 buf[LINES_POS] = lines
367 buf[LINES_POS] = lines
368 records[i] = tuple(buf)
368 records[i] = tuple(buf)
369 return records[tb_offset:]
369 return records[tb_offset:]
370
370
371 # Helper function -- largely belongs to VerboseTB, but we need the same
371 # Helper function -- largely belongs to VerboseTB, but we need the same
372 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
372 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
373 # can be recognized properly by ipython.el's py-traceback-line-re
373 # can be recognized properly by ipython.el's py-traceback-line-re
374 # (SyntaxErrors have to be treated specially because they have no traceback)
374 # (SyntaxErrors have to be treated specially because they have no traceback)
375
375
376 _parser = PyColorize.Parser()
376 _parser = PyColorize.Parser()
377
377
378
378
379 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, scheme=None):
379 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, scheme=None):
380 numbers_width = INDENT_SIZE - 1
380 numbers_width = INDENT_SIZE - 1
381 res = []
381 res = []
382 i = lnum - index
382 i = lnum - index
383
383
384 # This lets us get fully syntax-highlighted tracebacks.
384 # This lets us get fully syntax-highlighted tracebacks.
385 if scheme is None:
385 if scheme is None:
386 ipinst = get_ipython()
386 ipinst = get_ipython()
387 if ipinst is not None:
387 if ipinst is not None:
388 scheme = ipinst.colors
388 scheme = ipinst.colors
389 else:
389 else:
390 scheme = DEFAULT_SCHEME
390 scheme = DEFAULT_SCHEME
391
391
392 _line_format = _parser.format2
392 _line_format = _parser.format2
393
393
394 for line in lines:
394 for line in lines:
395 line = py3compat.cast_unicode(line)
395 line = py3compat.cast_unicode(line)
396
396
397 new_line, err = _line_format(line, 'str', scheme)
397 new_line, err = _line_format(line, 'str', scheme)
398 if not err: line = new_line
398 if not err: line = new_line
399
399
400 if i == lnum:
400 if i == lnum:
401 # This is the line with the error
401 # This is the line with the error
402 pad = numbers_width - len(str(i))
402 pad = numbers_width - len(str(i))
403 if pad >= 3:
403 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
404 marker = '-' * (pad - 3) + '-> '
405 elif pad == 2:
406 marker = '> '
407 elif pad == 1:
408 marker = '>'
409 else:
410 marker = ''
411 num = marker + str(i)
412 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
404 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
413 Colors.line, line, Colors.Normal)
405 Colors.line, line, Colors.Normal)
414 else:
406 else:
415 num = '%*s' % (numbers_width, i)
407 num = '%*s' % (numbers_width, i)
416 line = '%s%s%s %s' % (Colors.lineno, num,
408 line = '%s%s%s %s' % (Colors.lineno, num,
417 Colors.Normal, line)
409 Colors.Normal, line)
418
410
419 res.append(line)
411 res.append(line)
420 if lvals and i == lnum:
412 if lvals and i == lnum:
421 res.append(lvals + '\n')
413 res.append(lvals + '\n')
422 i = i + 1
414 i = i + 1
423 return res
415 return res
424
416
425 def is_recursion_error(etype, value, records):
417 def is_recursion_error(etype, value, records):
426 try:
418 try:
427 # RecursionError is new in Python 3.5
419 # RecursionError is new in Python 3.5
428 recursion_error_type = RecursionError
420 recursion_error_type = RecursionError
429 except NameError:
421 except NameError:
430 recursion_error_type = RuntimeError
422 recursion_error_type = RuntimeError
431
423
432 # The default recursion limit is 1000, but some of that will be taken up
424 # The default recursion limit is 1000, but some of that will be taken up
433 # by stack frames in IPython itself. >500 frames probably indicates
425 # by stack frames in IPython itself. >500 frames probably indicates
434 # a recursion error.
426 # a recursion error.
435 return (etype is recursion_error_type) \
427 return (etype is recursion_error_type) \
436 and "recursion" in str(value).lower() \
428 and "recursion" in str(value).lower() \
437 and len(records) > 500
429 and len(records) > 500
438
430
439 def find_recursion(etype, value, records):
431 def find_recursion(etype, value, records):
440 """Identify the repeating stack frames from a RecursionError traceback
432 """Identify the repeating stack frames from a RecursionError traceback
441
433
442 'records' is a list as returned by VerboseTB.get_records()
434 'records' is a list as returned by VerboseTB.get_records()
443
435
444 Returns (last_unique, repeat_length)
436 Returns (last_unique, repeat_length)
445 """
437 """
446 # This involves a bit of guesswork - we want to show enough of the traceback
438 # This involves a bit of guesswork - we want to show enough of the traceback
447 # to indicate where the recursion is occurring. We guess that the innermost
439 # to indicate where the recursion is occurring. We guess that the innermost
448 # quarter of the traceback (250 frames by default) is repeats, and find the
440 # quarter of the traceback (250 frames by default) is repeats, and find the
449 # first frame (from in to out) that looks different.
441 # first frame (from in to out) that looks different.
450 if not is_recursion_error(etype, value, records):
442 if not is_recursion_error(etype, value, records):
451 return len(records), 0
443 return len(records), 0
452
444
453 # Select filename, lineno, func_name to track frames with
445 # Select filename, lineno, func_name to track frames with
454 records = [r[1:4] for r in records]
446 records = [r[1:4] for r in records]
455 inner_frames = records[-(len(records)//4):]
447 inner_frames = records[-(len(records)//4):]
456 frames_repeated = set(inner_frames)
448 frames_repeated = set(inner_frames)
457
449
458 last_seen_at = {}
450 last_seen_at = {}
459 longest_repeat = 0
451 longest_repeat = 0
460 i = len(records)
452 i = len(records)
461 for frame in reversed(records):
453 for frame in reversed(records):
462 i -= 1
454 i -= 1
463 if frame not in frames_repeated:
455 if frame not in frames_repeated:
464 last_unique = i
456 last_unique = i
465 break
457 break
466
458
467 if frame in last_seen_at:
459 if frame in last_seen_at:
468 distance = last_seen_at[frame] - i
460 distance = last_seen_at[frame] - i
469 longest_repeat = max(longest_repeat, distance)
461 longest_repeat = max(longest_repeat, distance)
470
462
471 last_seen_at[frame] = i
463 last_seen_at[frame] = i
472 else:
464 else:
473 last_unique = 0 # The whole traceback was recursion
465 last_unique = 0 # The whole traceback was recursion
474
466
475 return last_unique, longest_repeat
467 return last_unique, longest_repeat
476
468
477 #---------------------------------------------------------------------------
469 #---------------------------------------------------------------------------
478 # Module classes
470 # Module classes
479 class TBTools(object):
471 class TBTools(object):
480 """Basic tools used by all traceback printer classes."""
472 """Basic tools used by all traceback printer classes."""
481
473
482 # Number of frames to skip when reporting tracebacks
474 # Number of frames to skip when reporting tracebacks
483 tb_offset = 0
475 tb_offset = 0
484
476
485 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
477 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
486 # Whether to call the interactive pdb debugger after printing
478 # Whether to call the interactive pdb debugger after printing
487 # tracebacks or not
479 # tracebacks or not
488 self.call_pdb = call_pdb
480 self.call_pdb = call_pdb
489
481
490 # Output stream to write to. Note that we store the original value in
482 # Output stream to write to. Note that we store the original value in
491 # a private attribute and then make the public ostream a property, so
483 # a private attribute and then make the public ostream a property, so
492 # that we can delay accessing io.stdout until runtime. The way
484 # that we can delay accessing io.stdout until runtime. The way
493 # things are written now, the io.stdout object is dynamically managed
485 # things are written now, the io.stdout object is dynamically managed
494 # so a reference to it should NEVER be stored statically. This
486 # so a reference to it should NEVER be stored statically. This
495 # property approach confines this detail to a single location, and all
487 # property approach confines this detail to a single location, and all
496 # subclasses can simply access self.ostream for writing.
488 # subclasses can simply access self.ostream for writing.
497 self._ostream = ostream
489 self._ostream = ostream
498
490
499 # Create color table
491 # Create color table
500 self.color_scheme_table = exception_colors()
492 self.color_scheme_table = exception_colors()
501
493
502 self.set_colors(color_scheme)
494 self.set_colors(color_scheme)
503 self.old_scheme = color_scheme # save initial value for toggles
495 self.old_scheme = color_scheme # save initial value for toggles
504
496
505 if call_pdb:
497 if call_pdb:
506 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
498 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
507 else:
499 else:
508 self.pdb = None
500 self.pdb = None
509
501
510 def _get_ostream(self):
502 def _get_ostream(self):
511 """Output stream that exceptions are written to.
503 """Output stream that exceptions are written to.
512
504
513 Valid values are:
505 Valid values are:
514
506
515 - None: the default, which means that IPython will dynamically resolve
507 - None: the default, which means that IPython will dynamically resolve
516 to io.stdout. This ensures compatibility with most tools, including
508 to io.stdout. This ensures compatibility with most tools, including
517 Windows (where plain stdout doesn't recognize ANSI escapes).
509 Windows (where plain stdout doesn't recognize ANSI escapes).
518
510
519 - Any object with 'write' and 'flush' attributes.
511 - Any object with 'write' and 'flush' attributes.
520 """
512 """
521 return io.stdout if self._ostream is None else self._ostream
513 return io.stdout if self._ostream is None else self._ostream
522
514
523 def _set_ostream(self, val):
515 def _set_ostream(self, val):
524 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
516 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
525 self._ostream = val
517 self._ostream = val
526
518
527 ostream = property(_get_ostream, _set_ostream)
519 ostream = property(_get_ostream, _set_ostream)
528
520
529 def set_colors(self, *args, **kw):
521 def set_colors(self, *args, **kw):
530 """Shorthand access to the color table scheme selector method."""
522 """Shorthand access to the color table scheme selector method."""
531
523
532 # Set own color table
524 # Set own color table
533 self.color_scheme_table.set_active_scheme(*args, **kw)
525 self.color_scheme_table.set_active_scheme(*args, **kw)
534 # for convenience, set Colors to the active scheme
526 # for convenience, set Colors to the active scheme
535 self.Colors = self.color_scheme_table.active_colors
527 self.Colors = self.color_scheme_table.active_colors
536 # Also set colors of debugger
528 # Also set colors of debugger
537 if hasattr(self, 'pdb') and self.pdb is not None:
529 if hasattr(self, 'pdb') and self.pdb is not None:
538 self.pdb.set_colors(*args, **kw)
530 self.pdb.set_colors(*args, **kw)
539
531
540 def color_toggle(self):
532 def color_toggle(self):
541 """Toggle between the currently active color scheme and NoColor."""
533 """Toggle between the currently active color scheme and NoColor."""
542
534
543 if self.color_scheme_table.active_scheme_name == 'NoColor':
535 if self.color_scheme_table.active_scheme_name == 'NoColor':
544 self.color_scheme_table.set_active_scheme(self.old_scheme)
536 self.color_scheme_table.set_active_scheme(self.old_scheme)
545 self.Colors = self.color_scheme_table.active_colors
537 self.Colors = self.color_scheme_table.active_colors
546 else:
538 else:
547 self.old_scheme = self.color_scheme_table.active_scheme_name
539 self.old_scheme = self.color_scheme_table.active_scheme_name
548 self.color_scheme_table.set_active_scheme('NoColor')
540 self.color_scheme_table.set_active_scheme('NoColor')
549 self.Colors = self.color_scheme_table.active_colors
541 self.Colors = self.color_scheme_table.active_colors
550
542
551 def stb2text(self, stb):
543 def stb2text(self, stb):
552 """Convert a structured traceback (a list) to a string."""
544 """Convert a structured traceback (a list) to a string."""
553 return '\n'.join(stb)
545 return '\n'.join(stb)
554
546
555 def text(self, etype, value, tb, tb_offset=None, context=5):
547 def text(self, etype, value, tb, tb_offset=None, context=5):
556 """Return formatted traceback.
548 """Return formatted traceback.
557
549
558 Subclasses may override this if they add extra arguments.
550 Subclasses may override this if they add extra arguments.
559 """
551 """
560 tb_list = self.structured_traceback(etype, value, tb,
552 tb_list = self.structured_traceback(etype, value, tb,
561 tb_offset, context)
553 tb_offset, context)
562 return self.stb2text(tb_list)
554 return self.stb2text(tb_list)
563
555
564 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
556 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
565 context=5, mode=None):
557 context=5, mode=None):
566 """Return a list of traceback frames.
558 """Return a list of traceback frames.
567
559
568 Must be implemented by each class.
560 Must be implemented by each class.
569 """
561 """
570 raise NotImplementedError()
562 raise NotImplementedError()
571
563
572
564
573 #---------------------------------------------------------------------------
565 #---------------------------------------------------------------------------
574 class ListTB(TBTools):
566 class ListTB(TBTools):
575 """Print traceback information from a traceback list, with optional color.
567 """Print traceback information from a traceback list, with optional color.
576
568
577 Calling requires 3 arguments: (etype, evalue, elist)
569 Calling requires 3 arguments: (etype, evalue, elist)
578 as would be obtained by::
570 as would be obtained by::
579
571
580 etype, evalue, tb = sys.exc_info()
572 etype, evalue, tb = sys.exc_info()
581 if tb:
573 if tb:
582 elist = traceback.extract_tb(tb)
574 elist = traceback.extract_tb(tb)
583 else:
575 else:
584 elist = None
576 elist = None
585
577
586 It can thus be used by programs which need to process the traceback before
578 It can thus be used by programs which need to process the traceback before
587 printing (such as console replacements based on the code module from the
579 printing (such as console replacements based on the code module from the
588 standard library).
580 standard library).
589
581
590 Because they are meant to be called without a full traceback (only a
582 Because they are meant to be called without a full traceback (only a
591 list), instances of this class can't call the interactive pdb debugger."""
583 list), instances of this class can't call the interactive pdb debugger."""
592
584
593 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
585 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
594 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
586 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
595 ostream=ostream)
587 ostream=ostream)
596
588
597 def __call__(self, etype, value, elist):
589 def __call__(self, etype, value, elist):
598 self.ostream.flush()
590 self.ostream.flush()
599 self.ostream.write(self.text(etype, value, elist))
591 self.ostream.write(self.text(etype, value, elist))
600 self.ostream.write('\n')
592 self.ostream.write('\n')
601
593
602 def structured_traceback(self, etype, value, elist, tb_offset=None,
594 def structured_traceback(self, etype, value, elist, tb_offset=None,
603 context=5):
595 context=5):
604 """Return a color formatted string with the traceback info.
596 """Return a color formatted string with the traceback info.
605
597
606 Parameters
598 Parameters
607 ----------
599 ----------
608 etype : exception type
600 etype : exception type
609 Type of the exception raised.
601 Type of the exception raised.
610
602
611 value : object
603 value : object
612 Data stored in the exception
604 Data stored in the exception
613
605
614 elist : list
606 elist : list
615 List of frames, see class docstring for details.
607 List of frames, see class docstring for details.
616
608
617 tb_offset : int, optional
609 tb_offset : int, optional
618 Number of frames in the traceback to skip. If not given, the
610 Number of frames in the traceback to skip. If not given, the
619 instance value is used (set in constructor).
611 instance value is used (set in constructor).
620
612
621 context : int, optional
613 context : int, optional
622 Number of lines of context information to print.
614 Number of lines of context information to print.
623
615
624 Returns
616 Returns
625 -------
617 -------
626 String with formatted exception.
618 String with formatted exception.
627 """
619 """
628 tb_offset = self.tb_offset if tb_offset is None else tb_offset
620 tb_offset = self.tb_offset if tb_offset is None else tb_offset
629 Colors = self.Colors
621 Colors = self.Colors
630 out_list = []
622 out_list = []
631 if elist:
623 if elist:
632
624
633 if tb_offset and len(elist) > tb_offset:
625 if tb_offset and len(elist) > tb_offset:
634 elist = elist[tb_offset:]
626 elist = elist[tb_offset:]
635
627
636 out_list.append('Traceback %s(most recent call last)%s:' %
628 out_list.append('Traceback %s(most recent call last)%s:' %
637 (Colors.normalEm, Colors.Normal) + '\n')
629 (Colors.normalEm, Colors.Normal) + '\n')
638 out_list.extend(self._format_list(elist))
630 out_list.extend(self._format_list(elist))
639 # The exception info should be a single entry in the list.
631 # The exception info should be a single entry in the list.
640 lines = ''.join(self._format_exception_only(etype, value))
632 lines = ''.join(self._format_exception_only(etype, value))
641 out_list.append(lines)
633 out_list.append(lines)
642
634
643 # Note: this code originally read:
635 # Note: this code originally read:
644
636
645 ## for line in lines[:-1]:
637 ## for line in lines[:-1]:
646 ## out_list.append(" "+line)
638 ## out_list.append(" "+line)
647 ## out_list.append(lines[-1])
639 ## out_list.append(lines[-1])
648
640
649 # This means it was indenting everything but the last line by a little
641 # This means it was indenting everything but the last line by a little
650 # bit. I've disabled this for now, but if we see ugliness somewhere we
642 # bit. I've disabled this for now, but if we see ugliness somewhere we
651 # can restore it.
643 # can restore it.
652
644
653 return out_list
645 return out_list
654
646
655 def _format_list(self, extracted_list):
647 def _format_list(self, extracted_list):
656 """Format a list of traceback entry tuples for printing.
648 """Format a list of traceback entry tuples for printing.
657
649
658 Given a list of tuples as returned by extract_tb() or
650 Given a list of tuples as returned by extract_tb() or
659 extract_stack(), return a list of strings ready for printing.
651 extract_stack(), return a list of strings ready for printing.
660 Each string in the resulting list corresponds to the item with the
652 Each string in the resulting list corresponds to the item with the
661 same index in the argument list. Each string ends in a newline;
653 same index in the argument list. Each string ends in a newline;
662 the strings may contain internal newlines as well, for those items
654 the strings may contain internal newlines as well, for those items
663 whose source text line is not None.
655 whose source text line is not None.
664
656
665 Lifted almost verbatim from traceback.py
657 Lifted almost verbatim from traceback.py
666 """
658 """
667
659
668 Colors = self.Colors
660 Colors = self.Colors
669 list = []
661 list = []
670 for filename, lineno, name, line in extracted_list[:-1]:
662 for filename, lineno, name, line in extracted_list[:-1]:
671 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
663 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
672 (Colors.filename, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.Normal,
664 (Colors.filename, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.Normal,
673 Colors.lineno, lineno, Colors.Normal,
665 Colors.lineno, lineno, Colors.Normal,
674 Colors.name, py3compat.cast_unicode_py2(name, "utf-8"), Colors.Normal)
666 Colors.name, py3compat.cast_unicode_py2(name, "utf-8"), Colors.Normal)
675 if line:
667 if line:
676 item += ' %s\n' % line.strip()
668 item += ' %s\n' % line.strip()
677 list.append(item)
669 list.append(item)
678 # Emphasize the last entry
670 # Emphasize the last entry
679 filename, lineno, name, line = extracted_list[-1]
671 filename, lineno, name, line = extracted_list[-1]
680 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
672 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
681 (Colors.normalEm,
673 (Colors.normalEm,
682 Colors.filenameEm, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.normalEm,
674 Colors.filenameEm, py3compat.cast_unicode_py2(filename, "utf-8"), Colors.normalEm,
683 Colors.linenoEm, lineno, Colors.normalEm,
675 Colors.linenoEm, lineno, Colors.normalEm,
684 Colors.nameEm, py3compat.cast_unicode_py2(name, "utf-8"), Colors.normalEm,
676 Colors.nameEm, py3compat.cast_unicode_py2(name, "utf-8"), Colors.normalEm,
685 Colors.Normal)
677 Colors.Normal)
686 if line:
678 if line:
687 item += '%s %s%s\n' % (Colors.line, line.strip(),
679 item += '%s %s%s\n' % (Colors.line, line.strip(),
688 Colors.Normal)
680 Colors.Normal)
689 list.append(item)
681 list.append(item)
690 return list
682 return list
691
683
692 def _format_exception_only(self, etype, value):
684 def _format_exception_only(self, etype, value):
693 """Format the exception part of a traceback.
685 """Format the exception part of a traceback.
694
686
695 The arguments are the exception type and value such as given by
687 The arguments are the exception type and value such as given by
696 sys.exc_info()[:2]. The return value is a list of strings, each ending
688 sys.exc_info()[:2]. The return value is a list of strings, each ending
697 in a newline. Normally, the list contains a single string; however,
689 in a newline. Normally, the list contains a single string; however,
698 for SyntaxError exceptions, it contains several lines that (when
690 for SyntaxError exceptions, it contains several lines that (when
699 printed) display detailed information about where the syntax error
691 printed) display detailed information about where the syntax error
700 occurred. The message indicating which exception occurred is the
692 occurred. The message indicating which exception occurred is the
701 always last string in the list.
693 always last string in the list.
702
694
703 Also lifted nearly verbatim from traceback.py
695 Also lifted nearly verbatim from traceback.py
704 """
696 """
705 have_filedata = False
697 have_filedata = False
706 Colors = self.Colors
698 Colors = self.Colors
707 list = []
699 list = []
708 stype = Colors.excName + etype.__name__ + Colors.Normal
700 stype = Colors.excName + etype.__name__ + Colors.Normal
709 if value is None:
701 if value is None:
710 # Not sure if this can still happen in Python 2.6 and above
702 # Not sure if this can still happen in Python 2.6 and above
711 list.append(py3compat.cast_unicode(stype) + '\n')
703 list.append(py3compat.cast_unicode(stype) + '\n')
712 else:
704 else:
713 if issubclass(etype, SyntaxError):
705 if issubclass(etype, SyntaxError):
714 have_filedata = True
706 have_filedata = True
715 if not value.filename: value.filename = "<string>"
707 if not value.filename: value.filename = "<string>"
716 if value.lineno:
708 if value.lineno:
717 lineno = value.lineno
709 lineno = value.lineno
718 textline = ulinecache.getline(value.filename, value.lineno)
710 textline = ulinecache.getline(value.filename, value.lineno)
719 else:
711 else:
720 lineno = 'unknown'
712 lineno = 'unknown'
721 textline = ''
713 textline = ''
722 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
714 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
723 (Colors.normalEm,
715 (Colors.normalEm,
724 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
716 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
725 Colors.linenoEm, lineno, Colors.Normal ))
717 Colors.linenoEm, lineno, Colors.Normal ))
726 if textline == '':
718 if textline == '':
727 textline = py3compat.cast_unicode(value.text, "utf-8")
719 textline = py3compat.cast_unicode(value.text, "utf-8")
728
720
729 if textline is not None:
721 if textline is not None:
730 i = 0
722 i = 0
731 while i < len(textline) and textline[i].isspace():
723 while i < len(textline) and textline[i].isspace():
732 i += 1
724 i += 1
733 list.append('%s %s%s\n' % (Colors.line,
725 list.append('%s %s%s\n' % (Colors.line,
734 textline.strip(),
726 textline.strip(),
735 Colors.Normal))
727 Colors.Normal))
736 if value.offset is not None:
728 if value.offset is not None:
737 s = ' '
729 s = ' '
738 for c in textline[i:value.offset - 1]:
730 for c in textline[i:value.offset - 1]:
739 if c.isspace():
731 if c.isspace():
740 s += c
732 s += c
741 else:
733 else:
742 s += ' '
734 s += ' '
743 list.append('%s%s^%s\n' % (Colors.caret, s,
735 list.append('%s%s^%s\n' % (Colors.caret, s,
744 Colors.Normal))
736 Colors.Normal))
745
737
746 try:
738 try:
747 s = value.msg
739 s = value.msg
748 except Exception:
740 except Exception:
749 s = self._some_str(value)
741 s = self._some_str(value)
750 if s:
742 if s:
751 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
743 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
752 Colors.Normal, s))
744 Colors.Normal, s))
753 else:
745 else:
754 list.append('%s\n' % str(stype))
746 list.append('%s\n' % str(stype))
755
747
756 # sync with user hooks
748 # sync with user hooks
757 if have_filedata:
749 if have_filedata:
758 ipinst = get_ipython()
750 ipinst = get_ipython()
759 if ipinst is not None:
751 if ipinst is not None:
760 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
752 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
761
753
762 return list
754 return list
763
755
764 def get_exception_only(self, etype, value):
756 def get_exception_only(self, etype, value):
765 """Only print the exception type and message, without a traceback.
757 """Only print the exception type and message, without a traceback.
766
758
767 Parameters
759 Parameters
768 ----------
760 ----------
769 etype : exception type
761 etype : exception type
770 value : exception value
762 value : exception value
771 """
763 """
772 return ListTB.structured_traceback(self, etype, value, [])
764 return ListTB.structured_traceback(self, etype, value, [])
773
765
774 def show_exception_only(self, etype, evalue):
766 def show_exception_only(self, etype, evalue):
775 """Only print the exception type and message, without a traceback.
767 """Only print the exception type and message, without a traceback.
776
768
777 Parameters
769 Parameters
778 ----------
770 ----------
779 etype : exception type
771 etype : exception type
780 value : exception value
772 value : exception value
781 """
773 """
782 # This method needs to use __call__ from *this* class, not the one from
774 # This method needs to use __call__ from *this* class, not the one from
783 # a subclass whose signature or behavior may be different
775 # a subclass whose signature or behavior may be different
784 ostream = self.ostream
776 ostream = self.ostream
785 ostream.flush()
777 ostream.flush()
786 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
778 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
787 ostream.flush()
779 ostream.flush()
788
780
789 def _some_str(self, value):
781 def _some_str(self, value):
790 # Lifted from traceback.py
782 # Lifted from traceback.py
791 try:
783 try:
792 return str(value)
784 return str(value)
793 except:
785 except:
794 return '<unprintable %s object>' % type(value).__name__
786 return '<unprintable %s object>' % type(value).__name__
795
787
796
788
797 #----------------------------------------------------------------------------
789 #----------------------------------------------------------------------------
798 class VerboseTB(TBTools):
790 class VerboseTB(TBTools):
799 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
791 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
800 of HTML. Requires inspect and pydoc. Crazy, man.
792 of HTML. Requires inspect and pydoc. Crazy, man.
801
793
802 Modified version which optionally strips the topmost entries from the
794 Modified version which optionally strips the topmost entries from the
803 traceback, to be used with alternate interpreters (because their own code
795 traceback, to be used with alternate interpreters (because their own code
804 would appear in the traceback)."""
796 would appear in the traceback)."""
805
797
806 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
798 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
807 tb_offset=0, long_header=False, include_vars=True,
799 tb_offset=0, long_header=False, include_vars=True,
808 check_cache=None):
800 check_cache=None):
809 """Specify traceback offset, headers and color scheme.
801 """Specify traceback offset, headers and color scheme.
810
802
811 Define how many frames to drop from the tracebacks. Calling it with
803 Define how many frames to drop from the tracebacks. Calling it with
812 tb_offset=1 allows use of this handler in interpreters which will have
804 tb_offset=1 allows use of this handler in interpreters which will have
813 their own code at the top of the traceback (VerboseTB will first
805 their own code at the top of the traceback (VerboseTB will first
814 remove that frame before printing the traceback info)."""
806 remove that frame before printing the traceback info)."""
815 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
807 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
816 ostream=ostream)
808 ostream=ostream)
817 self.tb_offset = tb_offset
809 self.tb_offset = tb_offset
818 self.long_header = long_header
810 self.long_header = long_header
819 self.include_vars = include_vars
811 self.include_vars = include_vars
820 # By default we use linecache.checkcache, but the user can provide a
812 # By default we use linecache.checkcache, but the user can provide a
821 # different check_cache implementation. This is used by the IPython
813 # different check_cache implementation. This is used by the IPython
822 # kernel to provide tracebacks for interactive code that is cached,
814 # kernel to provide tracebacks for interactive code that is cached,
823 # by a compiler instance that flushes the linecache but preserves its
815 # by a compiler instance that flushes the linecache but preserves its
824 # own code cache.
816 # own code cache.
825 if check_cache is None:
817 if check_cache is None:
826 check_cache = linecache.checkcache
818 check_cache = linecache.checkcache
827 self.check_cache = check_cache
819 self.check_cache = check_cache
828
820
829 def format_records(self, records, last_unique, recursion_repeat):
821 def format_records(self, records, last_unique, recursion_repeat):
830 """Format the stack frames of the traceback"""
822 """Format the stack frames of the traceback"""
831 frames = []
823 frames = []
832 for r in records[:last_unique+recursion_repeat+1]:
824 for r in records[:last_unique+recursion_repeat+1]:
833 #print '*** record:',file,lnum,func,lines,index # dbg
825 #print '*** record:',file,lnum,func,lines,index # dbg
834 frames.append(self.format_record(*r))
826 frames.append(self.format_record(*r))
835
827
836 if recursion_repeat:
828 if recursion_repeat:
837 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
829 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
838 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
830 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
839
831
840 return frames
832 return frames
841
833
842 def format_record(self, frame, file, lnum, func, lines, index):
834 def format_record(self, frame, file, lnum, func, lines, index):
843 """Format a single stack frame"""
835 """Format a single stack frame"""
844 Colors = self.Colors # just a shorthand + quicker name lookup
836 Colors = self.Colors # just a shorthand + quicker name lookup
845 ColorsNormal = Colors.Normal # used a lot
837 ColorsNormal = Colors.Normal # used a lot
846 col_scheme = self.color_scheme_table.active_scheme_name
838 col_scheme = self.color_scheme_table.active_scheme_name
847 indent = ' ' * INDENT_SIZE
839 indent = ' ' * INDENT_SIZE
848 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
840 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
849 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
841 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
850 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
842 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
851 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
843 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
852 ColorsNormal)
844 ColorsNormal)
853 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
845 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
854 (Colors.vName, Colors.valEm, ColorsNormal)
846 (Colors.vName, Colors.valEm, ColorsNormal)
855 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
847 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
856 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
848 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
857 Colors.vName, ColorsNormal)
849 Colors.vName, ColorsNormal)
858 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
850 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
859
851
860 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
852 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
861 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
853 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
862 ColorsNormal)
854 ColorsNormal)
863
855
864 abspath = os.path.abspath
856 abspath = os.path.abspath
865
857
866
858
867 if not file:
859 if not file:
868 file = '?'
860 file = '?'
869 elif file.startswith(str("<")) and file.endswith(str(">")):
861 elif file.startswith(str("<")) and file.endswith(str(">")):
870 # Not a real filename, no problem...
862 # Not a real filename, no problem...
871 pass
863 pass
872 elif not os.path.isabs(file):
864 elif not os.path.isabs(file):
873 # Try to make the filename absolute by trying all
865 # Try to make the filename absolute by trying all
874 # sys.path entries (which is also what linecache does)
866 # sys.path entries (which is also what linecache does)
875 for dirname in sys.path:
867 for dirname in sys.path:
876 try:
868 try:
877 fullname = os.path.join(dirname, file)
869 fullname = os.path.join(dirname, file)
878 if os.path.isfile(fullname):
870 if os.path.isfile(fullname):
879 file = os.path.abspath(fullname)
871 file = os.path.abspath(fullname)
880 break
872 break
881 except Exception:
873 except Exception:
882 # Just in case that sys.path contains very
874 # Just in case that sys.path contains very
883 # strange entries...
875 # strange entries...
884 pass
876 pass
885
877
886 file = py3compat.cast_unicode(file, util_path.fs_encoding)
878 file = py3compat.cast_unicode(file, util_path.fs_encoding)
887 link = tpl_link % file
879 link = tpl_link % file
888 args, varargs, varkw, locals = fixed_getargvalues(frame)
880 args, varargs, varkw, locals = fixed_getargvalues(frame)
889
881
890 if func == '?':
882 if func == '?':
891 call = ''
883 call = ''
892 else:
884 else:
893 # Decide whether to include variable details or not
885 # Decide whether to include variable details or not
894 var_repr = self.include_vars and eqrepr or nullrepr
886 var_repr = self.include_vars and eqrepr or nullrepr
895 try:
887 try:
896 call = tpl_call % (func, inspect.formatargvalues(args,
888 call = tpl_call % (func, inspect.formatargvalues(args,
897 varargs, varkw,
889 varargs, varkw,
898 locals, formatvalue=var_repr))
890 locals, formatvalue=var_repr))
899 except KeyError:
891 except KeyError:
900 # This happens in situations like errors inside generator
892 # This happens in situations like errors inside generator
901 # expressions, where local variables are listed in the
893 # expressions, where local variables are listed in the
902 # line, but can't be extracted from the frame. I'm not
894 # line, but can't be extracted from the frame. I'm not
903 # 100% sure this isn't actually a bug in inspect itself,
895 # 100% sure this isn't actually a bug in inspect itself,
904 # but since there's no info for us to compute with, the
896 # but since there's no info for us to compute with, the
905 # best we can do is report the failure and move on. Here
897 # best we can do is report the failure and move on. Here
906 # we must *not* call any traceback construction again,
898 # we must *not* call any traceback construction again,
907 # because that would mess up use of %debug later on. So we
899 # because that would mess up use of %debug later on. So we
908 # simply report the failure and move on. The only
900 # simply report the failure and move on. The only
909 # limitation will be that this frame won't have locals
901 # limitation will be that this frame won't have locals
910 # listed in the call signature. Quite subtle problem...
902 # listed in the call signature. Quite subtle problem...
911 # I can't think of a good way to validate this in a unit
903 # I can't think of a good way to validate this in a unit
912 # test, but running a script consisting of:
904 # test, but running a script consisting of:
913 # dict( (k,v.strip()) for (k,v) in range(10) )
905 # dict( (k,v.strip()) for (k,v) in range(10) )
914 # will illustrate the error, if this exception catch is
906 # will illustrate the error, if this exception catch is
915 # disabled.
907 # disabled.
916 call = tpl_call_fail % func
908 call = tpl_call_fail % func
917
909
918 # Don't attempt to tokenize binary files.
910 # Don't attempt to tokenize binary files.
919 if file.endswith(('.so', '.pyd', '.dll')):
911 if file.endswith(('.so', '.pyd', '.dll')):
920 return '%s %s\n' % (link, call)
912 return '%s %s\n' % (link, call)
921
913
922 elif file.endswith(('.pyc', '.pyo')):
914 elif file.endswith(('.pyc', '.pyo')):
923 # Look up the corresponding source file.
915 # Look up the corresponding source file.
924 file = openpy.source_from_cache(file)
916 file = openpy.source_from_cache(file)
925
917
926 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
918 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
927 line = getline(file, lnum[0])
919 line = getline(file, lnum[0])
928 lnum[0] += 1
920 lnum[0] += 1
929 return line
921 return line
930
922
931 # Build the list of names on this line of code where the exception
923 # Build the list of names on this line of code where the exception
932 # occurred.
924 # occurred.
933 try:
925 try:
934 names = []
926 names = []
935 name_cont = False
927 name_cont = False
936
928
937 for token_type, token, start, end, line in generate_tokens(linereader):
929 for token_type, token, start, end, line in generate_tokens(linereader):
938 # build composite names
930 # build composite names
939 if token_type == tokenize.NAME and token not in keyword.kwlist:
931 if token_type == tokenize.NAME and token not in keyword.kwlist:
940 if name_cont:
932 if name_cont:
941 # Continuation of a dotted name
933 # Continuation of a dotted name
942 try:
934 try:
943 names[-1].append(token)
935 names[-1].append(token)
944 except IndexError:
936 except IndexError:
945 names.append([token])
937 names.append([token])
946 name_cont = False
938 name_cont = False
947 else:
939 else:
948 # Regular new names. We append everything, the caller
940 # Regular new names. We append everything, the caller
949 # will be responsible for pruning the list later. It's
941 # will be responsible for pruning the list later. It's
950 # very tricky to try to prune as we go, b/c composite
942 # very tricky to try to prune as we go, b/c composite
951 # names can fool us. The pruning at the end is easy
943 # names can fool us. The pruning at the end is easy
952 # to do (or the caller can print a list with repeated
944 # to do (or the caller can print a list with repeated
953 # names if so desired.
945 # names if so desired.
954 names.append([token])
946 names.append([token])
955 elif token == '.':
947 elif token == '.':
956 name_cont = True
948 name_cont = True
957 elif token_type == tokenize.NEWLINE:
949 elif token_type == tokenize.NEWLINE:
958 break
950 break
959
951
960 except (IndexError, UnicodeDecodeError, SyntaxError):
952 except (IndexError, UnicodeDecodeError, SyntaxError):
961 # signals exit of tokenizer
953 # signals exit of tokenizer
962 # SyntaxError can occur if the file is not actually Python
954 # SyntaxError can occur if the file is not actually Python
963 # - see gh-6300
955 # - see gh-6300
964 pass
956 pass
965 except tokenize.TokenError as msg:
957 except tokenize.TokenError as msg:
966 _m = ("An unexpected error occurred while tokenizing input\n"
958 _m = ("An unexpected error occurred while tokenizing input\n"
967 "The following traceback may be corrupted or invalid\n"
959 "The following traceback may be corrupted or invalid\n"
968 "The error message is: %s\n" % msg)
960 "The error message is: %s\n" % msg)
969 error(_m)
961 error(_m)
970
962
971 # Join composite names (e.g. "dict.fromkeys")
963 # Join composite names (e.g. "dict.fromkeys")
972 names = ['.'.join(n) for n in names]
964 names = ['.'.join(n) for n in names]
973 # prune names list of duplicates, but keep the right order
965 # prune names list of duplicates, but keep the right order
974 unique_names = uniq_stable(names)
966 unique_names = uniq_stable(names)
975
967
976 # Start loop over vars
968 # Start loop over vars
977 lvals = []
969 lvals = []
978 if self.include_vars:
970 if self.include_vars:
979 for name_full in unique_names:
971 for name_full in unique_names:
980 name_base = name_full.split('.', 1)[0]
972 name_base = name_full.split('.', 1)[0]
981 if name_base in frame.f_code.co_varnames:
973 if name_base in frame.f_code.co_varnames:
982 if name_base in locals:
974 if name_base in locals:
983 try:
975 try:
984 value = repr(eval(name_full, locals))
976 value = repr(eval(name_full, locals))
985 except:
977 except:
986 value = undefined
978 value = undefined
987 else:
979 else:
988 value = undefined
980 value = undefined
989 name = tpl_local_var % name_full
981 name = tpl_local_var % name_full
990 else:
982 else:
991 if name_base in frame.f_globals:
983 if name_base in frame.f_globals:
992 try:
984 try:
993 value = repr(eval(name_full, frame.f_globals))
985 value = repr(eval(name_full, frame.f_globals))
994 except:
986 except:
995 value = undefined
987 value = undefined
996 else:
988 else:
997 value = undefined
989 value = undefined
998 name = tpl_global_var % name_full
990 name = tpl_global_var % name_full
999 lvals.append(tpl_name_val % (name, value))
991 lvals.append(tpl_name_val % (name, value))
1000 if lvals:
992 if lvals:
1001 lvals = '%s%s' % (indent, em_normal.join(lvals))
993 lvals = '%s%s' % (indent, em_normal.join(lvals))
1002 else:
994 else:
1003 lvals = ''
995 lvals = ''
1004
996
1005 level = '%s %s\n' % (link, call)
997 level = '%s %s\n' % (link, call)
1006
998
1007 if index is None:
999 if index is None:
1008 return level
1000 return level
1009 else:
1001 else:
1010 return '%s%s' % (level, ''.join(
1002 return '%s%s' % (level, ''.join(
1011 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1003 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1012 col_scheme)))
1004 col_scheme)))
1013
1005
1014 def prepare_chained_exception_message(self, cause):
1006 def prepare_chained_exception_message(self, cause):
1015 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1007 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1016 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1008 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1017
1009
1018 if cause:
1010 if cause:
1019 message = [[direct_cause]]
1011 message = [[direct_cause]]
1020 else:
1012 else:
1021 message = [[exception_during_handling]]
1013 message = [[exception_during_handling]]
1022 return message
1014 return message
1023
1015
1024 def prepare_header(self, etype, long_version=False):
1016 def prepare_header(self, etype, long_version=False):
1025 colors = self.Colors # just a shorthand + quicker name lookup
1017 colors = self.Colors # just a shorthand + quicker name lookup
1026 colorsnormal = colors.Normal # used a lot
1018 colorsnormal = colors.Normal # used a lot
1027 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1019 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1028 if long_version:
1020 if long_version:
1029 # Header with the exception type, python version, and date
1021 # Header with the exception type, python version, and date
1030 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1022 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1031 date = time.ctime(time.time())
1023 date = time.ctime(time.time())
1032
1024
1033 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * 75, colorsnormal,
1025 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * 75, colorsnormal,
1034 exc, ' ' * (75 - len(str(etype)) - len(pyver)),
1026 exc, ' ' * (75 - len(str(etype)) - len(pyver)),
1035 pyver, date.rjust(75) )
1027 pyver, date.rjust(75) )
1036 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1028 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1037 "\ncalls leading up to the error, with the most recent (innermost) call last."
1029 "\ncalls leading up to the error, with the most recent (innermost) call last."
1038 else:
1030 else:
1039 # Simplified header
1031 # Simplified header
1040 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1032 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1041 rjust(75 - len(str(etype))) )
1033 rjust(75 - len(str(etype))) )
1042
1034
1043 return head
1035 return head
1044
1036
1045 def format_exception(self, etype, evalue):
1037 def format_exception(self, etype, evalue):
1046 colors = self.Colors # just a shorthand + quicker name lookup
1038 colors = self.Colors # just a shorthand + quicker name lookup
1047 colorsnormal = colors.Normal # used a lot
1039 colorsnormal = colors.Normal # used a lot
1048 indent = ' ' * INDENT_SIZE
1040 indent = ' ' * INDENT_SIZE
1049 # Get (safely) a string form of the exception info
1041 # Get (safely) a string form of the exception info
1050 try:
1042 try:
1051 etype_str, evalue_str = map(str, (etype, evalue))
1043 etype_str, evalue_str = map(str, (etype, evalue))
1052 except:
1044 except:
1053 # User exception is improperly defined.
1045 # User exception is improperly defined.
1054 etype, evalue = str, sys.exc_info()[:2]
1046 etype, evalue = str, sys.exc_info()[:2]
1055 etype_str, evalue_str = map(str, (etype, evalue))
1047 etype_str, evalue_str = map(str, (etype, evalue))
1056 # ... and format it
1048 # ... and format it
1057 exception = ['%s%s%s: %s' % (colors.excName, etype_str,
1049 exception = ['%s%s%s: %s' % (colors.excName, etype_str,
1058 colorsnormal, py3compat.cast_unicode(evalue_str))]
1050 colorsnormal, py3compat.cast_unicode(evalue_str))]
1059
1051
1060 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
1052 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
1061 try:
1053 try:
1062 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
1054 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
1063 except:
1055 except:
1064 # Every now and then, an object with funny internals blows up
1056 # Every now and then, an object with funny internals blows up
1065 # when dir() is called on it. We do the best we can to report
1057 # when dir() is called on it. We do the best we can to report
1066 # the problem and continue
1058 # the problem and continue
1067 _m = '%sException reporting error (object with broken dir())%s:'
1059 _m = '%sException reporting error (object with broken dir())%s:'
1068 exception.append(_m % (colors.excName, colorsnormal))
1060 exception.append(_m % (colors.excName, colorsnormal))
1069 etype_str, evalue_str = map(str, sys.exc_info()[:2])
1061 etype_str, evalue_str = map(str, sys.exc_info()[:2])
1070 exception.append('%s%s%s: %s' % (colors.excName, etype_str,
1062 exception.append('%s%s%s: %s' % (colors.excName, etype_str,
1071 colorsnormal, py3compat.cast_unicode(evalue_str)))
1063 colorsnormal, py3compat.cast_unicode(evalue_str)))
1072 names = []
1064 names = []
1073 for name in names:
1065 for name in names:
1074 value = text_repr(getattr(evalue, name))
1066 value = text_repr(getattr(evalue, name))
1075 exception.append('\n%s%s = %s' % (indent, name, value))
1067 exception.append('\n%s%s = %s' % (indent, name, value))
1076
1068
1077 return exception
1069 return exception
1078
1070
1079 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1071 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1080 """Formats the header, traceback and exception message for a single exception.
1072 """Formats the header, traceback and exception message for a single exception.
1081
1073
1082 This may be called multiple times by Python 3 exception chaining
1074 This may be called multiple times by Python 3 exception chaining
1083 (PEP 3134).
1075 (PEP 3134).
1084 """
1076 """
1085 # some locals
1077 # some locals
1086 orig_etype = etype
1078 orig_etype = etype
1087 try:
1079 try:
1088 etype = etype.__name__
1080 etype = etype.__name__
1089 except AttributeError:
1081 except AttributeError:
1090 pass
1082 pass
1091
1083
1092 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1084 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1093 head = self.prepare_header(etype, self.long_header)
1085 head = self.prepare_header(etype, self.long_header)
1094 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1086 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1095
1087
1096 if records is None:
1088 if records is None:
1097 return ""
1089 return ""
1098
1090
1099 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1091 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1100
1092
1101 frames = self.format_records(records, last_unique, recursion_repeat)
1093 frames = self.format_records(records, last_unique, recursion_repeat)
1102
1094
1103 formatted_exception = self.format_exception(etype, evalue)
1095 formatted_exception = self.format_exception(etype, evalue)
1104 if records:
1096 if records:
1105 filepath, lnum = records[-1][1:3]
1097 filepath, lnum = records[-1][1:3]
1106 filepath = os.path.abspath(filepath)
1098 filepath = os.path.abspath(filepath)
1107 ipinst = get_ipython()
1099 ipinst = get_ipython()
1108 if ipinst is not None:
1100 if ipinst is not None:
1109 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1101 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1110
1102
1111 return [[head] + frames + [''.join(formatted_exception[0])]]
1103 return [[head] + frames + [''.join(formatted_exception[0])]]
1112
1104
1113 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1105 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1114 try:
1106 try:
1115 # Try the default getinnerframes and Alex's: Alex's fixes some
1107 # Try the default getinnerframes and Alex's: Alex's fixes some
1116 # problems, but it generates empty tracebacks for console errors
1108 # problems, but it generates empty tracebacks for console errors
1117 # (5 blanks lines) where none should be returned.
1109 # (5 blanks lines) where none should be returned.
1118 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1110 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1119 except:
1111 except:
1120 # FIXME: I've been getting many crash reports from python 2.3
1112 # FIXME: I've been getting many crash reports from python 2.3
1121 # users, traceable to inspect.py. If I can find a small test-case
1113 # users, traceable to inspect.py. If I can find a small test-case
1122 # to reproduce this, I should either write a better workaround or
1114 # to reproduce this, I should either write a better workaround or
1123 # file a bug report against inspect (if that's the real problem).
1115 # file a bug report against inspect (if that's the real problem).
1124 # So far, I haven't been able to find an isolated example to
1116 # So far, I haven't been able to find an isolated example to
1125 # reproduce the problem.
1117 # reproduce the problem.
1126 inspect_error()
1118 inspect_error()
1127 traceback.print_exc(file=self.ostream)
1119 traceback.print_exc(file=self.ostream)
1128 info('\nUnfortunately, your original traceback can not be constructed.\n')
1120 info('\nUnfortunately, your original traceback can not be constructed.\n')
1129 return None
1121 return None
1130
1122
1131 def get_parts_of_chained_exception(self, evalue):
1123 def get_parts_of_chained_exception(self, evalue):
1132 def get_chained_exception(exception_value):
1124 def get_chained_exception(exception_value):
1133 cause = getattr(exception_value, '__cause__', None)
1125 cause = getattr(exception_value, '__cause__', None)
1134 if cause:
1126 if cause:
1135 return cause
1127 return cause
1136 if getattr(exception_value, '__suppress_context__', False):
1128 if getattr(exception_value, '__suppress_context__', False):
1137 return None
1129 return None
1138 return getattr(exception_value, '__context__', None)
1130 return getattr(exception_value, '__context__', None)
1139
1131
1140 chained_evalue = get_chained_exception(evalue)
1132 chained_evalue = get_chained_exception(evalue)
1141
1133
1142 if chained_evalue:
1134 if chained_evalue:
1143 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1135 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1144
1136
1145 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1137 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1146 number_of_lines_of_context=5):
1138 number_of_lines_of_context=5):
1147 """Return a nice text document describing the traceback."""
1139 """Return a nice text document describing the traceback."""
1148
1140
1149 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1141 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1150 tb_offset)
1142 tb_offset)
1151
1143
1152 colors = self.Colors # just a shorthand + quicker name lookup
1144 colors = self.Colors # just a shorthand + quicker name lookup
1153 colorsnormal = colors.Normal # used a lot
1145 colorsnormal = colors.Normal # used a lot
1154 head = '%s%s%s' % (colors.topline, '-' * 75, colorsnormal)
1146 head = '%s%s%s' % (colors.topline, '-' * 75, colorsnormal)
1155 structured_traceback_parts = [head]
1147 structured_traceback_parts = [head]
1156 if py3compat.PY3:
1148 if py3compat.PY3:
1157 chained_exceptions_tb_offset = 0
1149 chained_exceptions_tb_offset = 0
1158 lines_of_context = 3
1150 lines_of_context = 3
1159 formatted_exceptions = formatted_exception
1151 formatted_exceptions = formatted_exception
1160 exception = self.get_parts_of_chained_exception(evalue)
1152 exception = self.get_parts_of_chained_exception(evalue)
1161 if exception:
1153 if exception:
1162 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1154 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1163 etype, evalue, etb = exception
1155 etype, evalue, etb = exception
1164 else:
1156 else:
1165 evalue = None
1157 evalue = None
1166 chained_exc_ids = set()
1158 chained_exc_ids = set()
1167 while evalue:
1159 while evalue:
1168 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1160 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1169 chained_exceptions_tb_offset)
1161 chained_exceptions_tb_offset)
1170 exception = self.get_parts_of_chained_exception(evalue)
1162 exception = self.get_parts_of_chained_exception(evalue)
1171
1163
1172 if exception and not id(exception[1]) in chained_exc_ids:
1164 if exception and not id(exception[1]) in chained_exc_ids:
1173 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1165 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1174 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1166 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1175 etype, evalue, etb = exception
1167 etype, evalue, etb = exception
1176 else:
1168 else:
1177 evalue = None
1169 evalue = None
1178
1170
1179 # we want to see exceptions in a reversed order:
1171 # we want to see exceptions in a reversed order:
1180 # the first exception should be on top
1172 # the first exception should be on top
1181 for formatted_exception in reversed(formatted_exceptions):
1173 for formatted_exception in reversed(formatted_exceptions):
1182 structured_traceback_parts += formatted_exception
1174 structured_traceback_parts += formatted_exception
1183 else:
1175 else:
1184 structured_traceback_parts += formatted_exception[0]
1176 structured_traceback_parts += formatted_exception[0]
1185
1177
1186 return structured_traceback_parts
1178 return structured_traceback_parts
1187
1179
1188 def debugger(self, force=False):
1180 def debugger(self, force=False):
1189 """Call up the pdb debugger if desired, always clean up the tb
1181 """Call up the pdb debugger if desired, always clean up the tb
1190 reference.
1182 reference.
1191
1183
1192 Keywords:
1184 Keywords:
1193
1185
1194 - force(False): by default, this routine checks the instance call_pdb
1186 - force(False): by default, this routine checks the instance call_pdb
1195 flag and does not actually invoke the debugger if the flag is false.
1187 flag and does not actually invoke the debugger if the flag is false.
1196 The 'force' option forces the debugger to activate even if the flag
1188 The 'force' option forces the debugger to activate even if the flag
1197 is false.
1189 is false.
1198
1190
1199 If the call_pdb flag is set, the pdb interactive debugger is
1191 If the call_pdb flag is set, the pdb interactive debugger is
1200 invoked. In all cases, the self.tb reference to the current traceback
1192 invoked. In all cases, the self.tb reference to the current traceback
1201 is deleted to prevent lingering references which hamper memory
1193 is deleted to prevent lingering references which hamper memory
1202 management.
1194 management.
1203
1195
1204 Note that each call to pdb() does an 'import readline', so if your app
1196 Note that each call to pdb() does an 'import readline', so if your app
1205 requires a special setup for the readline completers, you'll have to
1197 requires a special setup for the readline completers, you'll have to
1206 fix that by hand after invoking the exception handler."""
1198 fix that by hand after invoking the exception handler."""
1207
1199
1208 if force or self.call_pdb:
1200 if force or self.call_pdb:
1209 if self.pdb is None:
1201 if self.pdb is None:
1210 self.pdb = debugger.Pdb(
1202 self.pdb = debugger.Pdb(
1211 self.color_scheme_table.active_scheme_name)
1203 self.color_scheme_table.active_scheme_name)
1212 # the system displayhook may have changed, restore the original
1204 # the system displayhook may have changed, restore the original
1213 # for pdb
1205 # for pdb
1214 display_trap = DisplayTrap(hook=sys.__displayhook__)
1206 display_trap = DisplayTrap(hook=sys.__displayhook__)
1215 with display_trap:
1207 with display_trap:
1216 self.pdb.reset()
1208 self.pdb.reset()
1217 # Find the right frame so we don't pop up inside ipython itself
1209 # Find the right frame so we don't pop up inside ipython itself
1218 if hasattr(self, 'tb') and self.tb is not None:
1210 if hasattr(self, 'tb') and self.tb is not None:
1219 etb = self.tb
1211 etb = self.tb
1220 else:
1212 else:
1221 etb = self.tb = sys.last_traceback
1213 etb = self.tb = sys.last_traceback
1222 while self.tb is not None and self.tb.tb_next is not None:
1214 while self.tb is not None and self.tb.tb_next is not None:
1223 self.tb = self.tb.tb_next
1215 self.tb = self.tb.tb_next
1224 if etb and etb.tb_next:
1216 if etb and etb.tb_next:
1225 etb = etb.tb_next
1217 etb = etb.tb_next
1226 self.pdb.botframe = etb.tb_frame
1218 self.pdb.botframe = etb.tb_frame
1227 self.pdb.interaction(self.tb.tb_frame, self.tb)
1219 self.pdb.interaction(self.tb.tb_frame, self.tb)
1228
1220
1229 if hasattr(self, 'tb'):
1221 if hasattr(self, 'tb'):
1230 del self.tb
1222 del self.tb
1231
1223
1232 def handler(self, info=None):
1224 def handler(self, info=None):
1233 (etype, evalue, etb) = info or sys.exc_info()
1225 (etype, evalue, etb) = info or sys.exc_info()
1234 self.tb = etb
1226 self.tb = etb
1235 ostream = self.ostream
1227 ostream = self.ostream
1236 ostream.flush()
1228 ostream.flush()
1237 ostream.write(self.text(etype, evalue, etb))
1229 ostream.write(self.text(etype, evalue, etb))
1238 ostream.write('\n')
1230 ostream.write('\n')
1239 ostream.flush()
1231 ostream.flush()
1240
1232
1241 # Changed so an instance can just be called as VerboseTB_inst() and print
1233 # Changed so an instance can just be called as VerboseTB_inst() and print
1242 # out the right info on its own.
1234 # out the right info on its own.
1243 def __call__(self, etype=None, evalue=None, etb=None):
1235 def __call__(self, etype=None, evalue=None, etb=None):
1244 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1236 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1245 if etb is None:
1237 if etb is None:
1246 self.handler()
1238 self.handler()
1247 else:
1239 else:
1248 self.handler((etype, evalue, etb))
1240 self.handler((etype, evalue, etb))
1249 try:
1241 try:
1250 self.debugger()
1242 self.debugger()
1251 except KeyboardInterrupt:
1243 except KeyboardInterrupt:
1252 print("\nKeyboardInterrupt")
1244 print("\nKeyboardInterrupt")
1253
1245
1254
1246
1255 #----------------------------------------------------------------------------
1247 #----------------------------------------------------------------------------
1256 class FormattedTB(VerboseTB, ListTB):
1248 class FormattedTB(VerboseTB, ListTB):
1257 """Subclass ListTB but allow calling with a traceback.
1249 """Subclass ListTB but allow calling with a traceback.
1258
1250
1259 It can thus be used as a sys.excepthook for Python > 2.1.
1251 It can thus be used as a sys.excepthook for Python > 2.1.
1260
1252
1261 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1253 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1262
1254
1263 Allows a tb_offset to be specified. This is useful for situations where
1255 Allows a tb_offset to be specified. This is useful for situations where
1264 one needs to remove a number of topmost frames from the traceback (such as
1256 one needs to remove a number of topmost frames from the traceback (such as
1265 occurs with python programs that themselves execute other python code,
1257 occurs with python programs that themselves execute other python code,
1266 like Python shells). """
1258 like Python shells). """
1267
1259
1268 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1260 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1269 ostream=None,
1261 ostream=None,
1270 tb_offset=0, long_header=False, include_vars=False,
1262 tb_offset=0, long_header=False, include_vars=False,
1271 check_cache=None):
1263 check_cache=None):
1272
1264
1273 # NEVER change the order of this list. Put new modes at the end:
1265 # NEVER change the order of this list. Put new modes at the end:
1274 self.valid_modes = ['Plain', 'Context', 'Verbose']
1266 self.valid_modes = ['Plain', 'Context', 'Verbose']
1275 self.verbose_modes = self.valid_modes[1:3]
1267 self.verbose_modes = self.valid_modes[1:3]
1276
1268
1277 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1269 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1278 ostream=ostream, tb_offset=tb_offset,
1270 ostream=ostream, tb_offset=tb_offset,
1279 long_header=long_header, include_vars=include_vars,
1271 long_header=long_header, include_vars=include_vars,
1280 check_cache=check_cache)
1272 check_cache=check_cache)
1281
1273
1282 # Different types of tracebacks are joined with different separators to
1274 # Different types of tracebacks are joined with different separators to
1283 # form a single string. They are taken from this dict
1275 # form a single string. They are taken from this dict
1284 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1276 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1285 # set_mode also sets the tb_join_char attribute
1277 # set_mode also sets the tb_join_char attribute
1286 self.set_mode(mode)
1278 self.set_mode(mode)
1287
1279
1288 def _extract_tb(self, tb):
1280 def _extract_tb(self, tb):
1289 if tb:
1281 if tb:
1290 return traceback.extract_tb(tb)
1282 return traceback.extract_tb(tb)
1291 else:
1283 else:
1292 return None
1284 return None
1293
1285
1294 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1286 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1295 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1287 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1296 mode = self.mode
1288 mode = self.mode
1297 if mode in self.verbose_modes:
1289 if mode in self.verbose_modes:
1298 # Verbose modes need a full traceback
1290 # Verbose modes need a full traceback
1299 return VerboseTB.structured_traceback(
1291 return VerboseTB.structured_traceback(
1300 self, etype, value, tb, tb_offset, number_of_lines_of_context
1292 self, etype, value, tb, tb_offset, number_of_lines_of_context
1301 )
1293 )
1302 else:
1294 else:
1303 # We must check the source cache because otherwise we can print
1295 # We must check the source cache because otherwise we can print
1304 # out-of-date source code.
1296 # out-of-date source code.
1305 self.check_cache()
1297 self.check_cache()
1306 # Now we can extract and format the exception
1298 # Now we can extract and format the exception
1307 elist = self._extract_tb(tb)
1299 elist = self._extract_tb(tb)
1308 return ListTB.structured_traceback(
1300 return ListTB.structured_traceback(
1309 self, etype, value, elist, tb_offset, number_of_lines_of_context
1301 self, etype, value, elist, tb_offset, number_of_lines_of_context
1310 )
1302 )
1311
1303
1312 def stb2text(self, stb):
1304 def stb2text(self, stb):
1313 """Convert a structured traceback (a list) to a string."""
1305 """Convert a structured traceback (a list) to a string."""
1314 return self.tb_join_char.join(stb)
1306 return self.tb_join_char.join(stb)
1315
1307
1316
1308
1317 def set_mode(self, mode=None):
1309 def set_mode(self, mode=None):
1318 """Switch to the desired mode.
1310 """Switch to the desired mode.
1319
1311
1320 If mode is not specified, cycles through the available modes."""
1312 If mode is not specified, cycles through the available modes."""
1321
1313
1322 if not mode:
1314 if not mode:
1323 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1315 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1324 len(self.valid_modes)
1316 len(self.valid_modes)
1325 self.mode = self.valid_modes[new_idx]
1317 self.mode = self.valid_modes[new_idx]
1326 elif mode not in self.valid_modes:
1318 elif mode not in self.valid_modes:
1327 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1319 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1328 'Valid modes: ' + str(self.valid_modes))
1320 'Valid modes: ' + str(self.valid_modes))
1329 else:
1321 else:
1330 self.mode = mode
1322 self.mode = mode
1331 # include variable details only in 'Verbose' mode
1323 # include variable details only in 'Verbose' mode
1332 self.include_vars = (self.mode == self.valid_modes[2])
1324 self.include_vars = (self.mode == self.valid_modes[2])
1333 # Set the join character for generating text tracebacks
1325 # Set the join character for generating text tracebacks
1334 self.tb_join_char = self._join_chars[self.mode]
1326 self.tb_join_char = self._join_chars[self.mode]
1335
1327
1336 # some convenient shortcuts
1328 # some convenient shortcuts
1337 def plain(self):
1329 def plain(self):
1338 self.set_mode(self.valid_modes[0])
1330 self.set_mode(self.valid_modes[0])
1339
1331
1340 def context(self):
1332 def context(self):
1341 self.set_mode(self.valid_modes[1])
1333 self.set_mode(self.valid_modes[1])
1342
1334
1343 def verbose(self):
1335 def verbose(self):
1344 self.set_mode(self.valid_modes[2])
1336 self.set_mode(self.valid_modes[2])
1345
1337
1346
1338
1347 #----------------------------------------------------------------------------
1339 #----------------------------------------------------------------------------
1348 class AutoFormattedTB(FormattedTB):
1340 class AutoFormattedTB(FormattedTB):
1349 """A traceback printer which can be called on the fly.
1341 """A traceback printer which can be called on the fly.
1350
1342
1351 It will find out about exceptions by itself.
1343 It will find out about exceptions by itself.
1352
1344
1353 A brief example::
1345 A brief example::
1354
1346
1355 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1347 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1356 try:
1348 try:
1357 ...
1349 ...
1358 except:
1350 except:
1359 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1351 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1360 """
1352 """
1361
1353
1362 def __call__(self, etype=None, evalue=None, etb=None,
1354 def __call__(self, etype=None, evalue=None, etb=None,
1363 out=None, tb_offset=None):
1355 out=None, tb_offset=None):
1364 """Print out a formatted exception traceback.
1356 """Print out a formatted exception traceback.
1365
1357
1366 Optional arguments:
1358 Optional arguments:
1367 - out: an open file-like object to direct output to.
1359 - out: an open file-like object to direct output to.
1368
1360
1369 - tb_offset: the number of frames to skip over in the stack, on a
1361 - tb_offset: the number of frames to skip over in the stack, on a
1370 per-call basis (this overrides temporarily the instance's tb_offset
1362 per-call basis (this overrides temporarily the instance's tb_offset
1371 given at initialization time. """
1363 given at initialization time. """
1372
1364
1373 if out is None:
1365 if out is None:
1374 out = self.ostream
1366 out = self.ostream
1375 out.flush()
1367 out.flush()
1376 out.write(self.text(etype, evalue, etb, tb_offset))
1368 out.write(self.text(etype, evalue, etb, tb_offset))
1377 out.write('\n')
1369 out.write('\n')
1378 out.flush()
1370 out.flush()
1379 # FIXME: we should remove the auto pdb behavior from here and leave
1371 # FIXME: we should remove the auto pdb behavior from here and leave
1380 # that to the clients.
1372 # that to the clients.
1381 try:
1373 try:
1382 self.debugger()
1374 self.debugger()
1383 except KeyboardInterrupt:
1375 except KeyboardInterrupt:
1384 print("\nKeyboardInterrupt")
1376 print("\nKeyboardInterrupt")
1385
1377
1386 def structured_traceback(self, etype=None, value=None, tb=None,
1378 def structured_traceback(self, etype=None, value=None, tb=None,
1387 tb_offset=None, number_of_lines_of_context=5):
1379 tb_offset=None, number_of_lines_of_context=5):
1388 if etype is None:
1380 if etype is None:
1389 etype, value, tb = sys.exc_info()
1381 etype, value, tb = sys.exc_info()
1390 self.tb = tb
1382 self.tb = tb
1391 return FormattedTB.structured_traceback(
1383 return FormattedTB.structured_traceback(
1392 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1384 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1393
1385
1394
1386
1395 #---------------------------------------------------------------------------
1387 #---------------------------------------------------------------------------
1396
1388
1397 # A simple class to preserve Nathan's original functionality.
1389 # A simple class to preserve Nathan's original functionality.
1398 class ColorTB(FormattedTB):
1390 class ColorTB(FormattedTB):
1399 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1391 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1400
1392
1401 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1393 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1402 FormattedTB.__init__(self, color_scheme=color_scheme,
1394 FormattedTB.__init__(self, color_scheme=color_scheme,
1403 call_pdb=call_pdb, **kwargs)
1395 call_pdb=call_pdb, **kwargs)
1404
1396
1405
1397
1406 class SyntaxTB(ListTB):
1398 class SyntaxTB(ListTB):
1407 """Extension which holds some state: the last exception value"""
1399 """Extension which holds some state: the last exception value"""
1408
1400
1409 def __init__(self, color_scheme='NoColor'):
1401 def __init__(self, color_scheme='NoColor'):
1410 ListTB.__init__(self, color_scheme)
1402 ListTB.__init__(self, color_scheme)
1411 self.last_syntax_error = None
1403 self.last_syntax_error = None
1412
1404
1413 def __call__(self, etype, value, elist):
1405 def __call__(self, etype, value, elist):
1414 self.last_syntax_error = value
1406 self.last_syntax_error = value
1415
1407
1416 ListTB.__call__(self, etype, value, elist)
1408 ListTB.__call__(self, etype, value, elist)
1417
1409
1418 def structured_traceback(self, etype, value, elist, tb_offset=None,
1410 def structured_traceback(self, etype, value, elist, tb_offset=None,
1419 context=5):
1411 context=5):
1420 # If the source file has been edited, the line in the syntax error can
1412 # If the source file has been edited, the line in the syntax error can
1421 # be wrong (retrieved from an outdated cache). This replaces it with
1413 # be wrong (retrieved from an outdated cache). This replaces it with
1422 # the current value.
1414 # the current value.
1423 if isinstance(value, SyntaxError) \
1415 if isinstance(value, SyntaxError) \
1424 and isinstance(value.filename, py3compat.string_types) \
1416 and isinstance(value.filename, py3compat.string_types) \
1425 and isinstance(value.lineno, int):
1417 and isinstance(value.lineno, int):
1426 linecache.checkcache(value.filename)
1418 linecache.checkcache(value.filename)
1427 newtext = ulinecache.getline(value.filename, value.lineno)
1419 newtext = ulinecache.getline(value.filename, value.lineno)
1428 if newtext:
1420 if newtext:
1429 value.text = newtext
1421 value.text = newtext
1430 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1422 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1431 tb_offset=tb_offset, context=context)
1423 tb_offset=tb_offset, context=context)
1432
1424
1433 def clear_err_state(self):
1425 def clear_err_state(self):
1434 """Return the current error state and clear it"""
1426 """Return the current error state and clear it"""
1435 e = self.last_syntax_error
1427 e = self.last_syntax_error
1436 self.last_syntax_error = None
1428 self.last_syntax_error = None
1437 return e
1429 return e
1438
1430
1439 def stb2text(self, stb):
1431 def stb2text(self, stb):
1440 """Convert a structured traceback (a list) to a string."""
1432 """Convert a structured traceback (a list) to a string."""
1441 return ''.join(stb)
1433 return ''.join(stb)
1442
1434
1443
1435
1444 # some internal-use functions
1436 # some internal-use functions
1445 def text_repr(value):
1437 def text_repr(value):
1446 """Hopefully pretty robust repr equivalent."""
1438 """Hopefully pretty robust repr equivalent."""
1447 # this is pretty horrible but should always return *something*
1439 # this is pretty horrible but should always return *something*
1448 try:
1440 try:
1449 return pydoc.text.repr(value)
1441 return pydoc.text.repr(value)
1450 except KeyboardInterrupt:
1442 except KeyboardInterrupt:
1451 raise
1443 raise
1452 except:
1444 except:
1453 try:
1445 try:
1454 return repr(value)
1446 return repr(value)
1455 except KeyboardInterrupt:
1447 except KeyboardInterrupt:
1456 raise
1448 raise
1457 except:
1449 except:
1458 try:
1450 try:
1459 # all still in an except block so we catch
1451 # all still in an except block so we catch
1460 # getattr raising
1452 # getattr raising
1461 name = getattr(value, '__name__', None)
1453 name = getattr(value, '__name__', None)
1462 if name:
1454 if name:
1463 # ick, recursion
1455 # ick, recursion
1464 return text_repr(name)
1456 return text_repr(name)
1465 klass = getattr(value, '__class__', None)
1457 klass = getattr(value, '__class__', None)
1466 if klass:
1458 if klass:
1467 return '%s instance' % text_repr(klass)
1459 return '%s instance' % text_repr(klass)
1468 except KeyboardInterrupt:
1460 except KeyboardInterrupt:
1469 raise
1461 raise
1470 except:
1462 except:
1471 return 'UNRECOVERABLE REPR FAILURE'
1463 return 'UNRECOVERABLE REPR FAILURE'
1472
1464
1473
1465
1474 def eqrepr(value, repr=text_repr):
1466 def eqrepr(value, repr=text_repr):
1475 return '=%s' % repr(value)
1467 return '=%s' % repr(value)
1476
1468
1477
1469
1478 def nullrepr(value, repr=text_repr):
1470 def nullrepr(value, repr=text_repr):
1479 return ''
1471 return ''
General Comments 0
You need to be logged in to leave comments. Login now