summaryrefslogtreecommitdiff
path: root/_pytest/_code/code.py
diff options
context:
space:
mode:
authorBruno Oliveira <nicoddemus@gmail.com>2017-06-06 12:41:15 -0300
committerBruno Oliveira <nicoddemus@gmail.com>2017-06-07 14:40:13 -0300
commit2127a2378ad92bf0483f7659524027ab3c49ab15 (patch)
tree6eef72e4a17aa7f2a9a35c05f93c30b5e3bb2c88 /_pytest/_code/code.py
parent57e2ced96933e15a4428eb5a74143d470cb94084 (diff)
downloadpytest-2127a2378ad92bf0483f7659524027ab3c49ab15.tar.gz
Fix internal error with recursive tracebacks with that frames contain objects that can't be compared
Fix #2459
Diffstat (limited to '_pytest/_code/code.py')
-rw-r--r--_pytest/_code/code.py41
1 files changed, 34 insertions, 7 deletions
diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py
index f872dba0b..9b3408dc4 100644
--- a/_pytest/_code/code.py
+++ b/_pytest/_code/code.py
@@ -3,7 +3,7 @@ import sys
from inspect import CO_VARARGS, CO_VARKEYWORDS
import re
from weakref import ref
-from _pytest.compat import _PY2, _PY3, PY35
+from _pytest.compat import _PY2, _PY3, PY35, safe_str
import py
builtin_repr = repr
@@ -602,21 +602,48 @@ class FormattedExcinfo(object):
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
- recursionindex = None
+
if is_recursion_error(excinfo):
- recursionindex = traceback.recursionindex()
+ traceback, extraline = self._truncate_recursive_traceback(traceback)
+ else:
+ extraline = None
+
last = traceback[-1]
entries = []
- extraline = None
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
- if index == recursionindex:
- extraline = "!!! Recursion detected (same locals & position)"
- break
return ReprTraceback(entries, extraline, style=self.style)
+ def _truncate_recursive_traceback(self, traceback):
+ """
+ Truncate the given recursive traceback trying to find the starting point
+ of the recursion.
+
+ The detection is done by going through each traceback entry and finding the
+ point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``.
+
+ Handle the situation where the recursion process might raise an exception (for example
+ comparing numpy arrays using equality raises a TypeError), in which case we do our best to
+ warn the user of the error and show a limited traceback.
+ """
+ try:
+ recursionindex = traceback.recursionindex()
+ except Exception as e:
+ max_frames = 10
+ extraline = (
+ '!!! Recursion error detected, but an error occurred locating the origin of recursion.\n'
+ ' The following exception happened when comparing locals in the stack frame:\n'
+ ' {exc_type}: {exc_msg}\n'
+ ' Displaying first and last {max_frames} stack frames out of {total}.'
+ ).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback))
+ traceback = traceback[:max_frames] + traceback[-max_frames:]
+ else:
+ extraline = "!!! Recursion detected (same locals & position)"
+ traceback = traceback[:recursionindex + 1]
+
+ return traceback, extraline
def repr_excinfo(self, excinfo):
if _PY2: