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