|
3 | 3 | from inspect import CO_VARARGS, CO_VARKEYWORDS
|
4 | 4 | import re
|
5 | 5 | from weakref import ref
|
6 |
| -from _pytest.compat import _PY2, _PY3, PY35 |
| 6 | +from _pytest.compat import _PY2, _PY3, PY35, safe_str |
7 | 7 |
|
8 | 8 | import py
|
9 | 9 | builtin_repr = repr
|
@@ -602,21 +602,48 @@ def repr_traceback(self, excinfo):
|
602 | 602 | traceback = excinfo.traceback
|
603 | 603 | if self.tbfilter:
|
604 | 604 | traceback = traceback.filter()
|
605 |
| - recursionindex = None |
| 605 | + |
606 | 606 | if is_recursion_error(excinfo):
|
607 |
| - recursionindex = traceback.recursionindex() |
| 607 | + traceback, extraline = self._truncate_recursive_traceback(traceback) |
| 608 | + else: |
| 609 | + extraline = None |
| 610 | + |
608 | 611 | last = traceback[-1]
|
609 | 612 | entries = []
|
610 |
| - extraline = None |
611 | 613 | for index, entry in enumerate(traceback):
|
612 | 614 | einfo = (last == entry) and excinfo or None
|
613 | 615 | reprentry = self.repr_traceback_entry(entry, einfo)
|
614 | 616 | entries.append(reprentry)
|
615 |
| - if index == recursionindex: |
616 |
| - extraline = "!!! Recursion detected (same locals & position)" |
617 |
| - break |
618 | 617 | return ReprTraceback(entries, extraline, style=self.style)
|
619 | 618 |
|
| 619 | + def _truncate_recursive_traceback(self, traceback): |
| 620 | + """ |
| 621 | + Truncate the given recursive traceback trying to find the starting point |
| 622 | + of the recursion. |
| 623 | +
|
| 624 | + The detection is done by going through each traceback entry and finding the |
| 625 | + point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``. |
| 626 | +
|
| 627 | + Handle the situation where the recursion process might raise an exception (for example |
| 628 | + comparing numpy arrays using equality raises a TypeError), in which case we do our best to |
| 629 | + warn the user of the error and show a limited traceback. |
| 630 | + """ |
| 631 | + try: |
| 632 | + recursionindex = traceback.recursionindex() |
| 633 | + except Exception as e: |
| 634 | + max_frames = 10 |
| 635 | + extraline = ( |
| 636 | + '!!! Recursion error detected, but an error occurred locating the origin of recursion.\n' |
| 637 | + ' The following exception happened when comparing locals in the stack frame:\n' |
| 638 | + ' {exc_type}: {exc_msg}\n' |
| 639 | + ' Displaying first and last {max_frames} stack frames out of {total}.' |
| 640 | + ).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback)) |
| 641 | + traceback = traceback[:max_frames] + traceback[-max_frames:] |
| 642 | + else: |
| 643 | + extraline = "!!! Recursion detected (same locals & position)" |
| 644 | + traceback = traceback[:recursionindex + 1] |
| 645 | + |
| 646 | + return traceback, extraline |
620 | 647 |
|
621 | 648 | def repr_excinfo(self, excinfo):
|
622 | 649 | if _PY2:
|
|
0 commit comments