1"""Extract, format and print information about Python stack traces.""" 2 3import collections 4import itertools 5import linecache 6import sys 7 8__all__ = ['extract_stack', 'extract_tb', 'format_exception', 9 'format_exception_only', 'format_list', 'format_stack', 10 'format_tb', 'print_exc', 'format_exc', 'print_exception', 11 'print_last', 'print_stack', 'print_tb', 'clear_frames', 12 'FrameSummary', 'StackSummary', 'TracebackException', 13 'walk_stack', 'walk_tb'] 14 15# 16# Formatting and printing lists of traceback lines. 17# 18 19def print_list(extracted_list, file=None): 20 """Print the list of tuples as returned by extract_tb() or 21 extract_stack() as a formatted stack trace to the given file.""" 22 if file is None: 23 file = sys.stderr 24 for item in StackSummary.from_list(extracted_list).format(): 25 print(item, file=file, end="") 26 27def format_list(extracted_list): 28 """Format a list of tuples or FrameSummary objects for printing. 29 30 Given a list of tuples or FrameSummary objects as returned by 31 extract_tb() or extract_stack(), return a list of strings ready 32 for printing. 33 34 Each string in the resulting list corresponds to the item with the 35 same index in the argument list. Each string ends in a newline; 36 the strings may contain internal newlines as well, for those items 37 whose source text line is not None. 38 """ 39 return StackSummary.from_list(extracted_list).format() 40 41# 42# Printing and Extracting Tracebacks. 43# 44 45def print_tb(tb, limit=None, file=None): 46 """Print up to 'limit' stack trace entries from the traceback 'tb'. 47 48 If 'limit' is omitted or None, all entries are printed. If 'file' 49 is omitted or None, the output goes to sys.stderr; otherwise 50 'file' should be an open file or file-like object with a write() 51 method. 52 """ 53 print_list(extract_tb(tb, limit=limit), file=file) 54 55def format_tb(tb, limit=None): 56 """A shorthand for 'format_list(extract_tb(tb, limit))'.""" 57 return extract_tb(tb, limit=limit).format() 58 59def extract_tb(tb, limit=None): 60 """ 61 Return a StackSummary object representing a list of 62 pre-processed entries from traceback. 63 64 This is useful for alternate formatting of stack traces. If 65 'limit' is omitted or None, all entries are extracted. A 66 pre-processed stack trace entry is a FrameSummary object 67 containing attributes filename, lineno, name, and line 68 representing the information that is usually printed for a stack 69 trace. The line is a string with leading and trailing 70 whitespace stripped; if the source is not available it is None. 71 """ 72 return StackSummary.extract(walk_tb(tb), limit=limit) 73 74# 75# Exception formatting and output. 76# 77 78_cause_message = ( 79 "\nThe above exception was the direct cause " 80 "of the following exception:\n\n") 81 82_context_message = ( 83 "\nDuring handling of the above exception, " 84 "another exception occurred:\n\n") 85 86 87class _Sentinel: 88 def __repr__(self): 89 return "<implicit>" 90 91_sentinel = _Sentinel() 92 93def _parse_value_tb(exc, value, tb): 94 if (value is _sentinel) != (tb is _sentinel): 95 raise ValueError("Both or neither of value and tb must be given") 96 if value is tb is _sentinel: 97 if exc is not None: 98 return exc, exc.__traceback__ 99 else: 100 return None, None 101 return value, tb 102 103 104def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ 105 file=None, chain=True): 106 """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. 107 108 This differs from print_tb() in the following ways: (1) if 109 traceback is not None, it prints a header "Traceback (most recent 110 call last):"; (2) it prints the exception type and value after the 111 stack trace; (3) if type is SyntaxError and value has the 112 appropriate format, it prints the line where the syntax error 113 occurred with a caret on the next line indicating the approximate 114 position of the error. 115 """ 116 value, tb = _parse_value_tb(exc, value, tb) 117 if file is None: 118 file = sys.stderr 119 te = TracebackException(type(value), value, tb, limit=limit, compact=True) 120 for line in te.format(chain=chain): 121 print(line, file=file, end="") 122 123 124def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ 125 chain=True): 126 """Format a stack trace and the exception information. 127 128 The arguments have the same meaning as the corresponding arguments 129 to print_exception(). The return value is a list of strings, each 130 ending in a newline and some containing internal newlines. When 131 these lines are concatenated and printed, exactly the same text is 132 printed as does print_exception(). 133 """ 134 value, tb = _parse_value_tb(exc, value, tb) 135 te = TracebackException(type(value), value, tb, limit=limit, compact=True) 136 return list(te.format(chain=chain)) 137 138 139def format_exception_only(exc, /, value=_sentinel): 140 """Format the exception part of a traceback. 141 142 The return value is a list of strings, each ending in a newline. 143 144 Normally, the list contains a single string; however, for 145 SyntaxError exceptions, it contains several lines that (when 146 printed) display detailed information about where the syntax 147 error occurred. 148 149 The message indicating which exception occurred is always the last 150 string in the list. 151 152 """ 153 if value is _sentinel: 154 value = exc 155 te = TracebackException(type(value), value, None, compact=True) 156 return list(te.format_exception_only()) 157 158 159# -- not official API but folk probably use these two functions. 160 161def _format_final_exc_line(etype, value): 162 valuestr = _some_str(value) 163 if value is None or not valuestr: 164 line = "%s\n" % etype 165 else: 166 line = "%s: %s\n" % (etype, valuestr) 167 return line 168 169def _some_str(value): 170 try: 171 return str(value) 172 except: 173 return '<unprintable %s object>' % type(value).__name__ 174 175# -- 176 177def print_exc(limit=None, file=None, chain=True): 178 """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'.""" 179 print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) 180 181def format_exc(limit=None, chain=True): 182 """Like print_exc() but return a string.""" 183 return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) 184 185def print_last(limit=None, file=None, chain=True): 186 """This is a shorthand for 'print_exception(sys.last_type, 187 sys.last_value, sys.last_traceback, limit, file)'.""" 188 if not hasattr(sys, "last_type"): 189 raise ValueError("no last exception") 190 print_exception(sys.last_type, sys.last_value, sys.last_traceback, 191 limit, file, chain) 192 193# 194# Printing and Extracting Stacks. 195# 196 197def print_stack(f=None, limit=None, file=None): 198 """Print a stack trace from its invocation point. 199 200 The optional 'f' argument can be used to specify an alternate 201 stack frame at which to start. The optional 'limit' and 'file' 202 arguments have the same meaning as for print_exception(). 203 """ 204 if f is None: 205 f = sys._getframe().f_back 206 print_list(extract_stack(f, limit=limit), file=file) 207 208 209def format_stack(f=None, limit=None): 210 """Shorthand for 'format_list(extract_stack(f, limit))'.""" 211 if f is None: 212 f = sys._getframe().f_back 213 return format_list(extract_stack(f, limit=limit)) 214 215 216def extract_stack(f=None, limit=None): 217 """Extract the raw traceback from the current stack frame. 218 219 The return value has the same format as for extract_tb(). The 220 optional 'f' and 'limit' arguments have the same meaning as for 221 print_stack(). Each item in the list is a quadruple (filename, 222 line number, function name, text), and the entries are in order 223 from oldest to newest stack frame. 224 """ 225 if f is None: 226 f = sys._getframe().f_back 227 stack = StackSummary.extract(walk_stack(f), limit=limit) 228 stack.reverse() 229 return stack 230 231 232def clear_frames(tb): 233 "Clear all references to local variables in the frames of a traceback." 234 while tb is not None: 235 try: 236 tb.tb_frame.clear() 237 except RuntimeError: 238 # Ignore the exception raised if the frame is still executing. 239 pass 240 tb = tb.tb_next 241 242 243class FrameSummary: 244 """A single frame from a traceback. 245 246 - :attr:`filename` The filename for the frame. 247 - :attr:`lineno` The line within filename for the frame that was 248 active when the frame was captured. 249 - :attr:`name` The name of the function or method that was executing 250 when the frame was captured. 251 - :attr:`line` The text from the linecache module for the 252 of code that was running when the frame was captured. 253 - :attr:`locals` Either None if locals were not supplied, or a dict 254 mapping the name to the repr() of the variable. 255 """ 256 257 __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') 258 259 def __init__(self, filename, lineno, name, *, lookup_line=True, 260 locals=None, line=None): 261 """Construct a FrameSummary. 262 263 :param lookup_line: If True, `linecache` is consulted for the source 264 code line. Otherwise, the line will be looked up when first needed. 265 :param locals: If supplied the frame locals, which will be captured as 266 object representations. 267 :param line: If provided, use this instead of looking up the line in 268 the linecache. 269 """ 270 self.filename = filename 271 self.lineno = lineno 272 self.name = name 273 self._line = line 274 if lookup_line: 275 self.line 276 self.locals = {k: repr(v) for k, v in locals.items()} if locals else None 277 278 def __eq__(self, other): 279 if isinstance(other, FrameSummary): 280 return (self.filename == other.filename and 281 self.lineno == other.lineno and 282 self.name == other.name and 283 self.locals == other.locals) 284 if isinstance(other, tuple): 285 return (self.filename, self.lineno, self.name, self.line) == other 286 return NotImplemented 287 288 def __getitem__(self, pos): 289 return (self.filename, self.lineno, self.name, self.line)[pos] 290 291 def __iter__(self): 292 return iter([self.filename, self.lineno, self.name, self.line]) 293 294 def __repr__(self): 295 return "<FrameSummary file {filename}, line {lineno} in {name}>".format( 296 filename=self.filename, lineno=self.lineno, name=self.name) 297 298 def __len__(self): 299 return 4 300 301 @property 302 def line(self): 303 if self._line is None: 304 if self.lineno is None: 305 return None 306 self._line = linecache.getline(self.filename, self.lineno) 307 return self._line.strip() 308 309def walk_stack(f): 310 """Walk a stack yielding the frame and line number for each frame. 311 312 This will follow f.f_back from the given frame. If no frame is given, the 313 current stack is used. Usually used with StackSummary.extract. 314 """ 315 if f is None: 316 f = sys._getframe().f_back.f_back 317 while f is not None: 318 yield f, f.f_lineno 319 f = f.f_back 320 321 322def walk_tb(tb): 323 """Walk a traceback yielding the frame and line number for each frame. 324 325 This will follow tb.tb_next (and thus is in the opposite order to 326 walk_stack). Usually used with StackSummary.extract. 327 """ 328 while tb is not None: 329 yield tb.tb_frame, tb.tb_lineno 330 tb = tb.tb_next 331 332 333_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. 334 335class StackSummary(list): 336 """A stack of frames.""" 337 338 @classmethod 339 def extract(klass, frame_gen, *, limit=None, lookup_lines=True, 340 capture_locals=False): 341 """Create a StackSummary from a traceback or stack object. 342 343 :param frame_gen: A generator that yields (frame, lineno) tuples to 344 include in the stack. 345 :param limit: None to include all frames or the number of frames to 346 include. 347 :param lookup_lines: If True, lookup lines for each frame immediately, 348 otherwise lookup is deferred until the frame is rendered. 349 :param capture_locals: If True, the local variables from each frame will 350 be captured as object representations into the FrameSummary. 351 """ 352 if limit is None: 353 limit = getattr(sys, 'tracebacklimit', None) 354 if limit is not None and limit < 0: 355 limit = 0 356 if limit is not None: 357 if limit >= 0: 358 frame_gen = itertools.islice(frame_gen, limit) 359 else: 360 frame_gen = collections.deque(frame_gen, maxlen=-limit) 361 362 result = klass() 363 fnames = set() 364 for f, lineno in frame_gen: 365 co = f.f_code 366 filename = co.co_filename 367 name = co.co_name 368 369 fnames.add(filename) 370 linecache.lazycache(filename, f.f_globals) 371 # Must defer line lookups until we have called checkcache. 372 if capture_locals: 373 f_locals = f.f_locals 374 else: 375 f_locals = None 376 result.append(FrameSummary( 377 filename, lineno, name, lookup_line=False, locals=f_locals)) 378 for filename in fnames: 379 linecache.checkcache(filename) 380 # If immediate lookup was desired, trigger lookups now. 381 if lookup_lines: 382 for f in result: 383 f.line 384 return result 385 386 @classmethod 387 def from_list(klass, a_list): 388 """ 389 Create a StackSummary object from a supplied list of 390 FrameSummary objects or old-style list of tuples. 391 """ 392 # While doing a fast-path check for isinstance(a_list, StackSummary) is 393 # appealing, idlelib.run.cleanup_traceback and other similar code may 394 # break this by making arbitrary frames plain tuples, so we need to 395 # check on a frame by frame basis. 396 result = StackSummary() 397 for frame in a_list: 398 if isinstance(frame, FrameSummary): 399 result.append(frame) 400 else: 401 filename, lineno, name, line = frame 402 result.append(FrameSummary(filename, lineno, name, line=line)) 403 return result 404 405 def format(self): 406 """Format the stack ready for printing. 407 408 Returns a list of strings ready for printing. Each string in the 409 resulting list corresponds to a single frame from the stack. 410 Each string ends in a newline; the strings may contain internal 411 newlines as well, for those items with source text lines. 412 413 For long sequences of the same frame and line, the first few 414 repetitions are shown, followed by a summary line stating the exact 415 number of further repetitions. 416 """ 417 result = [] 418 last_file = None 419 last_line = None 420 last_name = None 421 count = 0 422 for frame in self: 423 if (last_file is None or last_file != frame.filename or 424 last_line is None or last_line != frame.lineno or 425 last_name is None or last_name != frame.name): 426 if count > _RECURSIVE_CUTOFF: 427 count -= _RECURSIVE_CUTOFF 428 result.append( 429 f' [Previous line repeated {count} more ' 430 f'time{"s" if count > 1 else ""}]\n' 431 ) 432 last_file = frame.filename 433 last_line = frame.lineno 434 last_name = frame.name 435 count = 0 436 count += 1 437 if count > _RECURSIVE_CUTOFF: 438 continue 439 row = [] 440 row.append(' File "{}", line {}, in {}\n'.format( 441 frame.filename, frame.lineno, frame.name)) 442 if frame.line: 443 row.append(' {}\n'.format(frame.line.strip())) 444 if frame.locals: 445 for name, value in sorted(frame.locals.items()): 446 row.append(' {name} = {value}\n'.format(name=name, value=value)) 447 result.append(''.join(row)) 448 if count > _RECURSIVE_CUTOFF: 449 count -= _RECURSIVE_CUTOFF 450 result.append( 451 f' [Previous line repeated {count} more ' 452 f'time{"s" if count > 1 else ""}]\n' 453 ) 454 return result 455 456 457class TracebackException: 458 """An exception ready for rendering. 459 460 The traceback module captures enough attributes from the original exception 461 to this intermediary form to ensure that no references are held, while 462 still being able to fully print or format it. 463 464 Use `from_exception` to create TracebackException instances from exception 465 objects, or the constructor to create TracebackException instances from 466 individual components. 467 468 - :attr:`__cause__` A TracebackException of the original *__cause__*. 469 - :attr:`__context__` A TracebackException of the original *__context__*. 470 - :attr:`__suppress_context__` The *__suppress_context__* value from the 471 original exception. 472 - :attr:`stack` A `StackSummary` representing the traceback. 473 - :attr:`exc_type` The class of the original traceback. 474 - :attr:`filename` For syntax errors - the filename where the error 475 occurred. 476 - :attr:`lineno` For syntax errors - the linenumber where the error 477 occurred. 478 - :attr:`end_lineno` For syntax errors - the end linenumber where the error 479 occurred. Can be `None` if not present. 480 - :attr:`text` For syntax errors - the text where the error 481 occurred. 482 - :attr:`offset` For syntax errors - the offset into the text where the 483 error occurred. 484 - :attr:`end_offset` For syntax errors - the offset into the text where the 485 error occurred. Can be `None` if not present. 486 - :attr:`msg` For syntax errors - the compiler error message. 487 """ 488 489 def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, 490 lookup_lines=True, capture_locals=False, compact=False, 491 _seen=None): 492 # NB: we need to accept exc_traceback, exc_value, exc_traceback to 493 # permit backwards compat with the existing API, otherwise we 494 # need stub thunk objects just to glue it together. 495 # Handle loops in __cause__ or __context__. 496 is_recursive_call = _seen is not None 497 if _seen is None: 498 _seen = set() 499 _seen.add(id(exc_value)) 500 501 # TODO: locals. 502 self.stack = StackSummary.extract( 503 walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, 504 capture_locals=capture_locals) 505 self.exc_type = exc_type 506 # Capture now to permit freeing resources: only complication is in the 507 # unofficial API _format_final_exc_line 508 self._str = _some_str(exc_value) 509 if exc_type and issubclass(exc_type, SyntaxError): 510 # Handle SyntaxError's specially 511 self.filename = exc_value.filename 512 lno = exc_value.lineno 513 self.lineno = str(lno) if lno is not None else None 514 end_lno = exc_value.end_lineno 515 self.end_lineno = str(end_lno) if end_lno is not None else None 516 self.text = exc_value.text 517 self.offset = exc_value.offset 518 self.end_offset = exc_value.end_offset 519 self.msg = exc_value.msg 520 if lookup_lines: 521 self._load_lines() 522 self.__suppress_context__ = \ 523 exc_value.__suppress_context__ if exc_value is not None else False 524 525 # Convert __cause__ and __context__ to `TracebackExceptions`s, use a 526 # queue to avoid recursion (only the top-level call gets _seen == None) 527 if not is_recursive_call: 528 queue = [(self, exc_value)] 529 while queue: 530 te, e = queue.pop() 531 if (e and e.__cause__ is not None 532 and id(e.__cause__) not in _seen): 533 cause = TracebackException( 534 type(e.__cause__), 535 e.__cause__, 536 e.__cause__.__traceback__, 537 limit=limit, 538 lookup_lines=lookup_lines, 539 capture_locals=capture_locals, 540 _seen=_seen) 541 else: 542 cause = None 543 544 if compact: 545 need_context = (cause is None and 546 e is not None and 547 not e.__suppress_context__) 548 else: 549 need_context = True 550 if (e and e.__context__ is not None 551 and need_context and id(e.__context__) not in _seen): 552 context = TracebackException( 553 type(e.__context__), 554 e.__context__, 555 e.__context__.__traceback__, 556 limit=limit, 557 lookup_lines=lookup_lines, 558 capture_locals=capture_locals, 559 _seen=_seen) 560 else: 561 context = None 562 te.__cause__ = cause 563 te.__context__ = context 564 if cause: 565 queue.append((te.__cause__, e.__cause__)) 566 if context: 567 queue.append((te.__context__, e.__context__)) 568 569 @classmethod 570 def from_exception(cls, exc, *args, **kwargs): 571 """Create a TracebackException from an exception.""" 572 return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) 573 574 def _load_lines(self): 575 """Private API. force all lines in the stack to be loaded.""" 576 for frame in self.stack: 577 frame.line 578 579 def __eq__(self, other): 580 if isinstance(other, TracebackException): 581 return self.__dict__ == other.__dict__ 582 return NotImplemented 583 584 def __str__(self): 585 return self._str 586 587 def format_exception_only(self): 588 """Format the exception part of the traceback. 589 590 The return value is a generator of strings, each ending in a newline. 591 592 Normally, the generator emits a single string; however, for 593 SyntaxError exceptions, it emits several lines that (when 594 printed) display detailed information about where the syntax 595 error occurred. 596 597 The message indicating which exception occurred is always the last 598 string in the output. 599 """ 600 if self.exc_type is None: 601 yield _format_final_exc_line(None, self._str) 602 return 603 604 stype = self.exc_type.__qualname__ 605 smod = self.exc_type.__module__ 606 if smod not in ("__main__", "builtins"): 607 if not isinstance(smod, str): 608 smod = "<unknown>" 609 stype = smod + '.' + stype 610 611 if not issubclass(self.exc_type, SyntaxError): 612 yield _format_final_exc_line(stype, self._str) 613 else: 614 yield from self._format_syntax_error(stype) 615 616 def _format_syntax_error(self, stype): 617 """Format SyntaxError exceptions (internal helper).""" 618 # Show exactly where the problem was found. 619 filename_suffix = '' 620 if self.lineno is not None: 621 yield ' File "{}", line {}\n'.format( 622 self.filename or "<string>", self.lineno) 623 elif self.filename is not None: 624 filename_suffix = ' ({})'.format(self.filename) 625 626 text = self.text 627 if text is not None: 628 # text = " foo\n" 629 # rtext = " foo" 630 # ltext = "foo" 631 rtext = text.rstrip('\n') 632 ltext = rtext.lstrip(' \n\f') 633 spaces = len(rtext) - len(ltext) 634 yield ' {}\n'.format(ltext) 635 636 if self.offset is not None: 637 offset = self.offset 638 end_offset = self.end_offset if self.end_offset not in {None, 0} else offset 639 if offset == end_offset or end_offset == -1: 640 end_offset = offset + 1 641 642 # Convert 1-based column offset to 0-based index into stripped text 643 colno = offset - 1 - spaces 644 end_colno = end_offset - 1 - spaces 645 if colno >= 0: 646 # non-space whitespace (likes tabs) must be kept for alignment 647 caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno]) 648 yield ' {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n")) 649 msg = self.msg or "<no detail available>" 650 yield "{}: {}{}\n".format(stype, msg, filename_suffix) 651 652 def format(self, *, chain=True): 653 """Format the exception. 654 655 If chain is not *True*, *__cause__* and *__context__* will not be formatted. 656 657 The return value is a generator of strings, each ending in a newline and 658 some containing internal newlines. `print_exception` is a wrapper around 659 this method which just prints the lines to a file. 660 661 The message indicating which exception occurred is always the last 662 string in the output. 663 """ 664 665 output = [] 666 exc = self 667 while exc: 668 if chain: 669 if exc.__cause__ is not None: 670 chained_msg = _cause_message 671 chained_exc = exc.__cause__ 672 elif (exc.__context__ is not None and 673 not exc.__suppress_context__): 674 chained_msg = _context_message 675 chained_exc = exc.__context__ 676 else: 677 chained_msg = None 678 chained_exc = None 679 680 output.append((chained_msg, exc)) 681 exc = chained_exc 682 else: 683 output.append((None, exc)) 684 exc = None 685 686 for msg, exc in reversed(output): 687 if msg is not None: 688 yield msg 689 if exc.stack: 690 yield 'Traceback (most recent call last):\n' 691 yield from exc.stack.format() 692 yield from exc.format_exception_only() 693