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