Merge pull request #8194 from bluetech/typing-public-3
Export pytest.Metafunc and pytest.Callinfo, hide NodeKeywords
This commit is contained in:
commit
7751904875
|
@ -3,5 +3,7 @@ Directly constructing the following classes is now deprecated:
|
||||||
- ``_pytest.mark.structures.Mark``
|
- ``_pytest.mark.structures.Mark``
|
||||||
- ``_pytest.mark.structures.MarkDecorator``
|
- ``_pytest.mark.structures.MarkDecorator``
|
||||||
- ``_pytest.mark.structures.MarkGenerator``
|
- ``_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.
|
These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.
|
||||||
|
|
|
@ -5,6 +5,8 @@ The newly-exported types are:
|
||||||
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
|
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
|
||||||
- ``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.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.
|
||||||
|
|
|
@ -397,8 +397,8 @@ Metafunc.addcall
|
||||||
|
|
||||||
.. versionremoved:: 4.0
|
.. versionremoved:: 4.0
|
||||||
|
|
||||||
``_pytest.python.Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use
|
``Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use
|
||||||
:meth:`_pytest.python.Metafunc.parametrize` instead.
|
:meth:`pytest.Metafunc.parametrize` instead.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ There are several limitations and difficulties with this approach:
|
||||||
2. parametrizing the "db" resource is not straight forward:
|
2. parametrizing the "db" resource is not straight forward:
|
||||||
you need to apply a "parametrize" decorator or implement a
|
you need to apply a "parametrize" decorator or implement a
|
||||||
:py:func:`~hookspec.pytest_generate_tests` hook
|
: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
|
performs parametrization at the places where the resource
|
||||||
is used. Moreover, you need to modify the factory to use an
|
is used. Moreover, you need to modify the factory to use an
|
||||||
``extrakey`` parameter containing ``request.param`` to the
|
``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
|
allow to re-use already written factories because effectively
|
||||||
``request.param`` was already used when test functions/classes were
|
``request.param`` was already used when test functions/classes were
|
||||||
parametrized via
|
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:
|
Of course it's perfectly fine to combine parametrization and scoping:
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ pytest.mark.parametrize
|
||||||
|
|
||||||
**Tutorial**: :doc:`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`:
|
.. _`pytest.mark.skip ref`:
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
|
||||||
|
@ -870,7 +870,7 @@ Mark
|
||||||
Metafunc
|
Metafunc
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
.. autoclass:: _pytest.python.Metafunc
|
.. autoclass:: pytest.Metafunc()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
Module
|
Module
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import MutableMapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import overload
|
from typing import overload
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
@ -148,8 +150,9 @@ class Node(metaclass=NodeMeta):
|
||||||
#: Filesystem path where this node was collected from (can be None).
|
#: Filesystem path where this node was collected from (can be None).
|
||||||
self.fspath = fspath or getattr(parent, "fspath", None)
|
self.fspath = fspath or getattr(parent, "fspath", None)
|
||||||
|
|
||||||
|
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
||||||
#: Keywords/markers collected from all scopes.
|
#: Keywords/markers collected from all scopes.
|
||||||
self.keywords = NodeKeywords(self)
|
self.keywords: MutableMapping[str, Any] = NodeKeywords(self)
|
||||||
|
|
||||||
#: The marker objects belonging to this node.
|
#: The marker objects belonging to this node.
|
||||||
self.own_markers: List[Mark] = []
|
self.own_markers: List[Mark] = []
|
||||||
|
|
|
@ -55,6 +55,7 @@ from _pytest.config import Config
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
from _pytest.fixtures import FuncFixtureInfo
|
from _pytest.fixtures import FuncFixtureInfo
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
@ -467,7 +468,12 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
||||||
fixtureinfo = definition._fixtureinfo
|
fixtureinfo = definition._fixtureinfo
|
||||||
|
|
||||||
metafunc = Metafunc(
|
metafunc = Metafunc(
|
||||||
definition, fixtureinfo, self.config, cls=cls, module=module
|
definition=definition,
|
||||||
|
fixtureinfo=fixtureinfo,
|
||||||
|
config=self.config,
|
||||||
|
cls=cls,
|
||||||
|
module=module,
|
||||||
|
_ispytest=True,
|
||||||
)
|
)
|
||||||
methods = []
|
methods = []
|
||||||
if hasattr(module, "pytest_generate_tests"):
|
if hasattr(module, "pytest_generate_tests"):
|
||||||
|
@ -971,7 +977,11 @@ class Metafunc:
|
||||||
config: Config,
|
config: Config,
|
||||||
cls=None,
|
cls=None,
|
||||||
module=None,
|
module=None,
|
||||||
|
*,
|
||||||
|
_ispytest: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
check_ispytest(_ispytest)
|
||||||
|
|
||||||
#: Access to the underlying :class:`_pytest.python.FunctionDefinition`.
|
#: Access to the underlying :class:`_pytest.python.FunctionDefinition`.
|
||||||
self.definition = definition
|
self.definition = definition
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -40,6 +40,7 @@ from _pytest.pytester import Testdir
|
||||||
from _pytest.python import Class
|
from _pytest.python import Class
|
||||||
from _pytest.python import Function
|
from _pytest.python import Function
|
||||||
from _pytest.python import Instance
|
from _pytest.python import Instance
|
||||||
|
from _pytest.python import Metafunc
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
from _pytest.python import Package
|
from _pytest.python import Package
|
||||||
from _pytest.python_api import approx
|
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 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
|
||||||
|
@ -68,6 +70,7 @@ __all__ = [
|
||||||
"_fillfuncargs",
|
"_fillfuncargs",
|
||||||
"approx",
|
"approx",
|
||||||
"Cache",
|
"Cache",
|
||||||
|
"CallInfo",
|
||||||
"CaptureFixture",
|
"CaptureFixture",
|
||||||
"Class",
|
"Class",
|
||||||
"cmdline",
|
"cmdline",
|
||||||
|
@ -95,6 +98,7 @@ __all__ = [
|
||||||
"Mark",
|
"Mark",
|
||||||
"MarkDecorator",
|
"MarkDecorator",
|
||||||
"MarkGenerator",
|
"MarkGenerator",
|
||||||
|
"Metafunc",
|
||||||
"Module",
|
"Module",
|
||||||
"MonkeyPatch",
|
"MonkeyPatch",
|
||||||
"Package",
|
"Package",
|
||||||
|
|
|
@ -47,7 +47,7 @@ class TestMetafunc:
|
||||||
names = getfuncargnames(func)
|
names = getfuncargnames(func)
|
||||||
fixtureinfo: Any = FuncFixtureInfoMock(names)
|
fixtureinfo: Any = FuncFixtureInfoMock(names)
|
||||||
definition: Any = DefinitionMock._create(func, "mock::nodeid")
|
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 test_no_funcargs(self) -> None:
|
||||||
def function():
|
def function():
|
||||||
|
|
Loading…
Reference in New Issue