diff options
Diffstat (limited to 'Lib/unittest/result.py')
-rw-r--r-- | Lib/unittest/result.py | 53 |
1 files changed, 39 insertions, 14 deletions
diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index ce7468e..3da7005 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -173,18 +173,10 @@ class TestResult(object): def _exc_info_to_string(self, err, test): """Converts a sys.exc_info()-style tuple of values into a string.""" exctype, value, tb = err - # Skip test runner traceback levels - while tb and self._is_relevant_tb_level(tb): - tb = tb.tb_next - - if exctype is test.failureException: - # Skip assert*() traceback levels - length = self._count_relevant_tb_levels(tb) - else: - length = None + tb = self._clean_tracebacks(exctype, value, tb, test) tb_e = traceback.TracebackException( exctype, value, tb, - limit=length, capture_locals=self.tb_locals, compact=True) + capture_locals=self.tb_locals, compact=True) msgLines = list(tb_e.format()) if self.buffer: @@ -200,16 +192,49 @@ class TestResult(object): msgLines.append(STDERR_LINE % error) return ''.join(msgLines) + def _clean_tracebacks(self, exctype, value, tb, test): + ret = None + first = True + excs = [(exctype, value, tb)] + while excs: + (exctype, value, tb) = excs.pop() + # Skip test runner traceback levels + while tb and self._is_relevant_tb_level(tb): + tb = tb.tb_next + + # Skip assert*() traceback levels + if exctype is test.failureException: + self._remove_unittest_tb_frames(tb) + + if first: + ret = tb + first = False + else: + value.__traceback__ = tb + + if value is not None: + for c in (value.__cause__, value.__context__): + if c is not None: + excs.append((type(c), c, c.__traceback__)) + return ret def _is_relevant_tb_level(self, tb): return '__unittest' in tb.tb_frame.f_globals - def _count_relevant_tb_levels(self, tb): - length = 0 + def _remove_unittest_tb_frames(self, tb): + '''Truncates usercode tb at the first unittest frame. + + If the first frame of the traceback is in user code, + the prefix up to the first unittest frame is returned. + If the first frame is already in the unittest module, + the traceback is not modified. + ''' + prev = None while tb and not self._is_relevant_tb_level(tb): - length += 1 + prev = tb tb = tb.tb_next - return length + if prev is not None: + prev.tb_next = None def __repr__(self): return ("<%s run=%i errors=%i failures=%i>" % |