##// END OF EJS Templates
Refactor of coloring and traceback mechanism....
Matthias Bussonnier -
Show More
@@ -1,1461 +1,1475 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Verbose and colourful traceback formatting.
4 4
5 5 **ColorTB**
6 6
7 7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 8 ColorTB class is a solution to that problem. It colors the different parts of a
9 9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 10 text editor.
11 11
12 12 Installation instructions for ColorTB::
13 13
14 14 import sys,ultratb
15 15 sys.excepthook = ultratb.ColorTB()
16 16
17 17 **VerboseTB**
18 18
19 19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 21 and intended it for CGI programmers, but why should they have all the fun? I
22 22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 23 but kind of neat, and maybe useful for long-running programs that you believe
24 24 are bug-free. If a crash *does* occur in that type of program you want details.
25 25 Give it a shot--you'll love it or you'll hate it.
26 26
27 27 .. note::
28 28
29 29 The Verbose mode prints the variables currently visible where the exception
30 30 happened (shortening their strings if too long). This can potentially be
31 31 very slow, if you happen to have a huge data structure whose string
32 32 representation is complex to compute. Your computer may appear to freeze for
33 33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 34 with Ctrl-C (maybe hitting it more than once).
35 35
36 36 If you encounter this kind of situation often, you may want to use the
37 37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 38 variables (but otherwise includes the information and context given by
39 39 Verbose).
40 40
41 41 .. note::
42 42
43 43 The verbose mode print all variables in the stack, which means it can
44 44 potentially leak sensitive information like access keys, or unencryted
45 45 password.
46 46
47 47 Installation instructions for VerboseTB::
48 48
49 49 import sys,ultratb
50 50 sys.excepthook = ultratb.VerboseTB()
51 51
52 52 Note: Much of the code in this module was lifted verbatim from the standard
53 53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54 54
55 55 Color schemes
56 56 -------------
57 57
58 58 The colors are defined in the class TBTools through the use of the
59 59 ColorSchemeTable class. Currently the following exist:
60 60
61 61 - NoColor: allows all of this module to be used in any terminal (the color
62 62 escapes are just dummy blank strings).
63 63
64 64 - Linux: is meant to look good in a terminal like the Linux console (black
65 65 or very dark background).
66 66
67 67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 68 in light background terminals.
69 69
70 70 - Neutral: a neutral color scheme that should be readable on both light and
71 71 dark background
72 72
73 73 You can implement other color schemes easily, the syntax is fairly
74 74 self-explanatory. Please send back new schemes you develop to the author for
75 75 possible inclusion in future releases.
76 76
77 77 Inheritance diagram:
78 78
79 79 .. inheritance-diagram:: IPython.core.ultratb
80 80 :parts: 3
81 81 """
82 82
83 83 #*****************************************************************************
84 84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 86 #
87 87 # Distributed under the terms of the BSD License. The full license is in
88 88 # the file COPYING, distributed as part of this software.
89 89 #*****************************************************************************
90 90
91 91
92 92 import dis
93 93 import inspect
94 94 import keyword
95 95 import linecache
96 96 import os
97 97 import pydoc
98 98 import re
99 99 import sys
100 100 import time
101 101 import tokenize
102 102 import traceback
103 103
104 104 try: # Python 2
105 105 generate_tokens = tokenize.generate_tokens
106 106 except AttributeError: # Python 3
107 107 generate_tokens = tokenize.tokenize
108 108
109 109 # For purposes of monkeypatching inspect to fix a bug in it.
110 110 from inspect import getsourcefile, getfile, getmodule, \
111 111 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
112 112
113 113 # IPython's own modules
114 114 from IPython import get_ipython
115 115 from IPython.core import debugger
116 116 from IPython.core.display_trap import DisplayTrap
117 117 from IPython.core.excolors import exception_colors
118 118 from IPython.utils import PyColorize
119 119 from IPython.utils import openpy
120 120 from IPython.utils import path as util_path
121 121 from IPython.utils import py3compat
122 122 from IPython.utils.data import uniq_stable
123 123 from IPython.utils.terminal import get_terminal_size
124 124 from logging import info, error, debug
125 125
126 126 import IPython.utils.colorable as colorable
127 127
128 128 # Globals
129 129 # amount of space to put line numbers before verbose tracebacks
130 130 INDENT_SIZE = 8
131 131
132 132 # Default color scheme. This is used, for example, by the traceback
133 133 # formatter. When running in an actual IPython instance, the user's rc.colors
134 134 # value is used, but having a module global makes this functionality available
135 135 # to users of ultratb who are NOT running inside ipython.
136 136 DEFAULT_SCHEME = 'NoColor'
137 137
138 138 # ---------------------------------------------------------------------------
139 139 # Code begins
140 140
141 141 # Utility functions
142 142 def inspect_error():
143 143 """Print a message about internal inspect errors.
144 144
145 145 These are unfortunately quite common."""
146 146
147 147 error('Internal Python error in the inspect module.\n'
148 148 'Below is the traceback from this internal error.\n')
149 149
150 150
151 151 # This function is a monkeypatch we apply to the Python inspect module. We have
152 152 # now found when it's needed (see discussion on issue gh-1456), and we have a
153 153 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
154 154 # the monkeypatch is not applied. TK, Aug 2012.
155 155 def findsource(object):
156 156 """Return the entire source file and starting line number for an object.
157 157
158 158 The argument may be a module, class, method, function, traceback, frame,
159 159 or code object. The source code is returned as a list of all the lines
160 160 in the file and the line number indexes a line in that list. An IOError
161 161 is raised if the source code cannot be retrieved.
162 162
163 163 FIXED version with which we monkeypatch the stdlib to work around a bug."""
164 164
165 165 file = getsourcefile(object) or getfile(object)
166 166 # If the object is a frame, then trying to get the globals dict from its
167 167 # module won't work. Instead, the frame object itself has the globals
168 168 # dictionary.
169 169 globals_dict = None
170 170 if inspect.isframe(object):
171 171 # XXX: can this ever be false?
172 172 globals_dict = object.f_globals
173 173 else:
174 174 module = getmodule(object, file)
175 175 if module:
176 176 globals_dict = module.__dict__
177 177 lines = linecache.getlines(file, globals_dict)
178 178 if not lines:
179 179 raise IOError('could not get source code')
180 180
181 181 if ismodule(object):
182 182 return lines, 0
183 183
184 184 if isclass(object):
185 185 name = object.__name__
186 186 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
187 187 # make some effort to find the best matching class definition:
188 188 # use the one with the least indentation, which is the one
189 189 # that's most probably not inside a function definition.
190 190 candidates = []
191 191 for i, line in enumerate(lines):
192 192 match = pat.match(line)
193 193 if match:
194 194 # if it's at toplevel, it's already the best one
195 195 if line[0] == 'c':
196 196 return lines, i
197 197 # else add whitespace to candidate list
198 198 candidates.append((match.group(1), i))
199 199 if candidates:
200 200 # this will sort by whitespace, and by line number,
201 201 # less whitespace first
202 202 candidates.sort()
203 203 return lines, candidates[0][1]
204 204 else:
205 205 raise IOError('could not find class definition')
206 206
207 207 if ismethod(object):
208 208 object = object.__func__
209 209 if isfunction(object):
210 210 object = object.__code__
211 211 if istraceback(object):
212 212 object = object.tb_frame
213 213 if isframe(object):
214 214 object = object.f_code
215 215 if iscode(object):
216 216 if not hasattr(object, 'co_firstlineno'):
217 217 raise IOError('could not find function definition')
218 218 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
219 219 pmatch = pat.match
220 220 # fperez - fix: sometimes, co_firstlineno can give a number larger than
221 221 # the length of lines, which causes an error. Safeguard against that.
222 222 lnum = min(object.co_firstlineno, len(lines)) - 1
223 223 while lnum > 0:
224 224 if pmatch(lines[lnum]):
225 225 break
226 226 lnum -= 1
227 227
228 228 return lines, lnum
229 229 raise IOError('could not find code object')
230 230
231 231
232 232 # This is a patched version of inspect.getargs that applies the (unmerged)
233 233 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
234 234 # https://github.com/ipython/ipython/issues/8205 and
235 235 # https://github.com/ipython/ipython/issues/8293
236 236 def getargs(co):
237 237 """Get information about the arguments accepted by a code object.
238 238
239 239 Three things are returned: (args, varargs, varkw), where 'args' is
240 240 a list of argument names (possibly containing nested lists), and
241 241 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
242 242 if not iscode(co):
243 243 raise TypeError('{!r} is not a code object'.format(co))
244 244
245 245 nargs = co.co_argcount
246 246 names = co.co_varnames
247 247 args = list(names[:nargs])
248 248 step = 0
249 249
250 250 # The following acrobatics are for anonymous (tuple) arguments.
251 251 for i in range(nargs):
252 252 if args[i][:1] in ('', '.'):
253 253 stack, remain, count = [], [], []
254 254 while step < len(co.co_code):
255 255 op = ord(co.co_code[step])
256 256 step = step + 1
257 257 if op >= dis.HAVE_ARGUMENT:
258 258 opname = dis.opname[op]
259 259 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
260 260 step = step + 2
261 261 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
262 262 remain.append(value)
263 263 count.append(value)
264 264 elif opname in ('STORE_FAST', 'STORE_DEREF'):
265 265 if op in dis.haslocal:
266 266 stack.append(co.co_varnames[value])
267 267 elif op in dis.hasfree:
268 268 stack.append((co.co_cellvars + co.co_freevars)[value])
269 269 # Special case for sublists of length 1: def foo((bar))
270 270 # doesn't generate the UNPACK_TUPLE bytecode, so if
271 271 # `remain` is empty here, we have such a sublist.
272 272 if not remain:
273 273 stack[0] = [stack[0]]
274 274 break
275 275 else:
276 276 remain[-1] = remain[-1] - 1
277 277 while remain[-1] == 0:
278 278 remain.pop()
279 279 size = count.pop()
280 280 stack[-size:] = [stack[-size:]]
281 281 if not remain:
282 282 break
283 283 remain[-1] = remain[-1] - 1
284 284 if not remain:
285 285 break
286 286 args[i] = stack[0]
287 287
288 288 varargs = None
289 289 if co.co_flags & inspect.CO_VARARGS:
290 290 varargs = co.co_varnames[nargs]
291 291 nargs = nargs + 1
292 292 varkw = None
293 293 if co.co_flags & inspect.CO_VARKEYWORDS:
294 294 varkw = co.co_varnames[nargs]
295 295 return inspect.Arguments(args, varargs, varkw)
296 296
297 297
298 298 # Monkeypatch inspect to apply our bugfix.
299 299 def with_patch_inspect(f):
300 300 """
301 301 Deprecated since IPython 6.0
302 302 decorator for monkeypatching inspect.findsource
303 303 """
304 304
305 305 def wrapped(*args, **kwargs):
306 306 save_findsource = inspect.findsource
307 307 save_getargs = inspect.getargs
308 308 inspect.findsource = findsource
309 309 inspect.getargs = getargs
310 310 try:
311 311 return f(*args, **kwargs)
312 312 finally:
313 313 inspect.findsource = save_findsource
314 314 inspect.getargs = save_getargs
315 315
316 316 return wrapped
317 317
318 318
319 319 def fix_frame_records_filenames(records):
320 320 """Try to fix the filenames in each record from inspect.getinnerframes().
321 321
322 322 Particularly, modules loaded from within zip files have useless filenames
323 323 attached to their code object, and inspect.getinnerframes() just uses it.
324 324 """
325 325 fixed_records = []
326 326 for frame, filename, line_no, func_name, lines, index in records:
327 327 # Look inside the frame's globals dictionary for __file__,
328 328 # which should be better. However, keep Cython filenames since
329 329 # we prefer the source filenames over the compiled .so file.
330 330 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
331 331 better_fn = frame.f_globals.get('__file__', None)
332 332 if isinstance(better_fn, str):
333 333 # Check the type just in case someone did something weird with
334 334 # __file__. It might also be None if the error occurred during
335 335 # import.
336 336 filename = better_fn
337 337 fixed_records.append((frame, filename, line_no, func_name, lines, index))
338 338 return fixed_records
339 339
340 340
341 341 @with_patch_inspect
342 342 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
343 343 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
344 344
345 345 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
346 346 # If the error is at the console, don't build any context, since it would
347 347 # otherwise produce 5 blank lines printed out (there is no file at the
348 348 # console)
349 349 rec_check = records[tb_offset:]
350 350 try:
351 351 rname = rec_check[0][1]
352 352 if rname == '<ipython console>' or rname.endswith('<string>'):
353 353 return rec_check
354 354 except IndexError:
355 355 pass
356 356
357 357 aux = traceback.extract_tb(etb)
358 358 assert len(records) == len(aux)
359 359 for i, (file, lnum, _, _) in enumerate(aux):
360 360 maybeStart = lnum - 1 - context // 2
361 361 start = max(maybeStart, 0)
362 362 end = start + context
363 363 lines = linecache.getlines(file)[start:end]
364 364 buf = list(records[i])
365 365 buf[LNUM_POS] = lnum
366 366 buf[INDEX_POS] = lnum - 1 - start
367 367 buf[LINES_POS] = lines
368 368 records[i] = tuple(buf)
369 369 return records[tb_offset:]
370 370
371 371 # Helper function -- largely belongs to VerboseTB, but we need the same
372 372 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
373 373 # can be recognized properly by ipython.el's py-traceback-line-re
374 374 # (SyntaxErrors have to be treated specially because they have no traceback)
375 375
376 376
377 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, _line_format=(lambda x,_:x,None)):
377 def _format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
378 """
379 Format tracebacks lines with pointing arrow, leading numbers...
380
381 Parameters
382 ==========
383
384 lnum: int
385 index: int
386 lines: list[string]
387 Colors:
388 ColorScheme used.
389 lvals: bytes
390 Values of local variables, already colored, to inject just after the error line.
391 _line_format: f (str) -> (str, bool)
392 return (colorized version of str, failure to do so)
393 """
378 394 numbers_width = INDENT_SIZE - 1
379 395 res = []
380 i = lnum - index
381 396
382 for line in lines:
397 for i,line in enumerate(lines, lnum-index):
383 398 line = py3compat.cast_unicode(line)
384 399
385 400 new_line, err = _line_format(line, 'str')
386 if not err: line = new_line
401 if not err:
402 line = new_line
387 403
388 404 if i == lnum:
389 405 # This is the line with the error
390 406 pad = numbers_width - len(str(i))
391 407 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
392 408 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
393 409 Colors.line, line, Colors.Normal)
394 410 else:
395 411 num = '%*s' % (numbers_width, i)
396 412 line = '%s%s%s %s' % (Colors.lineno, num,
397 413 Colors.Normal, line)
398 414
399 415 res.append(line)
400 416 if lvals and i == lnum:
401 417 res.append(lvals + '\n')
402 i = i + 1
403 418 return res
404 419
405 420 def is_recursion_error(etype, value, records):
406 421 try:
407 422 # RecursionError is new in Python 3.5
408 423 recursion_error_type = RecursionError
409 424 except NameError:
410 425 recursion_error_type = RuntimeError
411 426
412 427 # The default recursion limit is 1000, but some of that will be taken up
413 428 # by stack frames in IPython itself. >500 frames probably indicates
414 429 # a recursion error.
415 430 return (etype is recursion_error_type) \
416 431 and "recursion" in str(value).lower() \
417 432 and len(records) > 500
418 433
419 434 def find_recursion(etype, value, records):
420 435 """Identify the repeating stack frames from a RecursionError traceback
421 436
422 437 'records' is a list as returned by VerboseTB.get_records()
423 438
424 439 Returns (last_unique, repeat_length)
425 440 """
426 441 # This involves a bit of guesswork - we want to show enough of the traceback
427 442 # to indicate where the recursion is occurring. We guess that the innermost
428 443 # quarter of the traceback (250 frames by default) is repeats, and find the
429 444 # first frame (from in to out) that looks different.
430 445 if not is_recursion_error(etype, value, records):
431 446 return len(records), 0
432 447
433 448 # Select filename, lineno, func_name to track frames with
434 449 records = [r[1:4] for r in records]
435 450 inner_frames = records[-(len(records)//4):]
436 451 frames_repeated = set(inner_frames)
437 452
438 453 last_seen_at = {}
439 454 longest_repeat = 0
440 455 i = len(records)
441 456 for frame in reversed(records):
442 457 i -= 1
443 458 if frame not in frames_repeated:
444 459 last_unique = i
445 460 break
446 461
447 462 if frame in last_seen_at:
448 463 distance = last_seen_at[frame] - i
449 464 longest_repeat = max(longest_repeat, distance)
450 465
451 466 last_seen_at[frame] = i
452 467 else:
453 468 last_unique = 0 # The whole traceback was recursion
454 469
455 470 return last_unique, longest_repeat
456 471
457 472 #---------------------------------------------------------------------------
458 473 # Module classes
459 474 class TBTools(colorable.Colorable):
460 475 """Basic tools used by all traceback printer classes."""
461 476
462 477 # Number of frames to skip when reporting tracebacks
463 478 tb_offset = 0
464 479
465 480 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
466 481 # Whether to call the interactive pdb debugger after printing
467 482 # tracebacks or not
468 483 super(TBTools, self).__init__(parent=parent, config=config)
469 484 self.call_pdb = call_pdb
470 485
471 486 # Output stream to write to. Note that we store the original value in
472 487 # a private attribute and then make the public ostream a property, so
473 488 # that we can delay accessing sys.stdout until runtime. The way
474 489 # things are written now, the sys.stdout object is dynamically managed
475 490 # so a reference to it should NEVER be stored statically. This
476 491 # property approach confines this detail to a single location, and all
477 492 # subclasses can simply access self.ostream for writing.
478 493 self._ostream = ostream
479 494
480 495 # Create color table
481 496 self.color_scheme_table = exception_colors()
482 497
483 498 self.set_colors(color_scheme)
484 499 self.old_scheme = color_scheme # save initial value for toggles
485 500
486 501 if call_pdb:
487 502 self.pdb = debugger.Pdb()
488 503 else:
489 504 self.pdb = None
490 505
491 506 def _get_ostream(self):
492 507 """Output stream that exceptions are written to.
493 508
494 509 Valid values are:
495 510
496 511 - None: the default, which means that IPython will dynamically resolve
497 512 to sys.stdout. This ensures compatibility with most tools, including
498 513 Windows (where plain stdout doesn't recognize ANSI escapes).
499 514
500 515 - Any object with 'write' and 'flush' attributes.
501 516 """
502 517 return sys.stdout if self._ostream is None else self._ostream
503 518
504 519 def _set_ostream(self, val):
505 520 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
506 521 self._ostream = val
507 522
508 523 ostream = property(_get_ostream, _set_ostream)
509 524
510 525 def set_colors(self, *args, **kw):
511 526 """Shorthand access to the color table scheme selector method."""
512 527
513 528 # Set own color table
514 529 self.color_scheme_table.set_active_scheme(*args, **kw)
515 530 # for convenience, set Colors to the active scheme
516 531 self.Colors = self.color_scheme_table.active_colors
517 532 # Also set colors of debugger
518 533 if hasattr(self, 'pdb') and self.pdb is not None:
519 534 self.pdb.set_colors(*args, **kw)
520 535
521 536 def color_toggle(self):
522 537 """Toggle between the currently active color scheme and NoColor."""
523 538
524 539 if self.color_scheme_table.active_scheme_name == 'NoColor':
525 540 self.color_scheme_table.set_active_scheme(self.old_scheme)
526 541 self.Colors = self.color_scheme_table.active_colors
527 542 else:
528 543 self.old_scheme = self.color_scheme_table.active_scheme_name
529 544 self.color_scheme_table.set_active_scheme('NoColor')
530 545 self.Colors = self.color_scheme_table.active_colors
531 546
532 547 def stb2text(self, stb):
533 548 """Convert a structured traceback (a list) to a string."""
534 549 return '\n'.join(stb)
535 550
536 551 def text(self, etype, value, tb, tb_offset=None, context=5):
537 552 """Return formatted traceback.
538 553
539 554 Subclasses may override this if they add extra arguments.
540 555 """
541 556 tb_list = self.structured_traceback(etype, value, tb,
542 557 tb_offset, context)
543 558 return self.stb2text(tb_list)
544 559
545 560 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
546 561 context=5, mode=None):
547 562 """Return a list of traceback frames.
548 563
549 564 Must be implemented by each class.
550 565 """
551 566 raise NotImplementedError()
552 567
553 568
554 569 #---------------------------------------------------------------------------
555 570 class ListTB(TBTools):
556 571 """Print traceback information from a traceback list, with optional color.
557 572
558 573 Calling requires 3 arguments: (etype, evalue, elist)
559 574 as would be obtained by::
560 575
561 576 etype, evalue, tb = sys.exc_info()
562 577 if tb:
563 578 elist = traceback.extract_tb(tb)
564 579 else:
565 580 elist = None
566 581
567 582 It can thus be used by programs which need to process the traceback before
568 583 printing (such as console replacements based on the code module from the
569 584 standard library).
570 585
571 586 Because they are meant to be called without a full traceback (only a
572 587 list), instances of this class can't call the interactive pdb debugger."""
573 588
574 589 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
575 590 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
576 591 ostream=ostream, parent=parent,config=config)
577 592
578 593 def __call__(self, etype, value, elist):
579 594 self.ostream.flush()
580 595 self.ostream.write(self.text(etype, value, elist))
581 596 self.ostream.write('\n')
582 597
583 598 def structured_traceback(self, etype, value, elist, tb_offset=None,
584 599 context=5):
585 600 """Return a color formatted string with the traceback info.
586 601
587 602 Parameters
588 603 ----------
589 604 etype : exception type
590 605 Type of the exception raised.
591 606
592 607 value : object
593 608 Data stored in the exception
594 609
595 610 elist : list
596 611 List of frames, see class docstring for details.
597 612
598 613 tb_offset : int, optional
599 614 Number of frames in the traceback to skip. If not given, the
600 615 instance value is used (set in constructor).
601 616
602 617 context : int, optional
603 618 Number of lines of context information to print.
604 619
605 620 Returns
606 621 -------
607 622 String with formatted exception.
608 623 """
609 624 tb_offset = self.tb_offset if tb_offset is None else tb_offset
610 625 Colors = self.Colors
611 626 out_list = []
612 627 if elist:
613 628
614 629 if tb_offset and len(elist) > tb_offset:
615 630 elist = elist[tb_offset:]
616 631
617 632 out_list.append('Traceback %s(most recent call last)%s:' %
618 633 (Colors.normalEm, Colors.Normal) + '\n')
619 634 out_list.extend(self._format_list(elist))
620 635 # The exception info should be a single entry in the list.
621 636 lines = ''.join(self._format_exception_only(etype, value))
622 637 out_list.append(lines)
623 638
624 639 # Note: this code originally read:
625 640
626 641 ## for line in lines[:-1]:
627 642 ## out_list.append(" "+line)
628 643 ## out_list.append(lines[-1])
629 644
630 645 # This means it was indenting everything but the last line by a little
631 646 # bit. I've disabled this for now, but if we see ugliness somewhere we
632 647 # can restore it.
633 648
634 649 return out_list
635 650
636 651 def _format_list(self, extracted_list):
637 652 """Format a list of traceback entry tuples for printing.
638 653
639 654 Given a list of tuples as returned by extract_tb() or
640 655 extract_stack(), return a list of strings ready for printing.
641 656 Each string in the resulting list corresponds to the item with the
642 657 same index in the argument list. Each string ends in a newline;
643 658 the strings may contain internal newlines as well, for those items
644 659 whose source text line is not None.
645 660
646 661 Lifted almost verbatim from traceback.py
647 662 """
648 663
649 664 Colors = self.Colors
650 665 list = []
651 666 for filename, lineno, name, line in extracted_list[:-1]:
652 667 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
653 668 (Colors.filename, filename, Colors.Normal,
654 669 Colors.lineno, lineno, Colors.Normal,
655 670 Colors.name, name, Colors.Normal)
656 671 if line:
657 672 item += ' %s\n' % line.strip()
658 673 list.append(item)
659 674 # Emphasize the last entry
660 675 filename, lineno, name, line = extracted_list[-1]
661 676 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
662 677 (Colors.normalEm,
663 678 Colors.filenameEm, filename, Colors.normalEm,
664 679 Colors.linenoEm, lineno, Colors.normalEm,
665 680 Colors.nameEm, name, Colors.normalEm,
666 681 Colors.Normal)
667 682 if line:
668 683 item += '%s %s%s\n' % (Colors.line, line.strip(),
669 684 Colors.Normal)
670 685 list.append(item)
671 686 return list
672 687
673 688 def _format_exception_only(self, etype, value):
674 689 """Format the exception part of a traceback.
675 690
676 691 The arguments are the exception type and value such as given by
677 692 sys.exc_info()[:2]. The return value is a list of strings, each ending
678 693 in a newline. Normally, the list contains a single string; however,
679 694 for SyntaxError exceptions, it contains several lines that (when
680 695 printed) display detailed information about where the syntax error
681 696 occurred. The message indicating which exception occurred is the
682 697 always last string in the list.
683 698
684 699 Also lifted nearly verbatim from traceback.py
685 700 """
686 701 have_filedata = False
687 702 Colors = self.Colors
688 703 list = []
689 704 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
690 705 if value is None:
691 706 # Not sure if this can still happen in Python 2.6 and above
692 707 list.append(stype + '\n')
693 708 else:
694 709 if issubclass(etype, SyntaxError):
695 710 have_filedata = True
696 711 if not value.filename: value.filename = "<string>"
697 712 if value.lineno:
698 713 lineno = value.lineno
699 714 textline = linecache.getline(value.filename, value.lineno)
700 715 else:
701 716 lineno = 'unknown'
702 717 textline = ''
703 718 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
704 719 (Colors.normalEm,
705 720 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
706 721 Colors.linenoEm, lineno, Colors.Normal ))
707 722 if textline == '':
708 723 textline = py3compat.cast_unicode(value.text, "utf-8")
709 724
710 725 if textline is not None:
711 726 i = 0
712 727 while i < len(textline) and textline[i].isspace():
713 728 i += 1
714 729 list.append('%s %s%s\n' % (Colors.line,
715 730 textline.strip(),
716 731 Colors.Normal))
717 732 if value.offset is not None:
718 733 s = ' '
719 734 for c in textline[i:value.offset - 1]:
720 735 if c.isspace():
721 736 s += c
722 737 else:
723 738 s += ' '
724 739 list.append('%s%s^%s\n' % (Colors.caret, s,
725 740 Colors.Normal))
726 741
727 742 try:
728 743 s = value.msg
729 744 except Exception:
730 745 s = self._some_str(value)
731 746 if s:
732 747 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
733 748 Colors.Normal, s))
734 749 else:
735 750 list.append('%s\n' % stype)
736 751
737 752 # sync with user hooks
738 753 if have_filedata:
739 754 ipinst = get_ipython()
740 755 if ipinst is not None:
741 756 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
742 757
743 758 return list
744 759
745 760 def get_exception_only(self, etype, value):
746 761 """Only print the exception type and message, without a traceback.
747 762
748 763 Parameters
749 764 ----------
750 765 etype : exception type
751 766 value : exception value
752 767 """
753 768 return ListTB.structured_traceback(self, etype, value, [])
754 769
755 770 def show_exception_only(self, etype, evalue):
756 771 """Only print the exception type and message, without a traceback.
757 772
758 773 Parameters
759 774 ----------
760 775 etype : exception type
761 776 value : exception value
762 777 """
763 778 # This method needs to use __call__ from *this* class, not the one from
764 779 # a subclass whose signature or behavior may be different
765 780 ostream = self.ostream
766 781 ostream.flush()
767 782 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
768 783 ostream.flush()
769 784
770 785 def _some_str(self, value):
771 786 # Lifted from traceback.py
772 787 try:
773 788 return py3compat.cast_unicode(str(value))
774 789 except:
775 790 return u'<unprintable %s object>' % type(value).__name__
776 791
777 792
778 793 #----------------------------------------------------------------------------
779 794 class VerboseTB(TBTools):
780 795 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
781 796 of HTML. Requires inspect and pydoc. Crazy, man.
782 797
783 798 Modified version which optionally strips the topmost entries from the
784 799 traceback, to be used with alternate interpreters (because their own code
785 800 would appear in the traceback)."""
786 801
787 802 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
788 803 tb_offset=0, long_header=False, include_vars=True,
789 804 check_cache=None, debugger_cls = None,
790 805 parent=None, config=None):
791 806 """Specify traceback offset, headers and color scheme.
792 807
793 808 Define how many frames to drop from the tracebacks. Calling it with
794 809 tb_offset=1 allows use of this handler in interpreters which will have
795 810 their own code at the top of the traceback (VerboseTB will first
796 811 remove that frame before printing the traceback info)."""
797 812 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
798 813 ostream=ostream, parent=parent, config=config)
799 814 self.tb_offset = tb_offset
800 815 self.long_header = long_header
801 816 self.include_vars = include_vars
802 817 # By default we use linecache.checkcache, but the user can provide a
803 818 # different check_cache implementation. This is used by the IPython
804 819 # kernel to provide tracebacks for interactive code that is cached,
805 820 # by a compiler instance that flushes the linecache but preserves its
806 821 # own code cache.
807 822 if check_cache is None:
808 823 check_cache = linecache.checkcache
809 824 self.check_cache = check_cache
810 825
811 826 self.debugger_cls = debugger_cls or debugger.Pdb
812 827
813 828 def format_records(self, records, last_unique, recursion_repeat):
814 829 """Format the stack frames of the traceback"""
815 830 frames = []
816 831 for r in records[:last_unique+recursion_repeat+1]:
817 832 #print '*** record:',file,lnum,func,lines,index # dbg
818 833 frames.append(self.format_record(*r))
819 834
820 835 if recursion_repeat:
821 836 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
822 837 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
823 838
824 839 return frames
825 840
826 841 def format_record(self, frame, file, lnum, func, lines, index):
827 842 """Format a single stack frame"""
828 843 Colors = self.Colors # just a shorthand + quicker name lookup
829 844 ColorsNormal = Colors.Normal # used a lot
830 845 col_scheme = self.color_scheme_table.active_scheme_name
831 846 indent = ' ' * INDENT_SIZE
832 847 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
833 848 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
834 849 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
835 850 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
836 851 ColorsNormal)
837 852 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
838 853 (Colors.vName, Colors.valEm, ColorsNormal)
839 854 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
840 855 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
841 856 Colors.vName, ColorsNormal)
842 857 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
843 858
844 859 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
845 860 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
846 861 ColorsNormal)
847 862
848 863 abspath = os.path.abspath
849 864
850 865
851 866 if not file:
852 867 file = '?'
853 868 elif file.startswith(str("<")) and file.endswith(str(">")):
854 869 # Not a real filename, no problem...
855 870 pass
856 871 elif not os.path.isabs(file):
857 872 # Try to make the filename absolute by trying all
858 873 # sys.path entries (which is also what linecache does)
859 874 for dirname in sys.path:
860 875 try:
861 876 fullname = os.path.join(dirname, file)
862 877 if os.path.isfile(fullname):
863 878 file = os.path.abspath(fullname)
864 879 break
865 880 except Exception:
866 881 # Just in case that sys.path contains very
867 882 # strange entries...
868 883 pass
869 884
870 885 file = py3compat.cast_unicode(file, util_path.fs_encoding)
871 886 link = tpl_link % util_path.compress_user(file)
872 args, varargs, varkw, locals = inspect.getargvalues(frame)
887 args, varargs, varkw, locals_ = inspect.getargvalues(frame)
873 888
874 889 if func == '?':
875 890 call = ''
876 891 else:
877 892 # Decide whether to include variable details or not
878 893 var_repr = self.include_vars and eqrepr or nullrepr
879 894 try:
880 895 call = tpl_call % (func, inspect.formatargvalues(args,
881 896 varargs, varkw,
882 locals, formatvalue=var_repr))
897 locals_, formatvalue=var_repr))
883 898 except KeyError:
884 899 # This happens in situations like errors inside generator
885 900 # expressions, where local variables are listed in the
886 901 # line, but can't be extracted from the frame. I'm not
887 902 # 100% sure this isn't actually a bug in inspect itself,
888 903 # but since there's no info for us to compute with, the
889 904 # best we can do is report the failure and move on. Here
890 905 # we must *not* call any traceback construction again,
891 906 # because that would mess up use of %debug later on. So we
892 907 # simply report the failure and move on. The only
893 908 # limitation will be that this frame won't have locals
894 909 # listed in the call signature. Quite subtle problem...
895 910 # I can't think of a good way to validate this in a unit
896 911 # test, but running a script consisting of:
897 912 # dict( (k,v.strip()) for (k,v) in range(10) )
898 913 # will illustrate the error, if this exception catch is
899 914 # disabled.
900 915 call = tpl_call_fail % func
901 916
902 917 # Don't attempt to tokenize binary files.
903 918 if file.endswith(('.so', '.pyd', '.dll')):
904 919 return '%s %s\n' % (link, call)
905 920
906 921 elif file.endswith(('.pyc', '.pyo')):
907 922 # Look up the corresponding source file.
908 923 try:
909 924 file = openpy.source_from_cache(file)
910 925 except ValueError:
911 926 # Failed to get the source file for some reason
912 927 # E.g. https://github.com/ipython/ipython/issues/9486
913 928 return '%s %s\n' % (link, call)
914 929
915 930 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
916 931 line = getline(file, lnum[0])
917 932 lnum[0] += 1
918 933 return line
919 934
920 935 # Build the list of names on this line of code where the exception
921 936 # occurred.
922 937 try:
923 938 names = []
924 939 name_cont = False
925 940
926 941 for token_type, token, start, end, line in generate_tokens(linereader):
927 942 # build composite names
928 943 if token_type == tokenize.NAME and token not in keyword.kwlist:
929 944 if name_cont:
930 945 # Continuation of a dotted name
931 946 try:
932 947 names[-1].append(token)
933 948 except IndexError:
934 949 names.append([token])
935 950 name_cont = False
936 951 else:
937 952 # Regular new names. We append everything, the caller
938 953 # will be responsible for pruning the list later. It's
939 954 # very tricky to try to prune as we go, b/c composite
940 955 # names can fool us. The pruning at the end is easy
941 956 # to do (or the caller can print a list with repeated
942 957 # names if so desired.
943 958 names.append([token])
944 959 elif token == '.':
945 960 name_cont = True
946 961 elif token_type == tokenize.NEWLINE:
947 962 break
948 963
949 964 except (IndexError, UnicodeDecodeError, SyntaxError):
950 965 # signals exit of tokenizer
951 966 # SyntaxError can occur if the file is not actually Python
952 967 # - see gh-6300
953 968 pass
954 969 except tokenize.TokenError as msg:
955 970 # Tokenizing may fail for various reasons, many of which are
956 971 # harmless. (A good example is when the line in question is the
957 972 # close of a triple-quoted string, cf gh-6864). We don't want to
958 973 # show this to users, but want make it available for debugging
959 974 # purposes.
960 975 _m = ("An unexpected error occurred while tokenizing input\n"
961 976 "The following traceback may be corrupted or invalid\n"
962 977 "The error message is: %s\n" % msg)
963 978 debug(_m)
964 979
965 980 # Join composite names (e.g. "dict.fromkeys")
966 981 names = ['.'.join(n) for n in names]
967 982 # prune names list of duplicates, but keep the right order
968 983 unique_names = uniq_stable(names)
969 984
970 985 # Start loop over vars
971 lvals = []
986 lvals = ''
987 lvals_list = []
972 988 if self.include_vars:
973 989 for name_full in unique_names:
974 990 name_base = name_full.split('.', 1)[0]
975 991 if name_base in frame.f_code.co_varnames:
976 if name_base in locals:
992 if name_base in locals_:
977 993 try:
978 value = repr(eval(name_full, locals))
994 value = repr(eval(name_full, locals_))
979 995 except:
980 996 value = undefined
981 997 else:
982 998 value = undefined
983 999 name = tpl_local_var % name_full
984 1000 else:
985 1001 if name_base in frame.f_globals:
986 1002 try:
987 1003 value = repr(eval(name_full, frame.f_globals))
988 1004 except:
989 1005 value = undefined
990 1006 else:
991 1007 value = undefined
992 1008 name = tpl_global_var % name_full
993 lvals.append(tpl_name_val % (name, value))
994 if lvals:
995 lvals = '%s%s' % (indent, em_normal.join(lvals))
996 else:
997 lvals = ''
1009 lvals_list.append(tpl_name_val % (name, value))
1010 if lvals_list:
1011 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
998 1012
999 1013 level = '%s %s\n' % (link, call)
1000 1014
1001 1015 if index is None:
1002 1016 return level
1003 1017 else:
1004 1018 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
1005 1019 return '%s%s' % (level, ''.join(
1006 1020 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1007 1021 _line_format)))
1008 1022
1009 1023 def prepare_chained_exception_message(self, cause):
1010 1024 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1011 1025 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1012 1026
1013 1027 if cause:
1014 1028 message = [[direct_cause]]
1015 1029 else:
1016 1030 message = [[exception_during_handling]]
1017 1031 return message
1018 1032
1019 1033 def prepare_header(self, etype, long_version=False):
1020 1034 colors = self.Colors # just a shorthand + quicker name lookup
1021 1035 colorsnormal = colors.Normal # used a lot
1022 1036 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1023 1037 width = min(75, get_terminal_size()[0])
1024 1038 if long_version:
1025 1039 # Header with the exception type, python version, and date
1026 1040 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1027 1041 date = time.ctime(time.time())
1028 1042
1029 1043 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
1030 1044 exc, ' ' * (width - len(str(etype)) - len(pyver)),
1031 1045 pyver, date.rjust(width) )
1032 1046 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1033 1047 "\ncalls leading up to the error, with the most recent (innermost) call last."
1034 1048 else:
1035 1049 # Simplified header
1036 1050 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1037 1051 rjust(width - len(str(etype))) )
1038 1052
1039 1053 return head
1040 1054
1041 1055 def format_exception(self, etype, evalue):
1042 1056 colors = self.Colors # just a shorthand + quicker name lookup
1043 1057 colorsnormal = colors.Normal # used a lot
1044 1058 indent = ' ' * INDENT_SIZE
1045 1059 # Get (safely) a string form of the exception info
1046 1060 try:
1047 1061 etype_str, evalue_str = map(str, (etype, evalue))
1048 1062 except:
1049 1063 # User exception is improperly defined.
1050 1064 etype, evalue = str, sys.exc_info()[:2]
1051 1065 etype_str, evalue_str = map(str, (etype, evalue))
1052 1066 # ... and format it
1053 1067 return ['%s%s%s: %s' % (colors.excName, etype_str,
1054 1068 colorsnormal, py3compat.cast_unicode(evalue_str))]
1055 1069
1056 1070 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1057 1071 """Formats the header, traceback and exception message for a single exception.
1058 1072
1059 1073 This may be called multiple times by Python 3 exception chaining
1060 1074 (PEP 3134).
1061 1075 """
1062 1076 # some locals
1063 1077 orig_etype = etype
1064 1078 try:
1065 1079 etype = etype.__name__
1066 1080 except AttributeError:
1067 1081 pass
1068 1082
1069 1083 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1070 1084 head = self.prepare_header(etype, self.long_header)
1071 1085 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1072 1086
1073 1087 if records is None:
1074 1088 return ""
1075 1089
1076 1090 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1077 1091
1078 1092 frames = self.format_records(records, last_unique, recursion_repeat)
1079 1093
1080 1094 formatted_exception = self.format_exception(etype, evalue)
1081 1095 if records:
1082 1096 filepath, lnum = records[-1][1:3]
1083 1097 filepath = os.path.abspath(filepath)
1084 1098 ipinst = get_ipython()
1085 1099 if ipinst is not None:
1086 1100 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1087 1101
1088 1102 return [[head] + frames + [''.join(formatted_exception[0])]]
1089 1103
1090 1104 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1091 1105 try:
1092 1106 # Try the default getinnerframes and Alex's: Alex's fixes some
1093 1107 # problems, but it generates empty tracebacks for console errors
1094 1108 # (5 blanks lines) where none should be returned.
1095 1109 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1096 1110 except UnicodeDecodeError:
1097 1111 # This can occur if a file's encoding magic comment is wrong.
1098 1112 # I can't see a way to recover without duplicating a bunch of code
1099 1113 # from the stdlib traceback module. --TK
1100 1114 error('\nUnicodeDecodeError while processing traceback.\n')
1101 1115 return None
1102 1116 except:
1103 1117 # FIXME: I've been getting many crash reports from python 2.3
1104 1118 # users, traceable to inspect.py. If I can find a small test-case
1105 1119 # to reproduce this, I should either write a better workaround or
1106 1120 # file a bug report against inspect (if that's the real problem).
1107 1121 # So far, I haven't been able to find an isolated example to
1108 1122 # reproduce the problem.
1109 1123 inspect_error()
1110 1124 traceback.print_exc(file=self.ostream)
1111 1125 info('\nUnfortunately, your original traceback can not be constructed.\n')
1112 1126 return None
1113 1127
1114 1128 def get_parts_of_chained_exception(self, evalue):
1115 1129 def get_chained_exception(exception_value):
1116 1130 cause = getattr(exception_value, '__cause__', None)
1117 1131 if cause:
1118 1132 return cause
1119 1133 if getattr(exception_value, '__suppress_context__', False):
1120 1134 return None
1121 1135 return getattr(exception_value, '__context__', None)
1122 1136
1123 1137 chained_evalue = get_chained_exception(evalue)
1124 1138
1125 1139 if chained_evalue:
1126 1140 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1127 1141
1128 1142 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1129 1143 number_of_lines_of_context=5):
1130 1144 """Return a nice text document describing the traceback."""
1131 1145
1132 1146 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1133 1147 tb_offset)
1134 1148
1135 1149 colors = self.Colors # just a shorthand + quicker name lookup
1136 1150 colorsnormal = colors.Normal # used a lot
1137 1151 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1138 1152 structured_traceback_parts = [head]
1139 1153 chained_exceptions_tb_offset = 0
1140 1154 lines_of_context = 3
1141 1155 formatted_exceptions = formatted_exception
1142 1156 exception = self.get_parts_of_chained_exception(evalue)
1143 1157 if exception:
1144 1158 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1145 1159 etype, evalue, etb = exception
1146 1160 else:
1147 1161 evalue = None
1148 1162 chained_exc_ids = set()
1149 1163 while evalue:
1150 1164 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1151 1165 chained_exceptions_tb_offset)
1152 1166 exception = self.get_parts_of_chained_exception(evalue)
1153 1167
1154 1168 if exception and not id(exception[1]) in chained_exc_ids:
1155 1169 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1156 1170 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1157 1171 etype, evalue, etb = exception
1158 1172 else:
1159 1173 evalue = None
1160 1174
1161 1175 # we want to see exceptions in a reversed order:
1162 1176 # the first exception should be on top
1163 1177 for formatted_exception in reversed(formatted_exceptions):
1164 1178 structured_traceback_parts += formatted_exception
1165 1179
1166 1180 return structured_traceback_parts
1167 1181
1168 1182 def debugger(self, force=False):
1169 1183 """Call up the pdb debugger if desired, always clean up the tb
1170 1184 reference.
1171 1185
1172 1186 Keywords:
1173 1187
1174 1188 - force(False): by default, this routine checks the instance call_pdb
1175 1189 flag and does not actually invoke the debugger if the flag is false.
1176 1190 The 'force' option forces the debugger to activate even if the flag
1177 1191 is false.
1178 1192
1179 1193 If the call_pdb flag is set, the pdb interactive debugger is
1180 1194 invoked. In all cases, the self.tb reference to the current traceback
1181 1195 is deleted to prevent lingering references which hamper memory
1182 1196 management.
1183 1197
1184 1198 Note that each call to pdb() does an 'import readline', so if your app
1185 1199 requires a special setup for the readline completers, you'll have to
1186 1200 fix that by hand after invoking the exception handler."""
1187 1201
1188 1202 if force or self.call_pdb:
1189 1203 if self.pdb is None:
1190 1204 self.pdb = self.debugger_cls()
1191 1205 # the system displayhook may have changed, restore the original
1192 1206 # for pdb
1193 1207 display_trap = DisplayTrap(hook=sys.__displayhook__)
1194 1208 with display_trap:
1195 1209 self.pdb.reset()
1196 1210 # Find the right frame so we don't pop up inside ipython itself
1197 1211 if hasattr(self, 'tb') and self.tb is not None:
1198 1212 etb = self.tb
1199 1213 else:
1200 1214 etb = self.tb = sys.last_traceback
1201 1215 while self.tb is not None and self.tb.tb_next is not None:
1202 1216 self.tb = self.tb.tb_next
1203 1217 if etb and etb.tb_next:
1204 1218 etb = etb.tb_next
1205 1219 self.pdb.botframe = etb.tb_frame
1206 1220 self.pdb.interaction(self.tb.tb_frame, self.tb)
1207 1221
1208 1222 if hasattr(self, 'tb'):
1209 1223 del self.tb
1210 1224
1211 1225 def handler(self, info=None):
1212 1226 (etype, evalue, etb) = info or sys.exc_info()
1213 1227 self.tb = etb
1214 1228 ostream = self.ostream
1215 1229 ostream.flush()
1216 1230 ostream.write(self.text(etype, evalue, etb))
1217 1231 ostream.write('\n')
1218 1232 ostream.flush()
1219 1233
1220 1234 # Changed so an instance can just be called as VerboseTB_inst() and print
1221 1235 # out the right info on its own.
1222 1236 def __call__(self, etype=None, evalue=None, etb=None):
1223 1237 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1224 1238 if etb is None:
1225 1239 self.handler()
1226 1240 else:
1227 1241 self.handler((etype, evalue, etb))
1228 1242 try:
1229 1243 self.debugger()
1230 1244 except KeyboardInterrupt:
1231 1245 print("\nKeyboardInterrupt")
1232 1246
1233 1247
1234 1248 #----------------------------------------------------------------------------
1235 1249 class FormattedTB(VerboseTB, ListTB):
1236 1250 """Subclass ListTB but allow calling with a traceback.
1237 1251
1238 1252 It can thus be used as a sys.excepthook for Python > 2.1.
1239 1253
1240 1254 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1241 1255
1242 1256 Allows a tb_offset to be specified. This is useful for situations where
1243 1257 one needs to remove a number of topmost frames from the traceback (such as
1244 1258 occurs with python programs that themselves execute other python code,
1245 1259 like Python shells). """
1246 1260
1247 1261 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1248 1262 ostream=None,
1249 1263 tb_offset=0, long_header=False, include_vars=False,
1250 1264 check_cache=None, debugger_cls=None,
1251 1265 parent=None, config=None):
1252 1266
1253 1267 # NEVER change the order of this list. Put new modes at the end:
1254 1268 self.valid_modes = ['Plain', 'Context', 'Verbose']
1255 1269 self.verbose_modes = self.valid_modes[1:3]
1256 1270
1257 1271 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1258 1272 ostream=ostream, tb_offset=tb_offset,
1259 1273 long_header=long_header, include_vars=include_vars,
1260 1274 check_cache=check_cache, debugger_cls=debugger_cls,
1261 1275 parent=parent, config=config)
1262 1276
1263 1277 # Different types of tracebacks are joined with different separators to
1264 1278 # form a single string. They are taken from this dict
1265 1279 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1266 1280 # set_mode also sets the tb_join_char attribute
1267 1281 self.set_mode(mode)
1268 1282
1269 1283 def _extract_tb(self, tb):
1270 1284 if tb:
1271 1285 return traceback.extract_tb(tb)
1272 1286 else:
1273 1287 return None
1274 1288
1275 1289 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1276 1290 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1277 1291 mode = self.mode
1278 1292 if mode in self.verbose_modes:
1279 1293 # Verbose modes need a full traceback
1280 1294 return VerboseTB.structured_traceback(
1281 1295 self, etype, value, tb, tb_offset, number_of_lines_of_context
1282 1296 )
1283 1297 else:
1284 1298 # We must check the source cache because otherwise we can print
1285 1299 # out-of-date source code.
1286 1300 self.check_cache()
1287 1301 # Now we can extract and format the exception
1288 1302 elist = self._extract_tb(tb)
1289 1303 return ListTB.structured_traceback(
1290 1304 self, etype, value, elist, tb_offset, number_of_lines_of_context
1291 1305 )
1292 1306
1293 1307 def stb2text(self, stb):
1294 1308 """Convert a structured traceback (a list) to a string."""
1295 1309 return self.tb_join_char.join(stb)
1296 1310
1297 1311
1298 1312 def set_mode(self, mode=None):
1299 1313 """Switch to the desired mode.
1300 1314
1301 1315 If mode is not specified, cycles through the available modes."""
1302 1316
1303 1317 if not mode:
1304 1318 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1305 1319 len(self.valid_modes)
1306 1320 self.mode = self.valid_modes[new_idx]
1307 1321 elif mode not in self.valid_modes:
1308 1322 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1309 1323 'Valid modes: ' + str(self.valid_modes))
1310 1324 else:
1311 1325 self.mode = mode
1312 1326 # include variable details only in 'Verbose' mode
1313 1327 self.include_vars = (self.mode == self.valid_modes[2])
1314 1328 # Set the join character for generating text tracebacks
1315 1329 self.tb_join_char = self._join_chars[self.mode]
1316 1330
1317 1331 # some convenient shortcuts
1318 1332 def plain(self):
1319 1333 self.set_mode(self.valid_modes[0])
1320 1334
1321 1335 def context(self):
1322 1336 self.set_mode(self.valid_modes[1])
1323 1337
1324 1338 def verbose(self):
1325 1339 self.set_mode(self.valid_modes[2])
1326 1340
1327 1341
1328 1342 #----------------------------------------------------------------------------
1329 1343 class AutoFormattedTB(FormattedTB):
1330 1344 """A traceback printer which can be called on the fly.
1331 1345
1332 1346 It will find out about exceptions by itself.
1333 1347
1334 1348 A brief example::
1335 1349
1336 1350 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1337 1351 try:
1338 1352 ...
1339 1353 except:
1340 1354 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1341 1355 """
1342 1356
1343 1357 def __call__(self, etype=None, evalue=None, etb=None,
1344 1358 out=None, tb_offset=None):
1345 1359 """Print out a formatted exception traceback.
1346 1360
1347 1361 Optional arguments:
1348 1362 - out: an open file-like object to direct output to.
1349 1363
1350 1364 - tb_offset: the number of frames to skip over in the stack, on a
1351 1365 per-call basis (this overrides temporarily the instance's tb_offset
1352 1366 given at initialization time. """
1353 1367
1354 1368 if out is None:
1355 1369 out = self.ostream
1356 1370 out.flush()
1357 1371 out.write(self.text(etype, evalue, etb, tb_offset))
1358 1372 out.write('\n')
1359 1373 out.flush()
1360 1374 # FIXME: we should remove the auto pdb behavior from here and leave
1361 1375 # that to the clients.
1362 1376 try:
1363 1377 self.debugger()
1364 1378 except KeyboardInterrupt:
1365 1379 print("\nKeyboardInterrupt")
1366 1380
1367 1381 def structured_traceback(self, etype=None, value=None, tb=None,
1368 1382 tb_offset=None, number_of_lines_of_context=5):
1369 1383 if etype is None:
1370 1384 etype, value, tb = sys.exc_info()
1371 1385 self.tb = tb
1372 1386 return FormattedTB.structured_traceback(
1373 1387 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1374 1388
1375 1389
1376 1390 #---------------------------------------------------------------------------
1377 1391
1378 1392 # A simple class to preserve Nathan's original functionality.
1379 1393 class ColorTB(FormattedTB):
1380 1394 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1381 1395
1382 1396 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1383 1397 FormattedTB.__init__(self, color_scheme=color_scheme,
1384 1398 call_pdb=call_pdb, **kwargs)
1385 1399
1386 1400
1387 1401 class SyntaxTB(ListTB):
1388 1402 """Extension which holds some state: the last exception value"""
1389 1403
1390 1404 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1391 1405 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1392 1406 self.last_syntax_error = None
1393 1407
1394 1408 def __call__(self, etype, value, elist):
1395 1409 self.last_syntax_error = value
1396 1410
1397 1411 ListTB.__call__(self, etype, value, elist)
1398 1412
1399 1413 def structured_traceback(self, etype, value, elist, tb_offset=None,
1400 1414 context=5):
1401 1415 # If the source file has been edited, the line in the syntax error can
1402 1416 # be wrong (retrieved from an outdated cache). This replaces it with
1403 1417 # the current value.
1404 1418 if isinstance(value, SyntaxError) \
1405 1419 and isinstance(value.filename, str) \
1406 1420 and isinstance(value.lineno, int):
1407 1421 linecache.checkcache(value.filename)
1408 1422 newtext = linecache.getline(value.filename, value.lineno)
1409 1423 if newtext:
1410 1424 value.text = newtext
1411 1425 self.last_syntax_error = value
1412 1426 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1413 1427 tb_offset=tb_offset, context=context)
1414 1428
1415 1429 def clear_err_state(self):
1416 1430 """Return the current error state and clear it"""
1417 1431 e = self.last_syntax_error
1418 1432 self.last_syntax_error = None
1419 1433 return e
1420 1434
1421 1435 def stb2text(self, stb):
1422 1436 """Convert a structured traceback (a list) to a string."""
1423 1437 return ''.join(stb)
1424 1438
1425 1439
1426 1440 # some internal-use functions
1427 1441 def text_repr(value):
1428 1442 """Hopefully pretty robust repr equivalent."""
1429 1443 # this is pretty horrible but should always return *something*
1430 1444 try:
1431 1445 return pydoc.text.repr(value)
1432 1446 except KeyboardInterrupt:
1433 1447 raise
1434 1448 except:
1435 1449 try:
1436 1450 return repr(value)
1437 1451 except KeyboardInterrupt:
1438 1452 raise
1439 1453 except:
1440 1454 try:
1441 1455 # all still in an except block so we catch
1442 1456 # getattr raising
1443 1457 name = getattr(value, '__name__', None)
1444 1458 if name:
1445 1459 # ick, recursion
1446 1460 return text_repr(name)
1447 1461 klass = getattr(value, '__class__', None)
1448 1462 if klass:
1449 1463 return '%s instance' % text_repr(klass)
1450 1464 except KeyboardInterrupt:
1451 1465 raise
1452 1466 except:
1453 1467 return 'UNRECOVERABLE REPR FAILURE'
1454 1468
1455 1469
1456 1470 def eqrepr(value, repr=text_repr):
1457 1471 return '=%s' % repr(value)
1458 1472
1459 1473
1460 1474 def nullrepr(value, repr=text_repr):
1461 1475 return ''
@@ -1,318 +1,331 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
37 37 # Imports
38 38 import keyword
39 39 import os
40 40 import sys
41 41 import token
42 42 import tokenize
43 43
44 44 generate_tokens = tokenize.generate_tokens
45 45
46 46 from IPython.utils.coloransi import TermColors, InputTermColors ,ColorScheme, ColorSchemeTable
47 47 from .colorable import Colorable
48 48 from io import StringIO
49 49
50 50 #############################################################################
51 51 ### Python Source Parser (does Highlighting)
52 52 #############################################################################
53 53
54 54 _KEYWORD = token.NT_OFFSET + 1
55 55 _TEXT = token.NT_OFFSET + 2
56 56
57 57 #****************************************************************************
58 58 # Builtin color schemes
59 59
60 60 Colors = TermColors # just a shorthand
61 61
62 62 # Build a few color schemes
63 63 NoColor = ColorScheme(
64 64 'NoColor',{
65 65 'header' : Colors.NoColor,
66 66 token.NUMBER : Colors.NoColor,
67 67 token.OP : Colors.NoColor,
68 68 token.STRING : Colors.NoColor,
69 69 tokenize.COMMENT : Colors.NoColor,
70 70 token.NAME : Colors.NoColor,
71 71 token.ERRORTOKEN : Colors.NoColor,
72 72
73 73 _KEYWORD : Colors.NoColor,
74 74 _TEXT : Colors.NoColor,
75 75
76 76 'in_prompt' : InputTermColors.NoColor, # Input prompt
77 77 'in_number' : InputTermColors.NoColor, # Input prompt number
78 78 'in_prompt2' : InputTermColors.NoColor, # Continuation prompt
79 79 'in_normal' : InputTermColors.NoColor, # color off (usu. Colors.Normal)
80 80
81 81 'out_prompt' : Colors.NoColor, # Output prompt
82 82 'out_number' : Colors.NoColor, # Output prompt number
83 83
84 84 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
85 85 } )
86 86
87 87 LinuxColors = ColorScheme(
88 88 'Linux',{
89 89 'header' : Colors.LightRed,
90 90 token.NUMBER : Colors.LightCyan,
91 91 token.OP : Colors.Yellow,
92 92 token.STRING : Colors.LightBlue,
93 93 tokenize.COMMENT : Colors.LightRed,
94 94 token.NAME : Colors.Normal,
95 95 token.ERRORTOKEN : Colors.Red,
96 96
97 97 _KEYWORD : Colors.LightGreen,
98 98 _TEXT : Colors.Yellow,
99 99
100 100 'in_prompt' : InputTermColors.Green,
101 101 'in_number' : InputTermColors.LightGreen,
102 102 'in_prompt2' : InputTermColors.Green,
103 103 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
104 104
105 105 'out_prompt' : Colors.Red,
106 106 'out_number' : Colors.LightRed,
107 107
108 108 'normal' : Colors.Normal # color off (usu. Colors.Normal)
109 109 } )
110 110
111 111 NeutralColors = ColorScheme(
112 112 'Neutral',{
113 113 'header' : Colors.Red,
114 114 token.NUMBER : Colors.Cyan,
115 115 token.OP : Colors.Blue,
116 116 token.STRING : Colors.Blue,
117 117 tokenize.COMMENT : Colors.Red,
118 118 token.NAME : Colors.Normal,
119 119 token.ERRORTOKEN : Colors.Red,
120 120
121 121 _KEYWORD : Colors.Green,
122 122 _TEXT : Colors.Blue,
123 123
124 124 'in_prompt' : InputTermColors.Blue,
125 125 'in_number' : InputTermColors.LightBlue,
126 126 'in_prompt2' : InputTermColors.Blue,
127 127 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
128 128
129 129 'out_prompt' : Colors.Red,
130 130 'out_number' : Colors.LightRed,
131 131
132 132 'normal' : Colors.Normal # color off (usu. Colors.Normal)
133 133 } )
134 134
135 135 # Hack: the 'neutral' colours are not very visible on a dark background on
136 136 # Windows. Since Windows command prompts have a dark background by default, and
137 137 # relatively few users are likely to alter that, we will use the 'Linux' colours,
138 138 # designed for a dark background, as the default on Windows. Changing it here
139 139 # avoids affecting the prompt colours rendered by prompt_toolkit, where the
140 140 # neutral defaults do work OK.
141 141
142 142 if os.name == 'nt':
143 143 NeutralColors = LinuxColors.copy(name='Neutral')
144 144
145 145 LightBGColors = ColorScheme(
146 146 'LightBG',{
147 147 'header' : Colors.Red,
148 148 token.NUMBER : Colors.Cyan,
149 149 token.OP : Colors.Blue,
150 150 token.STRING : Colors.Blue,
151 151 tokenize.COMMENT : Colors.Red,
152 152 token.NAME : Colors.Normal,
153 153 token.ERRORTOKEN : Colors.Red,
154 154
155 155
156 156 _KEYWORD : Colors.Green,
157 157 _TEXT : Colors.Blue,
158 158
159 159 'in_prompt' : InputTermColors.Blue,
160 160 'in_number' : InputTermColors.LightBlue,
161 161 'in_prompt2' : InputTermColors.Blue,
162 162 'in_normal' : InputTermColors.Normal, # color off (usu. Colors.Normal)
163 163
164 164 'out_prompt' : Colors.Red,
165 165 'out_number' : Colors.LightRed,
166 166
167 167 'normal' : Colors.Normal # color off (usu. Colors.Normal)
168 168 } )
169 169
170 170 # Build table of color schemes (needed by the parser)
171 171 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors, NeutralColors],
172 172 _scheme_default)
173 173
174 174 Undefined = object()
175 175
176 176 class Parser(Colorable):
177 177 """ Format colored Python source.
178 178 """
179 179
180 180 def __init__(self, color_table=None, out = sys.stdout, parent=None, style=None):
181 181 """ Create a parser with a specified color table and output channel.
182 182
183 183 Call format() to process code.
184 184 """
185 185
186 186 super(Parser, self).__init__(parent=parent)
187 187
188 188 self.color_table = color_table and color_table or ANSICodeColors
189 189 self.out = out
190 190 if not style:
191 191 self.style = self.default_style
192 192 else:
193 193 self.style = style
194 194
195 195
196 196 def format(self, raw, out=None, scheme=Undefined):
197 197 import warnings
198 198 if scheme is not Undefined:
199 199 warnings.warn('The `scheme` argument of IPython.utils.PyColorize:Parser.format is deprecated since IPython 6.0.'
200 200 'It will have no effect. Set the parser `style` directly.',
201 201 stacklevel=2)
202 202 return self.format2(raw, out)[0]
203 203
204 204 def format2(self, raw, out = None):
205 205 """ Parse and send the colored source.
206 206
207 207 If out and scheme are not specified, the defaults (given to
208 208 constructor) are used.
209 209
210 210 out should be a file-type object. Optionally, out can be given as the
211 211 string 'str' and the parser will automatically return the output in a
212 212 string."""
213 213
214 214 string_output = 0
215 215 if out == 'str' or self.out == 'str' or \
216 isinstance(self.out,StringIO):
216 isinstance(self.out, StringIO):
217 217 # XXX - I don't really like this state handling logic, but at this
218 218 # point I don't want to make major changes, so adding the
219 219 # isinstance() check is the simplest I can do to ensure correct
220 220 # behavior.
221 221 out_old = self.out
222 222 self.out = StringIO()
223 223 string_output = 1
224 224 elif out is not None:
225 225 self.out = out
226 else:
227 raise ValueError('`out` or `self.out` should be file-like or the value `"str"`')
226 228
227 229 # Fast return of the unmodified input for NoColor scheme
228 230 if self.style == 'NoColor':
229 231 error = False
230 232 self.out.write(raw)
231 233 if string_output:
232 234 return raw,error
233 235 else:
234 236 return None,error
235 237
236 238 # local shorthands
237 239 colors = self.color_table[self.style].colors
238 240 self.colors = colors # put in object so __call__ sees it
239 241
240 242 # Remove trailing whitespace and normalize tabs
241 243 self.raw = raw.expandtabs().rstrip()
242 244
243 245 # store line offsets in self.lines
244 246 self.lines = [0, 0]
245 247 pos = 0
246 248 raw_find = self.raw.find
247 249 lines_append = self.lines.append
248 250 while 1:
249 251 pos = raw_find('\n', pos) + 1
250 252 if not pos: break
251 253 lines_append(pos)
252 254 lines_append(len(self.raw))
253 255
254 256 # parse the source and write it
255 257 self.pos = 0
256 258 text = StringIO(self.raw)
257 259
258 260 error = False
259 261 try:
260 262 for atoken in generate_tokens(text.readline):
261 263 self(*atoken)
262 264 except tokenize.TokenError as ex:
263 265 msg = ex.args[0]
264 266 line = ex.args[1][0]
265 267 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
266 268 (colors[token.ERRORTOKEN],
267 269 msg, self.raw[self.lines[line]:],
268 270 colors.normal)
269 271 )
270 272 error = True
271 273 self.out.write(colors.normal+'\n')
272 274 if string_output:
273 275 output = self.out.getvalue()
274 276 self.out = out_old
275 277 return (output, error)
276 278 return (None, error)
277 279
278 def __call__(self, toktype, toktext, start_pos, end_pos, line):
279 """ Token handler, with syntax highlighting."""
280 def _inner_call_(self, toktype, toktext, start_pos, end_pos, line):
281 """like call but write to a temporary buffer"""
282 buff = StringIO()
280 283 (srow,scol) = start_pos
281 284 (erow,ecol) = end_pos
282 285 colors = self.colors
283 owrite = self.out.write
286 owrite = buff.write
284 287
285 288 # line separator, so this works across platforms
286 289 linesep = os.linesep
287 290
288 291 # calculate new positions
289 292 oldpos = self.pos
290 293 newpos = self.lines[srow] + scol
291 294 self.pos = newpos + len(toktext)
292 295
293 296 # send the original whitespace, if needed
294 297 if newpos > oldpos:
295 298 owrite(self.raw[oldpos:newpos])
296 299
297 300 # skip indenting tokens
298 301 if toktype in [token.INDENT, token.DEDENT]:
299 302 self.pos = newpos
300 return
303 buff.seek(0)
304 return buff.read()
301 305
302 306 # map token type to a color group
303 307 if token.LPAR <= toktype <= token.OP:
304 308 toktype = token.OP
305 309 elif toktype == token.NAME and keyword.iskeyword(toktext):
306 310 toktype = _KEYWORD
307 311 color = colors.get(toktype, colors[_TEXT])
308 312
309 313 #print '<%s>' % toktext, # dbg
310 314
311 315 # Triple quoted strings must be handled carefully so that backtracking
312 316 # in pagers works correctly. We need color terminators on _each_ line.
313 317 if linesep in toktext:
314 318 toktext = toktext.replace(linesep, '%s%s%s' %
315 319 (colors.normal,linesep,color))
316 320
317 321 # send text
318 322 owrite('%s%s%s' % (color,toktext,colors.normal))
323 buff.seek(0)
324 return buff.read()
325
326
327 def __call__(self, toktype, toktext, start_pos, end_pos, line):
328 """ Token handler, with syntax highlighting."""
329 self.out.write(
330 self._inner_call_(toktype, toktext, start_pos, end_pos, line))
331
General Comments 0
You need to be logged in to leave comments. Login now