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