##// END OF EJS Templates
Simplified output stream management in ultratb....
Fernando Perez -
Show More
@@ -1,1201 +1,1224 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 # This attribute us used in globalipapp.py to have stdout used for
317 # writting exceptions. This is needed so nose can trap them. This attribute
318 # should be None (the default, which will use IPython.utils.io.Term) or
319 # the string 'stdout' which will cause the override to sys.stdout.
320 out_stream = None
321
322 316 # Number of frames to skip when reporting tracebacks
323 317 tb_offset = 0
324 318
325 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
319 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
326 320 # Whether to call the interactive pdb debugger after printing
327 321 # tracebacks or not
328 322 self.call_pdb = call_pdb
329 323
324 # Output stream to write to. Note that we store the original value in
325 # a private attribute and then make the public ostream a property, so
326 # that we can delay accessing io.Term.cout until runtime. The way
327 # things are written now, the Term.cout object is dynamically managed
328 # so a reference to it should NEVER be stored statically. This
329 # property approach confines this detail to a single location, and all
330 # subclasses can simply access self.ostream for writing.
331 self._ostream = ostream
332
330 333 # Create color table
331 334 self.color_scheme_table = exception_colors()
332 335
333 336 self.set_colors(color_scheme)
334 337 self.old_scheme = color_scheme # save initial value for toggles
335 338
336 339 if call_pdb:
337 340 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
338 341 else:
339 342 self.pdb = None
340 343
344 def _get_ostream(self):
345 """Output stream that exceptions are written to.
346
347 Valid values are:
348
349 - None: the default, which means that IPython will dynamically resolve
350 to io.Term.cout. This ensures compatibility with most tools, including
351 Windows (where plain stdout doesn't recognize ANSI escapes).
352
353 - Any object with 'write' and 'flush' attributes.
354 """
355 return io.Term.cout if self._ostream is None else self._ostream
356
357 def _set_ostream(self, val):
358 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
359 self._ostream = val
360
361 ostream = property(_get_ostream, _set_ostream)
362
341 363 def set_colors(self,*args,**kw):
342 364 """Shorthand access to the color table scheme selector method."""
343 365
344 366 # Set own color table
345 367 self.color_scheme_table.set_active_scheme(*args,**kw)
346 368 # for convenience, set Colors to the active scheme
347 369 self.Colors = self.color_scheme_table.active_colors
348 370 # Also set colors of debugger
349 371 if hasattr(self,'pdb') and self.pdb is not None:
350 372 self.pdb.set_colors(*args,**kw)
351 373
352 374 def color_toggle(self):
353 375 """Toggle between the currently active color scheme and NoColor."""
354 376
355 377 if self.color_scheme_table.active_scheme_name == 'NoColor':
356 378 self.color_scheme_table.set_active_scheme(self.old_scheme)
357 379 self.Colors = self.color_scheme_table.active_colors
358 380 else:
359 381 self.old_scheme = self.color_scheme_table.active_scheme_name
360 382 self.color_scheme_table.set_active_scheme('NoColor')
361 383 self.Colors = self.color_scheme_table.active_colors
362 384
363 385 def stb2text(self, stb):
364 386 """Convert a structured traceback (a list) to a string."""
365 387 return '\n'.join(stb)
366 388
367 389 def text(self, etype, value, tb, tb_offset=None, context=5):
368 390 """Return formatted traceback.
369 391
370 392 Subclasses may override this if they add extra arguments.
371 393 """
372 394 tb_list = self.structured_traceback(etype, value, tb,
373 395 tb_offset, context)
374 396 return self.stb2text(tb_list)
375 397
376 398 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
377 399 context=5, mode=None):
378 400 """Return a list of traceback frames.
379 401
380 402 Must be implemented by each class.
381 403 """
382 404 raise NotImplementedError()
383 405
384 406
385 407 #---------------------------------------------------------------------------
386 408 class ListTB(TBTools):
387 409 """Print traceback information from a traceback list, with optional color.
388 410
389 411 Calling: requires 3 arguments:
390 412 (etype, evalue, elist)
391 413 as would be obtained by:
392 414 etype, evalue, tb = sys.exc_info()
393 415 if tb:
394 416 elist = traceback.extract_tb(tb)
395 417 else:
396 418 elist = None
397 419
398 420 It can thus be used by programs which need to process the traceback before
399 421 printing (such as console replacements based on the code module from the
400 422 standard library).
401 423
402 424 Because they are meant to be called without a full traceback (only a
403 425 list), instances of this class can't call the interactive pdb debugger."""
404 426
405 def __init__(self,color_scheme = 'NoColor'):
406 TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
427 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
428 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
429 ostream=ostream)
407 430
408 431 def __call__(self, etype, value, elist):
409 432 io.Term.cout.flush()
410 433 io.Term.cerr.write(self.text(etype, value, elist))
411 434 io.Term.cerr.write('\n')
412 435
413 436 def structured_traceback(self, etype, value, elist, tb_offset=None,
414 437 context=5):
415 438 """Return a color formatted string with the traceback info.
416 439
417 440 Parameters
418 441 ----------
419 442 etype : exception type
420 443 Type of the exception raised.
421 444
422 445 value : object
423 446 Data stored in the exception
424 447
425 448 elist : list
426 449 List of frames, see class docstring for details.
427 450
428 451 tb_offset : int, optional
429 452 Number of frames in the traceback to skip. If not given, the
430 453 instance value is used (set in constructor).
431 454
432 455 context : int, optional
433 456 Number of lines of context information to print.
434 457
435 458 Returns
436 459 -------
437 460 String with formatted exception.
438 461 """
439 462 tb_offset = self.tb_offset if tb_offset is None else tb_offset
440 463 Colors = self.Colors
441 464 out_list = []
442 465 if elist:
443 466
444 467 if tb_offset and len(elist) > tb_offset:
445 468 elist = elist[tb_offset:]
446 469
447 470 out_list.append('Traceback %s(most recent call last)%s:' %
448 471 (Colors.normalEm, Colors.Normal) + '\n')
449 472 out_list.extend(self._format_list(elist))
450 473 # The exception info should be a single entry in the list.
451 474 lines = ''.join(self._format_exception_only(etype, value))
452 475 out_list.append(lines)
453 476
454 477 # Note: this code originally read:
455 478
456 479 ## for line in lines[:-1]:
457 480 ## out_list.append(" "+line)
458 481 ## out_list.append(lines[-1])
459 482
460 483 # This means it was indenting everything but the last line by a little
461 484 # bit. I've disabled this for now, but if we see ugliness somewhre we
462 485 # can restore it.
463 486
464 487 return out_list
465 488
466 489 def _format_list(self, extracted_list):
467 490 """Format a list of traceback entry tuples for printing.
468 491
469 492 Given a list of tuples as returned by extract_tb() or
470 493 extract_stack(), return a list of strings ready for printing.
471 494 Each string in the resulting list corresponds to the item with the
472 495 same index in the argument list. Each string ends in a newline;
473 496 the strings may contain internal newlines as well, for those items
474 497 whose source text line is not None.
475 498
476 499 Lifted almost verbatim from traceback.py
477 500 """
478 501
479 502 Colors = self.Colors
480 503 list = []
481 504 for filename, lineno, name, line in extracted_list[:-1]:
482 505 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
483 506 (Colors.filename, filename, Colors.Normal,
484 507 Colors.lineno, lineno, Colors.Normal,
485 508 Colors.name, name, Colors.Normal)
486 509 if line:
487 510 item = item + ' %s\n' % line.strip()
488 511 list.append(item)
489 512 # Emphasize the last entry
490 513 filename, lineno, name, line = extracted_list[-1]
491 514 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
492 515 (Colors.normalEm,
493 516 Colors.filenameEm, filename, Colors.normalEm,
494 517 Colors.linenoEm, lineno, Colors.normalEm,
495 518 Colors.nameEm, name, Colors.normalEm,
496 519 Colors.Normal)
497 520 if line:
498 521 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
499 522 Colors.Normal)
500 523 list.append(item)
501 524 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
502 525 return list
503 526
504 527 def _format_exception_only(self, etype, value):
505 528 """Format the exception part of a traceback.
506 529
507 530 The arguments are the exception type and value such as given by
508 531 sys.exc_info()[:2]. The return value is a list of strings, each ending
509 532 in a newline. Normally, the list contains a single string; however,
510 533 for SyntaxError exceptions, it contains several lines that (when
511 534 printed) display detailed information about where the syntax error
512 535 occurred. The message indicating which exception occurred is the
513 536 always last string in the list.
514 537
515 538 Also lifted nearly verbatim from traceback.py
516 539 """
517 540
518 541 have_filedata = False
519 542 Colors = self.Colors
520 543 list = []
521 544 try:
522 545 stype = Colors.excName + etype.__name__ + Colors.Normal
523 546 except AttributeError:
524 547 stype = etype # String exceptions don't get special coloring
525 548 if value is None:
526 549 list.append( str(stype) + '\n')
527 550 else:
528 551 if etype is SyntaxError:
529 552 try:
530 553 msg, (filename, lineno, offset, line) = value
531 554 except:
532 555 have_filedata = False
533 556 else:
534 557 have_filedata = True
535 558 #print 'filename is',filename # dbg
536 559 if not filename: filename = "<string>"
537 560 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
538 561 (Colors.normalEm,
539 562 Colors.filenameEm, filename, Colors.normalEm,
540 563 Colors.linenoEm, lineno, Colors.Normal ))
541 564 if line is not None:
542 565 i = 0
543 566 while i < len(line) and line[i].isspace():
544 567 i = i+1
545 568 list.append('%s %s%s\n' % (Colors.line,
546 569 line.strip(),
547 570 Colors.Normal))
548 571 if offset is not None:
549 572 s = ' '
550 573 for c in line[i:offset-1]:
551 574 if c.isspace():
552 575 s = s + c
553 576 else:
554 577 s = s + ' '
555 578 list.append('%s%s^%s\n' % (Colors.caret, s,
556 579 Colors.Normal) )
557 580 value = msg
558 581 s = self._some_str(value)
559 582 if s:
560 583 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
561 584 Colors.Normal, s))
562 585 else:
563 586 list.append('%s\n' % str(stype))
564 587
565 588 # sync with user hooks
566 589 if have_filedata:
567 590 ipinst = ipapi.get()
568 591 if ipinst is not None:
569 592 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
570 593
571 594 return list
572 595
573 596 def get_exception_only(self, etype, value):
574 597 """Only print the exception type and message, without a traceback.
575 598
576 599 Parameters
577 600 ----------
578 601 etype : exception type
579 602 value : exception value
580 603 """
581 604 return ListTB.structured_traceback(self, etype, value, [])
582 605
583 606
584 607 def show_exception_only(self, etype, value):
585 608 """Only print the exception type and message, without a traceback.
586 609
587 610 Parameters
588 611 ----------
589 612 etype : exception type
590 613 value : exception value
591 614 """
592 615 # This method needs to use __call__ from *this* class, not the one from
593 616 # a subclass whose signature or behavior may be different
594 if self.out_stream == 'stdout':
595 ostream = sys.stdout
596 else:
597 ostream = io.Term.cerr
617 ostream = self.ostream
598 618 ostream.flush()
599 619 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
600 620 ostream.flush()
601 621
602 622 def _some_str(self, value):
603 623 # Lifted from traceback.py
604 624 try:
605 625 return str(value)
606 626 except:
607 627 return '<unprintable %s object>' % type(value).__name__
608 628
609 629 #----------------------------------------------------------------------------
610 630 class VerboseTB(TBTools):
611 631 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
612 632 of HTML. Requires inspect and pydoc. Crazy, man.
613 633
614 634 Modified version which optionally strips the topmost entries from the
615 635 traceback, to be used with alternate interpreters (because their own code
616 636 would appear in the traceback)."""
617 637
618 def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0,
619 call_pdb = 0, include_vars=1):
638 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
639 tb_offset=0, long_header=False, include_vars=True):
620 640 """Specify traceback offset, headers and color scheme.
621 641
622 642 Define how many frames to drop from the tracebacks. Calling it with
623 643 tb_offset=1 allows use of this handler in interpreters which will have
624 644 their own code at the top of the traceback (VerboseTB will first
625 645 remove that frame before printing the traceback info)."""
626 TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb)
646 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
647 ostream=ostream)
627 648 self.tb_offset = tb_offset
628 649 self.long_header = long_header
629 650 self.include_vars = include_vars
630 651
631 652 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
632 653 context=5):
633 654 """Return a nice text document describing the traceback."""
634 655
635 656 tb_offset = self.tb_offset if tb_offset is None else tb_offset
636 657
637 658 # some locals
638 659 try:
639 660 etype = etype.__name__
640 661 except AttributeError:
641 662 pass
642 663 Colors = self.Colors # just a shorthand + quicker name lookup
643 664 ColorsNormal = Colors.Normal # used a lot
644 665 col_scheme = self.color_scheme_table.active_scheme_name
645 666 indent = ' '*INDENT_SIZE
646 667 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
647 668 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
648 669 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
649 670
650 671 # some internal-use functions
651 672 def text_repr(value):
652 673 """Hopefully pretty robust repr equivalent."""
653 674 # this is pretty horrible but should always return *something*
654 675 try:
655 676 return pydoc.text.repr(value)
656 677 except KeyboardInterrupt:
657 678 raise
658 679 except:
659 680 try:
660 681 return repr(value)
661 682 except KeyboardInterrupt:
662 683 raise
663 684 except:
664 685 try:
665 686 # all still in an except block so we catch
666 687 # getattr raising
667 688 name = getattr(value, '__name__', None)
668 689 if name:
669 690 # ick, recursion
670 691 return text_repr(name)
671 692 klass = getattr(value, '__class__', None)
672 693 if klass:
673 694 return '%s instance' % text_repr(klass)
674 695 except KeyboardInterrupt:
675 696 raise
676 697 except:
677 698 return 'UNRECOVERABLE REPR FAILURE'
678 699 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
679 700 def nullrepr(value, repr=text_repr): return ''
680 701
681 702 # meat of the code begins
682 703 try:
683 704 etype = etype.__name__
684 705 except AttributeError:
685 706 pass
686 707
687 708 if self.long_header:
688 709 # Header with the exception type, python version, and date
689 710 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
690 711 date = time.ctime(time.time())
691 712
692 713 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
693 714 exc, ' '*(75-len(str(etype))-len(pyver)),
694 715 pyver, string.rjust(date, 75) )
695 716 head += "\nA problem occured executing Python code. Here is the sequence of function"\
696 717 "\ncalls leading up to the error, with the most recent (innermost) call last."
697 718 else:
698 719 # Simplified header
699 720 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
700 721 string.rjust('Traceback (most recent call last)',
701 722 75 - len(str(etype)) ) )
702 723 frames = []
703 724 # Flush cache before calling inspect. This helps alleviate some of the
704 725 # problems with python 2.3's inspect.py.
705 726 linecache.checkcache()
706 727 # Drop topmost frames if requested
707 728 try:
708 729 # Try the default getinnerframes and Alex's: Alex's fixes some
709 730 # problems, but it generates empty tracebacks for console errors
710 731 # (5 blanks lines) where none should be returned.
711 732 #records = inspect.getinnerframes(etb, context)[tb_offset:]
712 733 #print 'python records:', records # dbg
713 734 records = _fixed_getinnerframes(etb, context, tb_offset)
714 735 #print 'alex records:', records # dbg
715 736 except:
716 737
717 738 # FIXME: I've been getting many crash reports from python 2.3
718 739 # users, traceable to inspect.py. If I can find a small test-case
719 740 # to reproduce this, I should either write a better workaround or
720 741 # file a bug report against inspect (if that's the real problem).
721 742 # So far, I haven't been able to find an isolated example to
722 743 # reproduce the problem.
723 744 inspect_error()
724 745 traceback.print_exc(file=io.Term.cerr)
725 746 info('\nUnfortunately, your original traceback can not be constructed.\n')
726 747 return ''
727 748
728 749 # build some color string templates outside these nested loops
729 750 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
730 751 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
731 752 ColorsNormal)
732 753 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
733 754 (Colors.vName, Colors.valEm, ColorsNormal)
734 755 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
735 756 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
736 757 Colors.vName, ColorsNormal)
737 758 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
738 759 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
739 760 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
740 761 ColorsNormal)
741 762
742 763 # now, loop over all records printing context and info
743 764 abspath = os.path.abspath
744 765 for frame, file, lnum, func, lines, index in records:
745 766 #print '*** record:',file,lnum,func,lines,index # dbg
746 767 try:
747 768 file = file and abspath(file) or '?'
748 769 except OSError:
749 770 # if file is '<console>' or something not in the filesystem,
750 771 # the abspath call will throw an OSError. Just ignore it and
751 772 # keep the original file string.
752 773 pass
753 774 link = tpl_link % file
754 775 try:
755 776 args, varargs, varkw, locals = inspect.getargvalues(frame)
756 777 except:
757 778 # This can happen due to a bug in python2.3. We should be
758 779 # able to remove this try/except when 2.4 becomes a
759 780 # requirement. Bug details at http://python.org/sf/1005466
760 781 inspect_error()
761 782 traceback.print_exc(file=io.Term.cerr)
762 783 info("\nIPython's exception reporting continues...\n")
763 784
764 785 if func == '?':
765 786 call = ''
766 787 else:
767 788 # Decide whether to include variable details or not
768 789 var_repr = self.include_vars and eqrepr or nullrepr
769 790 try:
770 791 call = tpl_call % (func,inspect.formatargvalues(args,
771 792 varargs, varkw,
772 793 locals,formatvalue=var_repr))
773 794 except KeyError:
774 795 # Very odd crash from inspect.formatargvalues(). The
775 796 # scenario under which it appeared was a call to
776 797 # view(array,scale) in NumTut.view.view(), where scale had
777 798 # been defined as a scalar (it should be a tuple). Somehow
778 799 # inspect messes up resolving the argument list of view()
779 800 # and barfs out. At some point I should dig into this one
780 801 # and file a bug report about it.
781 802 inspect_error()
782 803 traceback.print_exc(file=io.Term.cerr)
783 804 info("\nIPython's exception reporting continues...\n")
784 805 call = tpl_call_fail % func
785 806
786 807 # Initialize a list of names on the current line, which the
787 808 # tokenizer below will populate.
788 809 names = []
789 810
790 811 def tokeneater(token_type, token, start, end, line):
791 812 """Stateful tokeneater which builds dotted names.
792 813
793 814 The list of names it appends to (from the enclosing scope) can
794 815 contain repeated composite names. This is unavoidable, since
795 816 there is no way to disambguate partial dotted structures until
796 817 the full list is known. The caller is responsible for pruning
797 818 the final list of duplicates before using it."""
798 819
799 820 # build composite names
800 821 if token == '.':
801 822 try:
802 823 names[-1] += '.'
803 824 # store state so the next token is added for x.y.z names
804 825 tokeneater.name_cont = True
805 826 return
806 827 except IndexError:
807 828 pass
808 829 if token_type == tokenize.NAME and token not in keyword.kwlist:
809 830 if tokeneater.name_cont:
810 831 # Dotted names
811 832 names[-1] += token
812 833 tokeneater.name_cont = False
813 834 else:
814 835 # Regular new names. We append everything, the caller
815 836 # will be responsible for pruning the list later. It's
816 837 # very tricky to try to prune as we go, b/c composite
817 838 # names can fool us. The pruning at the end is easy
818 839 # to do (or the caller can print a list with repeated
819 840 # names if so desired.
820 841 names.append(token)
821 842 elif token_type == tokenize.NEWLINE:
822 843 raise IndexError
823 844 # we need to store a bit of state in the tokenizer to build
824 845 # dotted names
825 846 tokeneater.name_cont = False
826 847
827 848 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
828 849 line = getline(file, lnum[0])
829 850 lnum[0] += 1
830 851 return line
831 852
832 853 # Build the list of names on this line of code where the exception
833 854 # occurred.
834 855 try:
835 856 # This builds the names list in-place by capturing it from the
836 857 # enclosing scope.
837 858 tokenize.tokenize(linereader, tokeneater)
838 859 except IndexError:
839 860 # signals exit of tokenizer
840 861 pass
841 862 except tokenize.TokenError,msg:
842 863 _m = ("An unexpected error occurred while tokenizing input\n"
843 864 "The following traceback may be corrupted or invalid\n"
844 865 "The error message is: %s\n" % msg)
845 866 error(_m)
846 867
847 868 # prune names list of duplicates, but keep the right order
848 869 unique_names = uniq_stable(names)
849 870
850 871 # Start loop over vars
851 872 lvals = []
852 873 if self.include_vars:
853 874 for name_full in unique_names:
854 875 name_base = name_full.split('.',1)[0]
855 876 if name_base in frame.f_code.co_varnames:
856 877 if locals.has_key(name_base):
857 878 try:
858 879 value = repr(eval(name_full,locals))
859 880 except:
860 881 value = undefined
861 882 else:
862 883 value = undefined
863 884 name = tpl_local_var % name_full
864 885 else:
865 886 if frame.f_globals.has_key(name_base):
866 887 try:
867 888 value = repr(eval(name_full,frame.f_globals))
868 889 except:
869 890 value = undefined
870 891 else:
871 892 value = undefined
872 893 name = tpl_global_var % name_full
873 894 lvals.append(tpl_name_val % (name,value))
874 895 if lvals:
875 896 lvals = '%s%s' % (indent,em_normal.join(lvals))
876 897 else:
877 898 lvals = ''
878 899
879 900 level = '%s %s\n' % (link,call)
880 901
881 902 if index is None:
882 903 frames.append(level)
883 904 else:
884 905 frames.append('%s%s' % (level,''.join(
885 906 _format_traceback_lines(lnum,index,lines,Colors,lvals,
886 907 col_scheme))))
887 908
888 909 # Get (safely) a string form of the exception info
889 910 try:
890 911 etype_str,evalue_str = map(str,(etype,evalue))
891 912 except:
892 913 # User exception is improperly defined.
893 914 etype,evalue = str,sys.exc_info()[:2]
894 915 etype_str,evalue_str = map(str,(etype,evalue))
895 916 # ... and format it
896 917 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
897 918 ColorsNormal, evalue_str)]
898 919 if type(evalue) is types.InstanceType:
899 920 try:
900 921 names = [w for w in dir(evalue) if isinstance(w, basestring)]
901 922 except:
902 923 # Every now and then, an object with funny inernals blows up
903 924 # when dir() is called on it. We do the best we can to report
904 925 # the problem and continue
905 926 _m = '%sException reporting error (object with broken dir())%s:'
906 927 exception.append(_m % (Colors.excName,ColorsNormal))
907 928 etype_str,evalue_str = map(str,sys.exc_info()[:2])
908 929 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
909 930 ColorsNormal, evalue_str))
910 931 names = []
911 932 for name in names:
912 933 value = text_repr(getattr(evalue, name))
913 934 exception.append('\n%s%s = %s' % (indent, name, value))
914 935
915 936 # vds: >>
916 937 if records:
917 938 filepath, lnum = records[-1][1:3]
918 939 #print "file:", str(file), "linenb", str(lnum) # dbg
919 940 filepath = os.path.abspath(filepath)
920 941 ipinst = ipapi.get()
921 942 if ipinst is not None:
922 943 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
923 944 # vds: <<
924 945
925 946 # return all our info assembled as a single string
926 947 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
927 948 return [head] + frames + [''.join(exception[0])]
928 949
929 950 def debugger(self,force=False):
930 951 """Call up the pdb debugger if desired, always clean up the tb
931 952 reference.
932 953
933 954 Keywords:
934 955
935 956 - force(False): by default, this routine checks the instance call_pdb
936 957 flag and does not actually invoke the debugger if the flag is false.
937 958 The 'force' option forces the debugger to activate even if the flag
938 959 is false.
939 960
940 961 If the call_pdb flag is set, the pdb interactive debugger is
941 962 invoked. In all cases, the self.tb reference to the current traceback
942 963 is deleted to prevent lingering references which hamper memory
943 964 management.
944 965
945 966 Note that each call to pdb() does an 'import readline', so if your app
946 967 requires a special setup for the readline completers, you'll have to
947 968 fix that by hand after invoking the exception handler."""
948 969
949 970 if force or self.call_pdb:
950 971 if self.pdb is None:
951 972 self.pdb = debugger.Pdb(
952 973 self.color_scheme_table.active_scheme_name)
953 974 # the system displayhook may have changed, restore the original
954 975 # for pdb
955 976 display_trap = DisplayTrap(hook=sys.__displayhook__)
956 977 with display_trap:
957 978 self.pdb.reset()
958 979 # Find the right frame so we don't pop up inside ipython itself
959 980 if hasattr(self,'tb') and self.tb is not None:
960 981 etb = self.tb
961 982 else:
962 983 etb = self.tb = sys.last_traceback
963 984 while self.tb is not None and self.tb.tb_next is not None:
964 985 self.tb = self.tb.tb_next
965 986 if etb and etb.tb_next:
966 987 etb = etb.tb_next
967 988 self.pdb.botframe = etb.tb_frame
968 989 self.pdb.interaction(self.tb.tb_frame, self.tb)
969 990
970 991 if hasattr(self,'tb'):
971 992 del self.tb
972 993
973 994 def handler(self, info=None):
974 995 (etype, evalue, etb) = info or sys.exc_info()
975 996 self.tb = etb
976 io.Term.cout.flush()
977 io.Term.cerr.write(self.text(etype, evalue, etb))
978 io.Term.cerr.write('\n')
997 ostream = self.ostream
998 ostream.flush()
999 ostream.write(self.text(etype, evalue, etb))
1000 ostream.write('\n')
1001 ostream.flush()
979 1002
980 1003 # Changed so an instance can just be called as VerboseTB_inst() and print
981 1004 # out the right info on its own.
982 1005 def __call__(self, etype=None, evalue=None, etb=None):
983 1006 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
984 1007 if etb is None:
985 1008 self.handler()
986 1009 else:
987 1010 self.handler((etype, evalue, etb))
988 1011 try:
989 1012 self.debugger()
990 1013 except KeyboardInterrupt:
991 1014 print "\nKeyboardInterrupt"
992 1015
993 1016 #----------------------------------------------------------------------------
994 1017 class FormattedTB(VerboseTB, ListTB):
995 1018 """Subclass ListTB but allow calling with a traceback.
996 1019
997 1020 It can thus be used as a sys.excepthook for Python > 2.1.
998 1021
999 1022 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1000 1023
1001 1024 Allows a tb_offset to be specified. This is useful for situations where
1002 1025 one needs to remove a number of topmost frames from the traceback (such as
1003 1026 occurs with python programs that themselves execute other python code,
1004 1027 like Python shells). """
1005 1028
1006 def __init__(self, mode='Plain', color_scheme='Linux',
1007 tb_offset=0, long_header=0, call_pdb=0, include_vars=0):
1029 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1030 ostream=None,
1031 tb_offset=0, long_header=False, include_vars=False):
1008 1032
1009 1033 # NEVER change the order of this list. Put new modes at the end:
1010 1034 self.valid_modes = ['Plain','Context','Verbose']
1011 1035 self.verbose_modes = self.valid_modes[1:3]
1012 1036
1013 VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
1014 call_pdb=call_pdb,include_vars=include_vars)
1037 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1038 ostream=ostream, tb_offset=tb_offset,
1039 long_header=long_header, include_vars=include_vars)
1015 1040
1016 1041 # Different types of tracebacks are joined with different separators to
1017 1042 # form a single string. They are taken from this dict
1018 1043 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1019 1044 # set_mode also sets the tb_join_char attribute
1020 1045 self.set_mode(mode)
1021 1046
1022 1047 def _extract_tb(self,tb):
1023 1048 if tb:
1024 1049 return traceback.extract_tb(tb)
1025 1050 else:
1026 1051 return None
1027 1052
1028 1053 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1029 1054 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1030 1055 mode = self.mode
1031 1056 if mode in self.verbose_modes:
1032 1057 # Verbose modes need a full traceback
1033 1058 return VerboseTB.structured_traceback(
1034 1059 self, etype, value, tb, tb_offset, context
1035 1060 )
1036 1061 else:
1037 1062 # We must check the source cache because otherwise we can print
1038 1063 # out-of-date source code.
1039 1064 linecache.checkcache()
1040 1065 # Now we can extract and format the exception
1041 1066 elist = self._extract_tb(tb)
1042 1067 return ListTB.structured_traceback(
1043 1068 self, etype, value, elist, tb_offset, context
1044 1069 )
1045 1070
1046 1071 def stb2text(self, stb):
1047 1072 """Convert a structured traceback (a list) to a string."""
1048 1073 return self.tb_join_char.join(stb)
1049 1074
1050 1075
1051 1076 def set_mode(self,mode=None):
1052 1077 """Switch to the desired mode.
1053 1078
1054 1079 If mode is not specified, cycles through the available modes."""
1055 1080
1056 1081 if not mode:
1057 1082 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1058 1083 len(self.valid_modes)
1059 1084 self.mode = self.valid_modes[new_idx]
1060 1085 elif mode not in self.valid_modes:
1061 1086 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
1062 1087 'Valid modes: '+str(self.valid_modes)
1063 1088 else:
1064 1089 self.mode = mode
1065 1090 # include variable details only in 'Verbose' mode
1066 1091 self.include_vars = (self.mode == self.valid_modes[2])
1067 1092 # Set the join character for generating text tracebacks
1068 1093 self.tb_join_char = self._join_chars[mode]
1069 1094
1070 1095 # some convenient shorcuts
1071 1096 def plain(self):
1072 1097 self.set_mode(self.valid_modes[0])
1073 1098
1074 1099 def context(self):
1075 1100 self.set_mode(self.valid_modes[1])
1076 1101
1077 1102 def verbose(self):
1078 1103 self.set_mode(self.valid_modes[2])
1079 1104
1080 1105 #----------------------------------------------------------------------------
1081 1106 class AutoFormattedTB(FormattedTB):
1082 1107 """A traceback printer which can be called on the fly.
1083 1108
1084 1109 It will find out about exceptions by itself.
1085 1110
1086 1111 A brief example:
1087 1112
1088 1113 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1089 1114 try:
1090 1115 ...
1091 1116 except:
1092 1117 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1093 1118 """
1094 1119
1095 1120 def __call__(self,etype=None,evalue=None,etb=None,
1096 1121 out=None,tb_offset=None):
1097 1122 """Print out a formatted exception traceback.
1098 1123
1099 1124 Optional arguments:
1100 1125 - out: an open file-like object to direct output to.
1101 1126
1102 1127 - tb_offset: the number of frames to skip over in the stack, on a
1103 1128 per-call basis (this overrides temporarily the instance's tb_offset
1104 1129 given at initialization time. """
1105 1130
1131
1106 1132 if out is None:
1107 if self.out_stream == 'stdout':
1108 out = sys.stdout
1109 else:
1110 out = io.Term.cerr
1133 out = self.ostream
1111 1134 out.flush()
1112 1135 out.write(self.text(etype, evalue, etb, tb_offset))
1113 1136 out.write('\n')
1114 1137 out.flush()
1115 1138 # FIXME: we should remove the auto pdb behavior from here and leave
1116 1139 # that to the clients.
1117 1140 try:
1118 1141 self.debugger()
1119 1142 except KeyboardInterrupt:
1120 1143 print "\nKeyboardInterrupt"
1121 1144
1122 1145 def structured_traceback(self, etype=None, value=None, tb=None,
1123 1146 tb_offset=None, context=5):
1124 1147 if etype is None:
1125 1148 etype,value,tb = sys.exc_info()
1126 1149 self.tb = tb
1127 1150 return FormattedTB.structured_traceback(
1128 1151 self, etype, value, tb, tb_offset, context)
1129 1152
1130 1153 #---------------------------------------------------------------------------
1131 1154
1132 1155 # A simple class to preserve Nathan's original functionality.
1133 1156 class ColorTB(FormattedTB):
1134 1157 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1135 1158 def __init__(self,color_scheme='Linux',call_pdb=0):
1136 1159 FormattedTB.__init__(self,color_scheme=color_scheme,
1137 1160 call_pdb=call_pdb)
1138 1161
1139 1162
1140 1163 class SyntaxTB(ListTB):
1141 1164 """Extension which holds some state: the last exception value"""
1142 1165
1143 1166 def __init__(self,color_scheme = 'NoColor'):
1144 1167 ListTB.__init__(self,color_scheme)
1145 1168 self.last_syntax_error = None
1146 1169
1147 1170 def __call__(self, etype, value, elist):
1148 1171 self.last_syntax_error = value
1149 1172 ListTB.__call__(self,etype,value,elist)
1150 1173
1151 1174 def clear_err_state(self):
1152 1175 """Return the current error state and clear it"""
1153 1176 e = self.last_syntax_error
1154 1177 self.last_syntax_error = None
1155 1178 return e
1156 1179
1157 1180 def stb2text(self, stb):
1158 1181 """Convert a structured traceback (a list) to a string."""
1159 1182 return ''.join(stb)
1160 1183
1161 1184
1162 1185 #----------------------------------------------------------------------------
1163 1186 # module testing (minimal)
1164 1187 if __name__ == "__main__":
1165 1188 def spam(c, (d, e)):
1166 1189 x = c + d
1167 1190 y = c * d
1168 1191 foo(x, y)
1169 1192
1170 1193 def foo(a, b, bar=1):
1171 1194 eggs(a, b + bar)
1172 1195
1173 1196 def eggs(f, g, z=globals()):
1174 1197 h = f + g
1175 1198 i = f - g
1176 1199 return h / i
1177 1200
1178 1201 print ''
1179 1202 print '*** Before ***'
1180 1203 try:
1181 1204 print spam(1, (2, 3))
1182 1205 except:
1183 1206 traceback.print_exc()
1184 1207 print ''
1185 1208
1186 1209 handler = ColorTB()
1187 1210 print '*** ColorTB ***'
1188 1211 try:
1189 1212 print spam(1, (2, 3))
1190 1213 except:
1191 1214 apply(handler, sys.exc_info() )
1192 1215 print ''
1193 1216
1194 1217 handler = VerboseTB()
1195 1218 print '*** VerboseTB ***'
1196 1219 try:
1197 1220 print spam(1, (2, 3))
1198 1221 except:
1199 1222 apply(handler, sys.exc_info() )
1200 1223 print ''
1201 1224
@@ -1,173 +1,170 b''
1 1 """Global IPython app to support test running.
2 2
3 3 We must start our own ipython object and heavily muck with it so that all the
4 4 modifications IPython makes to system behavior don't send the doctest machinery
5 5 into a fit. This code should be considered a gross hack, but it gets the job
6 6 done.
7 7 """
8 8
9 9 from __future__ import absolute_import
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2009 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import __builtin__
23 23 import commands
24 24 import os
25 25 import sys
26 26
27 27 from . import tools
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Functions
31 31 #-----------------------------------------------------------------------------
32 32
33 33 # Hack to modify the %run command so we can sync the user's namespace with the
34 34 # test globals. Once we move over to a clean magic system, this will be done
35 35 # with much less ugliness.
36 36
37 37 class py_file_finder(object):
38 38 def __init__(self,test_filename):
39 39 self.test_filename = test_filename
40 40
41 41 def __call__(self,name):
42 42 from IPython.utils.path import get_py_filename
43 43 try:
44 44 return get_py_filename(name)
45 45 except IOError:
46 46 test_dir = os.path.dirname(self.test_filename)
47 47 new_path = os.path.join(test_dir,name)
48 48 return get_py_filename(new_path)
49 49
50 50
51 51 def _run_ns_sync(self,arg_s,runner=None):
52 52 """Modified version of %run that syncs testing namespaces.
53 53
54 54 This is strictly needed for running doctests that call %run.
55 55 """
56 56 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
57 57
58 58 _ip = get_ipython()
59 59 finder = py_file_finder(arg_s)
60 60 out = _ip.magic_run_ori(arg_s,runner,finder)
61 61 return out
62 62
63 63
64 64 class ipnsdict(dict):
65 65 """A special subclass of dict for use as an IPython namespace in doctests.
66 66
67 67 This subclass adds a simple checkpointing capability so that when testing
68 68 machinery clears it (we use it as the test execution context), it doesn't
69 69 get completely destroyed.
70 70 """
71 71
72 72 def __init__(self,*a):
73 73 dict.__init__(self,*a)
74 74 self._savedict = {}
75 75
76 76 def clear(self):
77 77 dict.clear(self)
78 78 self.update(self._savedict)
79 79
80 80 def _checkpoint(self):
81 81 self._savedict.clear()
82 82 self._savedict.update(self)
83 83
84 84 def update(self,other):
85 85 self._checkpoint()
86 86 dict.update(self,other)
87 87
88 88 # If '_' is in the namespace, python won't set it when executing code,
89 89 # and we have examples that test it. So we ensure that the namespace
90 90 # is always 'clean' of it before it's used for test code execution.
91 91 self.pop('_',None)
92 92
93 93 # The builtins namespace must *always* be the real __builtin__ module,
94 94 # else weird stuff happens. The main ipython code does have provisions
95 95 # to ensure this after %run, but since in this class we do some
96 96 # aggressive low-level cleaning of the execution namespace, we need to
97 97 # correct for that ourselves, to ensure consitency with the 'real'
98 98 # ipython.
99 99 self['__builtins__'] = __builtin__
100 100
101 101
102 102 def get_ipython():
103 103 # This will get replaced by the real thing once we start IPython below
104 104 return start_ipython()
105 105
106 106
107 107 def start_ipython():
108 108 """Start a global IPython shell, which we need for IPython-specific syntax.
109 109 """
110 110 global get_ipython
111 111
112 112 # This function should only ever run once!
113 113 if hasattr(start_ipython, 'already_called'):
114 114 return
115 115 start_ipython.already_called = True
116 116
117 117 from IPython.frontend.terminal import interactiveshell
118 118
119 119 def xsys(cmd):
120 120 """Execute a command and print its output.
121 121
122 122 This is just a convenience function to replace the IPython system call
123 123 with one that is more doctest-friendly.
124 124 """
125 125 cmd = _ip.var_expand(cmd,depth=1)
126 126 sys.stdout.write(commands.getoutput(cmd))
127 127 sys.stdout.flush()
128 128
129 129 # Store certain global objects that IPython modifies
130 130 _displayhook = sys.displayhook
131 131 _excepthook = sys.excepthook
132 132 _main = sys.modules.get('__main__')
133 133
134 134 # Create custom argv and namespaces for our IPython to be test-friendly
135 135 config = tools.default_config()
136 136
137 137 # Create and initialize our test-friendly IPython instance.
138 138 shell = interactiveshell.TerminalInteractiveShell.instance(
139 139 config=config,
140 140 user_ns=ipnsdict(), user_global_ns={}
141 141 )
142 142
143 143 # A few more tweaks needed for playing nicely with doctests...
144 144
145 145 # These traps are normally only active for interactive use, set them
146 146 # permanently since we'll be mocking interactive sessions.
147 147 shell.builtin_trap.set()
148 148
149 # Set error printing to stdout so nose can doctest exceptions
150 shell.InteractiveTB.out_stream = 'stdout'
151
152 149 # Modify the IPython system call with one that uses getoutput, so that we
153 150 # can capture subcommands and print them to Python's stdout, otherwise the
154 151 # doctest machinery would miss them.
155 152 shell.system = xsys
156 153
157 154 # IPython is ready, now clean up some global state...
158 155
159 156 # Deactivate the various python system hooks added by ipython for
160 157 # interactive convenience so we don't confuse the doctest system
161 158 sys.modules['__main__'] = _main
162 159 sys.displayhook = _displayhook
163 160 sys.excepthook = _excepthook
164 161
165 162 # So that ipython magics and aliases can be doctested (they work by making
166 163 # a call into a global _ip object). Also make the top-level get_ipython
167 164 # now return this without recursively calling here again.
168 165 _ip = shell
169 166 get_ipython = _ip.get_ipython
170 167 __builtin__._ip = _ip
171 168 __builtin__.get_ipython = get_ipython
172 169
173 170 return _ip
General Comments 0
You need to be logged in to leave comments. Login now