Merge pull request #8194 from bluetech/typing-public-3

Export pytest.Metafunc and pytest.Callinfo, hide NodeKeywords
This commit is contained in:
Ran Benita 2020-12-29 18:22:02 +02:00 committed by GitHub
commit 7751904875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 32 deletions

View File

@ -3,5 +3,7 @@ Directly constructing the following classes is now deprecated:
- ``_pytest.mark.structures.Mark``
- ``_pytest.mark.structures.MarkDecorator``
- ``_pytest.mark.structures.MarkGenerator``
- ``_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.

View File

@ -5,6 +5,8 @@ The newly-exported types are:
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
- ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`.
- ``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.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.
Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0.

View File

@ -397,8 +397,8 @@ Metafunc.addcall
.. versionremoved:: 4.0
``_pytest.python.Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use
:meth:`_pytest.python.Metafunc.parametrize` instead.
``Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use
:meth:`pytest.Metafunc.parametrize` instead.
Example:

View File

@ -47,7 +47,7 @@ There are several limitations and difficulties with this approach:
2. parametrizing the "db" resource is not straight forward:
you need to apply a "parametrize" decorator or implement a
:py:func:`~hookspec.pytest_generate_tests` hook
calling :py:func:`~python.Metafunc.parametrize` which
calling :py:func:`~pytest.Metafunc.parametrize` which
performs parametrization at the places where the resource
is used. Moreover, you need to modify the factory to use an
``extrakey`` parameter containing ``request.param`` to the
@ -113,7 +113,7 @@ This new way of parametrizing funcarg factories should in many cases
allow to re-use already written factories because effectively
``request.param`` was already used when test functions/classes were
parametrized via
:py:func:`metafunc.parametrize(indirect=True) <_pytest.python.Metafunc.parametrize>` calls.
:py:func:`metafunc.parametrize(indirect=True) <pytest.Metafunc.parametrize>` calls.
Of course it's perfectly fine to combine parametrization and scoping:

View File

@ -138,7 +138,7 @@ pytest.mark.parametrize
**Tutorial**: :doc:`parametrize`.
This mark has the same signature as :py:meth:`_pytest.python.Metafunc.parametrize`; see there.
This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see there.
.. _`pytest.mark.skip ref`:
@ -758,7 +758,7 @@ Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hoo
CallInfo
~~~~~~~~
.. autoclass:: _pytest.runner.CallInfo()
.. autoclass:: pytest.CallInfo()
:members:
@ -870,7 +870,7 @@ Mark
Metafunc
~~~~~~~~
.. autoclass:: _pytest.python.Metafunc
.. autoclass:: pytest.Metafunc()
:members:
Module

View File

@ -1,10 +1,12 @@
import os
import warnings
from pathlib import Path
from typing import Any
from typing import Callable
from typing import Iterable
from typing import Iterator
from typing import List
from typing import MutableMapping
from typing import Optional
from typing import overload
from typing import Set
@ -148,8 +150,9 @@ class Node(metaclass=NodeMeta):
#: Filesystem path where this node was collected from (can be None).
self.fspath = fspath or getattr(parent, "fspath", None)
# The explicit annotation is to avoid publicly exposing NodeKeywords.
#: Keywords/markers collected from all scopes.
self.keywords = NodeKeywords(self)
self.keywords: MutableMapping[str, Any] = NodeKeywords(self)
#: The marker objects belonging to this node.
self.own_markers: List[Mark] = []

View File

@ -55,6 +55,7 @@ from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.fixtures import FuncFixtureInfo
from _pytest.main import Session
@ -467,7 +468,12 @@ class PyCollector(PyobjMixin, nodes.Collector):
fixtureinfo = definition._fixtureinfo
metafunc = Metafunc(
definition, fixtureinfo, self.config, cls=cls, module=module
definition=definition,
fixtureinfo=fixtureinfo,
config=self.config,
cls=cls,
module=module,
_ispytest=True,
)
methods = []
if hasattr(module, "pytest_generate_tests"):
@ -971,7 +977,11 @@ class Metafunc:
config: Config,
cls=None,
module=None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
#: Access to the underlying :class:`_pytest.python.FunctionDefinition`.
self.definition = definition

View File

@ -26,6 +26,7 @@ from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr
from _pytest.compat import final
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.nodes import Collector
from _pytest.nodes import Item
from _pytest.nodes import Node
@ -260,34 +261,47 @@ TResult = TypeVar("TResult", covariant=True)
@final
@attr.s(repr=False)
@attr.s(repr=False, init=False, auto_attribs=True)
class CallInfo(Generic[TResult]):
"""Result/Exception info a function invocation.
"""Result/Exception info of a function invocation."""
:param T result:
The return value of the call, if it didn't raise. Can only be
accessed if excinfo is None.
:param Optional[ExceptionInfo] excinfo:
The captured exception of the call, if it raised.
:param float start:
The system time when the call started, in seconds since the epoch.
:param float stop:
The system time when the call ended, in seconds since the epoch.
:param float duration:
The call duration, in seconds.
:param str when:
The context of invocation: "setup", "call", "teardown", ...
"""
_result: Optional[TResult]
#: The captured exception of the call, if it raised.
excinfo: Optional[ExceptionInfo[BaseException]]
#: The system time when the call started, in seconds since the epoch.
start: float
#: The system time when the call ended, in seconds since the epoch.
stop: float
#: The call duration, in seconds.
duration: float
#: The context of invocation: "collect", "setup", "call" or "teardown".
when: "Literal['collect', 'setup', 'call', 'teardown']"
_result = attr.ib(type="Optional[TResult]")
excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]])
start = attr.ib(type=float)
stop = attr.ib(type=float)
duration = attr.ib(type=float)
when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']")
def __init__(
self,
result: Optional[TResult],
excinfo: Optional[ExceptionInfo[BaseException]],
start: float,
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
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:
raise AttributeError(f"{self!r} has no valid result")
# 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], ...]]
] = None,
) -> "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
start = timing.time()
precise_start = timing.perf_counter()
@ -325,6 +349,7 @@ class CallInfo(Generic[TResult]):
when=when,
result=result,
excinfo=excinfo,
_ispytest=True,
)
def __repr__(self) -> str:

View File

@ -40,6 +40,7 @@ from _pytest.pytester import Testdir
from _pytest.python import Class
from _pytest.python import Function
from _pytest.python import Instance
from _pytest.python import Metafunc
from _pytest.python import Module
from _pytest.python import Package
from _pytest.python_api import approx
@ -47,6 +48,7 @@ from _pytest.python_api import raises
from _pytest.recwarn import deprecated_call
from _pytest.recwarn import WarningsRecorder
from _pytest.recwarn import warns
from _pytest.runner import CallInfo
from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory
from _pytest.warning_types import PytestAssertRewriteWarning
@ -68,6 +70,7 @@ __all__ = [
"_fillfuncargs",
"approx",
"Cache",
"CallInfo",
"CaptureFixture",
"Class",
"cmdline",
@ -95,6 +98,7 @@ __all__ = [
"Mark",
"MarkDecorator",
"MarkGenerator",
"Metafunc",
"Module",
"MonkeyPatch",
"Package",

View File

@ -47,7 +47,7 @@ class TestMetafunc:
names = getfuncargnames(func)
fixtureinfo: Any = FuncFixtureInfoMock(names)
definition: Any = DefinitionMock._create(func, "mock::nodeid")
return python.Metafunc(definition, fixtureinfo, config)
return python.Metafunc(definition, fixtureinfo, config, _ispytest=True)
def test_no_funcargs(self) -> None:
def function():