diff --git a/changelog/4386.bugfix.rst b/changelog/4386.bugfix.rst new file mode 100644 index 00000000000..0367f3ddc60 --- /dev/null +++ b/changelog/4386.bugfix.rst @@ -0,0 +1 @@ +Handle uninitialized exceptioninfo in repr/str. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index d06e24f006c..26973c4e1b9 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -425,7 +425,10 @@ def __init__(self, tup=None, exprinfo=None): self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self)) def __repr__(self): - return "" % (self.typename, len(self.traceback)) + try: + return "" % (self.typename, len(self.traceback)) + except AttributeError: + return "" def exconly(self, tryshort=False): """ return the exception as a string @@ -513,9 +516,13 @@ def getrepr( return fmt.repr_excinfo(self) def __str__(self): - entry = self.traceback[-1] - loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) - return str(loc) + try: + entry = self.traceback[-1] + except AttributeError: + return repr(self) + else: + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) def __unicode__(self): entry = self.traceback[-1] diff --git a/testing/python/raises.py b/testing/python/raises.py index 3b94b05f506..130b196ac30 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -33,6 +33,23 @@ def __call__(self): except pytest.raises.Exception: pass + def test_raises_repr_inflight(self): + """Ensure repr() on an exception info inside a pytest.raises with block works (#4386)""" + + class E(Exception): + pass + + with pytest.raises(E) as excinfo: + # this test prints the inflight uninitialized object + # using repr and str as well as pprint to demonstrate + # it works + print(str(excinfo)) + print(repr(excinfo)) + import pprint + + pprint.pprint(excinfo) + raise E() + def test_raises_as_contextmanager(self, testdir): testdir.makepyfile( """