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