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