A list of FrameSummary objects, representing a stack of frames.
| 367 | _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. |
| 368 | |
| 369 | class StackSummary(list): |
| 370 | """A list of FrameSummary objects, representing a stack of frames.""" |
| 371 | |
| 372 | @classmethod |
| 373 | def extract(klass, frame_gen, *, limit=None, lookup_lines=True, |
| 374 | capture_locals=False): |
| 375 | """Create a StackSummary from a traceback or stack object. |
| 376 | |
| 377 | :param frame_gen: A generator that yields (frame, lineno) tuples |
| 378 | whose summaries are to be included in the stack. |
| 379 | :param limit: None to include all frames or the number of frames to |
| 380 | include. |
| 381 | :param lookup_lines: If True, lookup lines for each frame immediately, |
| 382 | otherwise lookup is deferred until the frame is rendered. |
| 383 | :param capture_locals: If True, the local variables from each frame will |
| 384 | be captured as object representations into the FrameSummary. |
| 385 | """ |
| 386 | def extended_frame_gen(): |
| 387 | for f, lineno in frame_gen: |
| 388 | yield f, (lineno, None, None, None) |
| 389 | |
| 390 | return klass._extract_from_extended_frame_gen( |
| 391 | extended_frame_gen(), limit=limit, lookup_lines=lookup_lines, |
| 392 | capture_locals=capture_locals) |
| 393 | |
| 394 | @classmethod |
| 395 | def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None, |
| 396 | lookup_lines=True, capture_locals=False): |
| 397 | # Same as extract but operates on a frame generator that yields |
| 398 | # (frame, (lineno, end_lineno, colno, end_colno)) in the stack. |
| 399 | # Only lineno is required, the remaining fields can be None if the |
| 400 | # information is not available. |
| 401 | if limit is None: |
| 402 | limit = getattr(sys, 'tracebacklimit', None) |
| 403 | if limit is not None and limit < 0: |
| 404 | limit = 0 |
| 405 | if limit is not None: |
| 406 | if limit >= 0: |
| 407 | frame_gen = itertools.islice(frame_gen, limit) |
| 408 | else: |
| 409 | frame_gen = collections.deque(frame_gen, maxlen=-limit) |
| 410 | |
| 411 | result = klass() |
| 412 | fnames = set() |
| 413 | for f, (lineno, end_lineno, colno, end_colno) in frame_gen: |
| 414 | co = f.f_code |
| 415 | filename = co.co_filename |
| 416 | name = co.co_name |
| 417 | |
| 418 | fnames.add(filename) |
| 419 | linecache.lazycache(filename, f.f_globals) |
| 420 | # Must defer line lookups until we have called checkcache. |
| 421 | if capture_locals: |
| 422 | f_locals = f.f_locals |
| 423 | else: |
| 424 | f_locals = None |
| 425 | result.append(FrameSummary( |
| 426 | filename, lineno, name, lookup_line=False, locals=f_locals, |