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