Identify the repeating stack frames from a RecursionError traceback 'records' is a list as returned by VerboseTB.get_records() Returns (last_unique, repeat_length)
(etype, value, records)
| 437 | and len(records) > _FRAME_RECURSION_LIMIT |
| 438 | |
| 439 | def find_recursion(etype, value, records): |
| 440 | """Identify the repeating stack frames from a RecursionError traceback |
| 441 | |
| 442 | 'records' is a list as returned by VerboseTB.get_records() |
| 443 | |
| 444 | Returns (last_unique, repeat_length) |
| 445 | """ |
| 446 | # This involves a bit of guesswork - we want to show enough of the traceback |
| 447 | # to indicate where the recursion is occurring. We guess that the innermost |
| 448 | # quarter of the traceback (250 frames by default) is repeats, and find the |
| 449 | # first frame (from in to out) that looks different. |
| 450 | if not is_recursion_error(etype, value, records): |
| 451 | return len(records), 0 |
| 452 | |
| 453 | # Select filename, lineno, func_name to track frames with |
| 454 | records = [r[1:4] for r in records] |
| 455 | inner_frames = records[-(len(records)//4):] |
| 456 | frames_repeated = set(inner_frames) |
| 457 | |
| 458 | last_seen_at = {} |
| 459 | longest_repeat = 0 |
| 460 | i = len(records) |
| 461 | for frame in reversed(records): |
| 462 | i -= 1 |
| 463 | if frame not in frames_repeated: |
| 464 | last_unique = i |
| 465 | break |
| 466 | |
| 467 | if frame in last_seen_at: |
| 468 | distance = last_seen_at[frame] - i |
| 469 | longest_repeat = max(longest_repeat, distance) |
| 470 | |
| 471 | last_seen_at[frame] = i |
| 472 | else: |
| 473 | last_unique = 0 # The whole traceback was recursion |
| 474 | |
| 475 | return last_unique, longest_repeat |
| 476 | |
| 477 | #--------------------------------------------------------------------------- |
| 478 | # Module classes |