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