Fix issue #138
This commit is contained in:
parent
3884398055
commit
fbc5ba08d9
|
@ -594,12 +594,36 @@ class FormattedExcinfo(object):
|
|||
break
|
||||
return ReprTraceback(entries, extraline, style=self.style)
|
||||
|
||||
def repr_excinfo(self, excinfo):
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
return ReprExceptionInfo(reprtraceback, reprcrash)
|
||||
|
||||
class TerminalRepr:
|
||||
def repr_excinfo(self, excinfo):
|
||||
if sys.version_info[0] < 3:
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
|
||||
return ReprExceptionInfo(reprtraceback, reprcrash)
|
||||
else:
|
||||
repr_chain = []
|
||||
e = excinfo.value
|
||||
descr = None
|
||||
while e is not None:
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
repr_chain += [(reprtraceback, reprcrash, descr)]
|
||||
if e.__cause__ is not None:
|
||||
e = e.__cause__
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
|
||||
descr = 'The above exception was the direct cause of the following exception:'
|
||||
elif e.__context__ is not None:
|
||||
e = e.__context__
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
|
||||
descr = 'During handling of the above exception, another exception occurred:'
|
||||
else:
|
||||
e = None
|
||||
repr_chain.reverse()
|
||||
return ExceptionChainRepr(repr_chain)
|
||||
|
||||
|
||||
class TerminalRepr(object):
|
||||
def __str__(self):
|
||||
s = self.__unicode__()
|
||||
if sys.version_info[0] < 3:
|
||||
|
@ -618,21 +642,47 @@ class TerminalRepr:
|
|||
return "<%s instance at %0x>" %(self.__class__, id(self))
|
||||
|
||||
|
||||
class ReprExceptionInfo(TerminalRepr):
|
||||
def __init__(self, reprtraceback, reprcrash):
|
||||
self.reprtraceback = reprtraceback
|
||||
self.reprcrash = reprcrash
|
||||
class ExceptionRepr(TerminalRepr):
|
||||
def __init__(self):
|
||||
self.sections = []
|
||||
|
||||
def addsection(self, name, content, sep="-"):
|
||||
self.sections.append((name, content, sep))
|
||||
|
||||
def toterminal(self, tw):
|
||||
self.reprtraceback.toterminal(tw)
|
||||
for name, content, sep in self.sections:
|
||||
tw.sep(sep, name)
|
||||
tw.line(content)
|
||||
|
||||
|
||||
class ExceptionChainRepr(ExceptionRepr):
|
||||
def __init__(self, chain):
|
||||
super(ExceptionChainRepr, self).__init__()
|
||||
self.chain = chain
|
||||
# reprcrash and reprtraceback of the outermost (the newest) exception
|
||||
# in the chain
|
||||
self.reprtraceback = chain[-1][0]
|
||||
self.reprcrash = chain[-1][1]
|
||||
|
||||
def toterminal(self, tw):
|
||||
for element in self.chain:
|
||||
element[0].toterminal(tw)
|
||||
if element[2] is not None:
|
||||
tw.line("")
|
||||
tw.line(element[2], yellow=True)
|
||||
super(ExceptionChainRepr, self).toterminal(tw)
|
||||
|
||||
|
||||
class ReprExceptionInfo(ExceptionRepr):
|
||||
def __init__(self, reprtraceback, reprcrash):
|
||||
super(ReprExceptionInfo, self).__init__()
|
||||
self.reprtraceback = reprtraceback
|
||||
self.reprcrash = reprcrash
|
||||
|
||||
def toterminal(self, tw):
|
||||
self.reprtraceback.toterminal(tw)
|
||||
super(ReprExceptionInfo, self).toterminal(tw)
|
||||
|
||||
class ReprTraceback(TerminalRepr):
|
||||
entrysep = "_ "
|
||||
|
||||
|
|
|
@ -491,9 +491,12 @@ def importorskip(modname, minversion=None):
|
|||
"""
|
||||
__tracebackhide__ = True
|
||||
compile(modname, '', 'eval') # to catch syntaxerrors
|
||||
should_skip = False
|
||||
try:
|
||||
__import__(modname)
|
||||
except ImportError:
|
||||
should_skip = True
|
||||
if should_skip:
|
||||
skip("could not import %r" %(modname,))
|
||||
mod = sys.modules[modname]
|
||||
if minversion is None:
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
import _pytest
|
||||
import py
|
||||
import pytest
|
||||
from _pytest._code.code import FormattedExcinfo, ReprExceptionInfo
|
||||
from _pytest._code.code import (FormattedExcinfo, ReprExceptionInfo,
|
||||
ExceptionChainRepr)
|
||||
|
||||
queue = py.builtin._tryimport('queue', 'Queue')
|
||||
|
||||
|
@ -385,6 +386,8 @@ class TestFormattedExcinfo:
|
|||
excinfo = _pytest._code.ExceptionInfo()
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
|
||||
|
||||
def test_repr_many_line_source_not_existing(self):
|
||||
pr = FormattedExcinfo()
|
||||
|
@ -398,6 +401,8 @@ raise ValueError()
|
|||
excinfo = _pytest._code.ExceptionInfo()
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
|
||||
|
||||
def test_repr_source_failing_fullsource(self):
|
||||
pr = FormattedExcinfo()
|
||||
|
@ -430,6 +435,7 @@ raise ValueError()
|
|||
|
||||
class FakeExcinfo(_pytest._code.ExceptionInfo):
|
||||
typename = "Foo"
|
||||
value = Exception()
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
@ -447,10 +453,15 @@ raise ValueError()
|
|||
fail = IOError() # noqa
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
|
||||
|
||||
|
||||
fail = py.error.ENOENT # noqa
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
|
||||
|
||||
|
||||
def test_repr_local(self):
|
||||
|
@ -637,6 +648,9 @@ raise ValueError()
|
|||
repr = p.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback
|
||||
assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert repr.chain[0][0]
|
||||
assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries)
|
||||
assert repr.reprcrash.path.endswith("mod.py")
|
||||
assert repr.reprcrash.message == "ValueError: 0"
|
||||
|
||||
|
@ -727,8 +741,13 @@ raise ValueError()
|
|||
for style in ("short", "long", "no"):
|
||||
for showlocals in (True, False):
|
||||
repr = excinfo.getrepr(style=style, showlocals=showlocals)
|
||||
assert isinstance(repr, ReprExceptionInfo)
|
||||
if py.std.sys.version_info[0] < 3:
|
||||
assert isinstance(repr, ReprExceptionInfo)
|
||||
assert repr.reprtraceback.style == style
|
||||
if py.std.sys.version_info[0] >= 3:
|
||||
assert isinstance(repr, ExceptionChainRepr)
|
||||
for repr in repr.chain:
|
||||
assert repr[0].style == style
|
||||
|
||||
def test_reprexcinfo_unicode(self):
|
||||
from _pytest._code.code import TerminalRepr
|
||||
|
@ -909,3 +928,70 @@ raise ValueError()
|
|||
assert tw.lines[14] == "E ValueError"
|
||||
assert tw.lines[15] == ""
|
||||
assert tw.lines[16].endswith("mod.py:9: ValueError")
|
||||
|
||||
@pytest.mark.skipif("sys.version_info[0] < 3")
|
||||
def test_exc_chain_repr(self, importasmod):
|
||||
mod = importasmod("""
|
||||
class Err(Exception):
|
||||
pass
|
||||
def f():
|
||||
try:
|
||||
g()
|
||||
except Exception as e:
|
||||
raise Err() from e
|
||||
finally:
|
||||
h()
|
||||
def g():
|
||||
raise ValueError()
|
||||
|
||||
def h():
|
||||
raise AttributeError()
|
||||
""")
|
||||
excinfo = pytest.raises(AttributeError, mod.f)
|
||||
r = excinfo.getrepr(style="long")
|
||||
tw = TWMock()
|
||||
r.toterminal(tw)
|
||||
for line in tw.lines: print (line)
|
||||
assert tw.lines[0] == ""
|
||||
assert tw.lines[1] == " def f():"
|
||||
assert tw.lines[2] == " try:"
|
||||
assert tw.lines[3] == "> g()"
|
||||
assert tw.lines[4] == ""
|
||||
assert tw.lines[5].endswith("mod.py:6: ")
|
||||
assert tw.lines[6] == ("_ ", None)
|
||||
assert tw.lines[7] == ""
|
||||
assert tw.lines[8] == " def g():"
|
||||
assert tw.lines[9] == "> raise ValueError()"
|
||||
assert tw.lines[10] == "E ValueError"
|
||||
assert tw.lines[11] == ""
|
||||
assert tw.lines[12].endswith("mod.py:12: ValueError")
|
||||
assert tw.lines[13] == ""
|
||||
assert tw.lines[14] == "The above exception was the direct cause of the following exception:"
|
||||
assert tw.lines[15] == ""
|
||||
assert tw.lines[16] == " def f():"
|
||||
assert tw.lines[17] == " try:"
|
||||
assert tw.lines[18] == " g()"
|
||||
assert tw.lines[19] == " except Exception as e:"
|
||||
assert tw.lines[20] == "> raise Err() from e"
|
||||
assert tw.lines[21] == "E test_exc_chain_repr0.mod.Err"
|
||||
assert tw.lines[22] == ""
|
||||
assert tw.lines[23].endswith("mod.py:8: Err")
|
||||
assert tw.lines[24] == ""
|
||||
assert tw.lines[25] == "During handling of the above exception, another exception occurred:"
|
||||
assert tw.lines[26] == ""
|
||||
assert tw.lines[27] == " def f():"
|
||||
assert tw.lines[28] == " try:"
|
||||
assert tw.lines[29] == " g()"
|
||||
assert tw.lines[30] == " except Exception as e:"
|
||||
assert tw.lines[31] == " raise Err() from e"
|
||||
assert tw.lines[32] == " finally:"
|
||||
assert tw.lines[33] == "> h()"
|
||||
assert tw.lines[34] == ""
|
||||
assert tw.lines[35].endswith("mod.py:10: ")
|
||||
assert tw.lines[36] == ('_ ', None)
|
||||
assert tw.lines[37] == ""
|
||||
assert tw.lines[38] == " def h():"
|
||||
assert tw.lines[39] == "> raise AttributeError()"
|
||||
assert tw.lines[40] == "E AttributeError"
|
||||
assert tw.lines[41] == ""
|
||||
assert tw.lines[42].endswith("mod.py:15: AttributeError")
|
||||
|
|
Loading…
Reference in New Issue