code: export ExceptionInfo for typing purposes

This type is most prominent in `pytest.raises` and we should allow to
refer to it by a public name.

The type is not in a perfectly "exposable" state. In particular:

- The `traceback` property with type `Traceback` which is derived from
  the `py.code` API and exposes a bunch more types transitively. This
  stuff is *not* exported and probably won't be.

- The `getrepr` method which probably should be private.

But they're already used in the wild so no point in just hiding them
now.

The __init__ API is hidden -- the public API for this are the `from_*`
classmethods.
This commit is contained in:
Ran Benita
2021-03-13 11:10:34 +02:00
parent 96ef7d678b
commit f2d65c85f4
11 changed files with 50 additions and 24 deletions

View File

@@ -42,6 +42,7 @@ from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr
from _pytest.compat import final
from _pytest.compat import get_real_func
from _pytest.deprecated import check_ispytest
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
@@ -440,15 +441,28 @@ E = TypeVar("E", bound=BaseException, covariant=True)
@final
@attr.s(repr=False)
@attr.s(repr=False, init=False)
class ExceptionInfo(Generic[E]):
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
_assert_start_repr = "AssertionError('assert "
_excinfo = attr.ib(type=Optional[Tuple[Type["E"], "E", TracebackType]])
_striptext = attr.ib(type=str, default="")
_traceback = attr.ib(type=Optional[Traceback], default=None)
_striptext = attr.ib(type=str)
_traceback = attr.ib(type=Optional[Traceback])
def __init__(
self,
excinfo: Optional[Tuple[Type["E"], "E", TracebackType]],
striptext: str = "",
traceback: Optional[Traceback] = None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
self._excinfo = excinfo
self._striptext = striptext
self._traceback = traceback
@classmethod
def from_exc_info(
@@ -475,7 +489,7 @@ class ExceptionInfo(Generic[E]):
if exprinfo and exprinfo.startswith(cls._assert_start_repr):
_striptext = "AssertionError: "
return cls(exc_info, _striptext)
return cls(exc_info, _striptext, _ispytest=True)
@classmethod
def from_current(
@@ -502,7 +516,7 @@ class ExceptionInfo(Generic[E]):
@classmethod
def for_later(cls) -> "ExceptionInfo[E]":
"""Return an unfilled ExceptionInfo."""
return cls(None)
return cls(None, _ispytest=True)
def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None:
"""Fill an unfilled ExceptionInfo created with ``for_later()``."""
@@ -922,7 +936,7 @@ class FormattedExcinfo:
if e.__cause__ is not None and self.chain:
e = e.__cause__
excinfo_ = (
ExceptionInfo((type(e), e, e.__traceback__))
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
if e.__traceback__
else None
)
@@ -932,7 +946,7 @@ class FormattedExcinfo:
):
e = e.__context__
excinfo_ = (
ExceptionInfo((type(e), e, e.__traceback__))
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
if e.__traceback__
else None
)