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