runner: export pytest.CallInfo for typing purposes

The type cannot be constructed directly, but is exported for use in type
annotations, since it is reachable through existing public API.

This also documents `from_call` as public, because at least
pytest-forked uses it, so we must treat it as public already anyway.
This commit is contained in:
Ran Benita 2020-12-26 21:23:23 +02:00
parent bd76042344
commit 96ea867fec
5 changed files with 52 additions and 23 deletions

View File

@ -4,5 +4,6 @@ Directly constructing the following classes is now deprecated:
- ``_pytest.mark.structures.MarkDecorator`` - ``_pytest.mark.structures.MarkDecorator``
- ``_pytest.mark.structures.MarkGenerator`` - ``_pytest.mark.structures.MarkGenerator``
- ``_pytest.python.Metafunc`` - ``_pytest.python.Metafunc``
- ``_pytest.runner.CallInfo``
These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.

View File

@ -6,6 +6,7 @@ The newly-exported types are:
- ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`. - ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`.
- ``pytest.MarkGenerator`` for the :class:`pytest.mark <pytest.MarkGenerator>` singleton. - ``pytest.MarkGenerator`` for the :class:`pytest.mark <pytest.MarkGenerator>` singleton.
- ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the `pytest_generate_tests <pytest.hookspec.pytest_generate_tests>` hook. - ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the `pytest_generate_tests <pytest.hookspec.pytest_generate_tests>` hook.
- ``pytest.runner.CallInfo`` for the :class:`CallInfo <pytest.CallInfo>` type passed to various hooks.
Constructing them directly is not supported; they are only meant for use in type annotations. Constructing them directly is not supported; they are only meant for use in type annotations.
Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0.

View File

@ -758,7 +758,7 @@ Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hoo
CallInfo CallInfo
~~~~~~~~ ~~~~~~~~
.. autoclass:: _pytest.runner.CallInfo() .. autoclass:: pytest.CallInfo()
:members: :members:

View File

@ -26,6 +26,7 @@ from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest.compat import final from _pytest.compat import final
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.nodes import Collector from _pytest.nodes import Collector
from _pytest.nodes import Item from _pytest.nodes import Item
from _pytest.nodes import Node from _pytest.nodes import Node
@ -260,34 +261,47 @@ TResult = TypeVar("TResult", covariant=True)
@final @final
@attr.s(repr=False) @attr.s(repr=False, init=False, auto_attribs=True)
class CallInfo(Generic[TResult]): class CallInfo(Generic[TResult]):
"""Result/Exception info a function invocation. """Result/Exception info of a function invocation."""
:param T result: _result: Optional[TResult]
The return value of the call, if it didn't raise. Can only be #: The captured exception of the call, if it raised.
accessed if excinfo is None. excinfo: Optional[ExceptionInfo[BaseException]]
:param Optional[ExceptionInfo] excinfo: #: The system time when the call started, in seconds since the epoch.
The captured exception of the call, if it raised. start: float
:param float start: #: The system time when the call ended, in seconds since the epoch.
The system time when the call started, in seconds since the epoch. stop: float
:param float stop: #: The call duration, in seconds.
The system time when the call ended, in seconds since the epoch. duration: float
:param float duration: #: The context of invocation: "collect", "setup", "call" or "teardown".
The call duration, in seconds. when: "Literal['collect', 'setup', 'call', 'teardown']"
:param str when:
The context of invocation: "setup", "call", "teardown", ...
"""
_result = attr.ib(type="Optional[TResult]") def __init__(
excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]]) self,
start = attr.ib(type=float) result: Optional[TResult],
stop = attr.ib(type=float) excinfo: Optional[ExceptionInfo[BaseException]],
duration = attr.ib(type=float) start: float,
when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']") stop: float,
duration: float,
when: "Literal['collect', 'setup', 'call', 'teardown']",
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
self._result = result
self.excinfo = excinfo
self.start = start
self.stop = stop
self.duration = duration
self.when = when
@property @property
def result(self) -> TResult: def result(self) -> TResult:
"""The return value of the call, if it didn't raise.
Can only be accessed if excinfo is None.
"""
if self.excinfo is not None: if self.excinfo is not None:
raise AttributeError(f"{self!r} has no valid result") raise AttributeError(f"{self!r} has no valid result")
# The cast is safe because an exception wasn't raised, hence # The cast is safe because an exception wasn't raised, hence
@ -304,6 +318,16 @@ class CallInfo(Generic[TResult]):
Union[Type[BaseException], Tuple[Type[BaseException], ...]] Union[Type[BaseException], Tuple[Type[BaseException], ...]]
] = None, ] = None,
) -> "CallInfo[TResult]": ) -> "CallInfo[TResult]":
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None excinfo = None
start = timing.time() start = timing.time()
precise_start = timing.perf_counter() precise_start = timing.perf_counter()
@ -325,6 +349,7 @@ class CallInfo(Generic[TResult]):
when=when, when=when,
result=result, result=result,
excinfo=excinfo, excinfo=excinfo,
_ispytest=True,
) )
def __repr__(self) -> str: def __repr__(self) -> str:

View File

@ -48,6 +48,7 @@ from _pytest.python_api import raises
from _pytest.recwarn import deprecated_call from _pytest.recwarn import deprecated_call
from _pytest.recwarn import WarningsRecorder from _pytest.recwarn import WarningsRecorder
from _pytest.recwarn import warns from _pytest.recwarn import warns
from _pytest.runner import CallInfo
from _pytest.tmpdir import TempdirFactory from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory from _pytest.tmpdir import TempPathFactory
from _pytest.warning_types import PytestAssertRewriteWarning from _pytest.warning_types import PytestAssertRewriteWarning
@ -69,6 +70,7 @@ __all__ = [
"_fillfuncargs", "_fillfuncargs",
"approx", "approx",
"Cache", "Cache",
"CallInfo",
"CaptureFixture", "CaptureFixture",
"Class", "Class",
"cmdline", "cmdline",