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