Merge pull request #8437 from bluetech/rm-typevar-prefix
Remove `_` prefix from TypeVars, expose ExceptionInfo
This commit is contained in:
commit
db539ed2b0
|
@ -5,5 +5,6 @@ Directly constructing the following classes is now deprecated:
|
||||||
- ``_pytest.mark.structures.MarkGenerator``
|
- ``_pytest.mark.structures.MarkGenerator``
|
||||||
- ``_pytest.python.Metafunc``
|
- ``_pytest.python.Metafunc``
|
||||||
- ``_pytest.runner.CallInfo``
|
- ``_pytest.runner.CallInfo``
|
||||||
|
- ``_pytest._code.ExceptionInfo``
|
||||||
|
|
||||||
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,8 +5,9 @@ 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.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the :func:`pytest_generate_tests <pytest.hookspec.pytest_generate_tests>` hook.
|
||||||
- ``pytest.runner.CallInfo`` for the :class:`CallInfo <pytest.CallInfo>` type passed to various hooks.
|
- ``pytest.CallInfo`` for the :class:`CallInfo <pytest.CallInfo>` type passed to various hooks.
|
||||||
|
- ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo <pytest.ExceptionInfo>` type returned from :func:`pytest.raises` and 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.
|
||||||
|
|
|
@ -98,7 +98,7 @@ and if you need to have access to the actual exception info you may use:
|
||||||
f()
|
f()
|
||||||
assert "maximum recursion" in str(excinfo.value)
|
assert "maximum recursion" in str(excinfo.value)
|
||||||
|
|
||||||
``excinfo`` is an ``ExceptionInfo`` instance, which is a wrapper around
|
``excinfo`` is an :class:`~pytest.ExceptionInfo` instance, which is a wrapper around
|
||||||
the actual exception raised. The main attributes of interest are
|
the actual exception raised. The main attributes of interest are
|
||||||
``.type``, ``.value`` and ``.traceback``.
|
``.type``, ``.value`` and ``.traceback``.
|
||||||
|
|
||||||
|
|
|
@ -793,7 +793,7 @@ Config
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. autoclass:: _pytest._code.ExceptionInfo
|
.. autoclass:: pytest.ExceptionInfo()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ from _pytest._io.saferepr import safeformat
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final
|
||||||
from _pytest.compat import get_real_func
|
from _pytest.compat import get_real_func
|
||||||
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.pathlib import absolutepath
|
from _pytest.pathlib import absolutepath
|
||||||
from _pytest.pathlib import bestrelpath
|
from _pytest.pathlib import bestrelpath
|
||||||
|
|
||||||
|
@ -436,26 +437,39 @@ co_equal = compile(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_E = TypeVar("_E", bound=BaseException, covariant=True)
|
E = TypeVar("E", bound=BaseException, covariant=True)
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(repr=False)
|
@attr.s(repr=False, init=False)
|
||||||
class ExceptionInfo(Generic[_E]):
|
class ExceptionInfo(Generic[E]):
|
||||||
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
|
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
|
||||||
|
|
||||||
_assert_start_repr = "AssertionError('assert "
|
_assert_start_repr = "AssertionError('assert "
|
||||||
|
|
||||||
_excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]])
|
_excinfo = attr.ib(type=Optional[Tuple[Type["E"], "E", TracebackType]])
|
||||||
_striptext = attr.ib(type=str, default="")
|
_striptext = attr.ib(type=str)
|
||||||
_traceback = attr.ib(type=Optional[Traceback], default=None)
|
_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
|
@classmethod
|
||||||
def from_exc_info(
|
def from_exc_info(
|
||||||
cls,
|
cls,
|
||||||
exc_info: Tuple[Type[_E], _E, TracebackType],
|
exc_info: Tuple[Type[E], E, TracebackType],
|
||||||
exprinfo: Optional[str] = None,
|
exprinfo: Optional[str] = None,
|
||||||
) -> "ExceptionInfo[_E]":
|
) -> "ExceptionInfo[E]":
|
||||||
"""Return an ExceptionInfo for an existing exc_info tuple.
|
"""Return an ExceptionInfo for an existing exc_info tuple.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
@ -475,7 +489,7 @@ class ExceptionInfo(Generic[_E]):
|
||||||
if exprinfo and exprinfo.startswith(cls._assert_start_repr):
|
if exprinfo and exprinfo.startswith(cls._assert_start_repr):
|
||||||
_striptext = "AssertionError: "
|
_striptext = "AssertionError: "
|
||||||
|
|
||||||
return cls(exc_info, _striptext)
|
return cls(exc_info, _striptext, _ispytest=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_current(
|
def from_current(
|
||||||
|
@ -500,17 +514,17 @@ class ExceptionInfo(Generic[_E]):
|
||||||
return ExceptionInfo.from_exc_info(exc_info, exprinfo)
|
return ExceptionInfo.from_exc_info(exc_info, exprinfo)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_later(cls) -> "ExceptionInfo[_E]":
|
def for_later(cls) -> "ExceptionInfo[E]":
|
||||||
"""Return an unfilled ExceptionInfo."""
|
"""Return an unfilled ExceptionInfo."""
|
||||||
return cls(None)
|
return cls(None, _ispytest=True)
|
||||||
|
|
||||||
def fill_unfilled(self, exc_info: Tuple[Type[_E], _E, TracebackType]) -> None:
|
def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None:
|
||||||
"""Fill an unfilled ExceptionInfo created with ``for_later()``."""
|
"""Fill an unfilled ExceptionInfo created with ``for_later()``."""
|
||||||
assert self._excinfo is None, "ExceptionInfo was already filled"
|
assert self._excinfo is None, "ExceptionInfo was already filled"
|
||||||
self._excinfo = exc_info
|
self._excinfo = exc_info
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> Type[_E]:
|
def type(self) -> Type[E]:
|
||||||
"""The exception class."""
|
"""The exception class."""
|
||||||
assert (
|
assert (
|
||||||
self._excinfo is not None
|
self._excinfo is not None
|
||||||
|
@ -518,7 +532,7 @@ class ExceptionInfo(Generic[_E]):
|
||||||
return self._excinfo[0]
|
return self._excinfo[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self) -> _E:
|
def value(self) -> E:
|
||||||
"""The exception value."""
|
"""The exception value."""
|
||||||
assert (
|
assert (
|
||||||
self._excinfo is not None
|
self._excinfo is not None
|
||||||
|
@ -562,10 +576,10 @@ class ExceptionInfo(Generic[_E]):
|
||||||
def exconly(self, tryshort: bool = False) -> str:
|
def exconly(self, tryshort: bool = False) -> str:
|
||||||
"""Return the exception as a string.
|
"""Return the exception as a string.
|
||||||
|
|
||||||
When 'tryshort' resolves to True, and the exception is a
|
When 'tryshort' resolves to True, and the exception is an
|
||||||
_pytest._code._AssertionError, only the actual exception part of
|
AssertionError, only the actual exception part of the exception
|
||||||
the exception representation is returned (so 'AssertionError: ' is
|
representation is returned (so 'AssertionError: ' is removed from
|
||||||
removed from the beginning).
|
the beginning).
|
||||||
"""
|
"""
|
||||||
lines = format_exception_only(self.type, self.value)
|
lines = format_exception_only(self.type, self.value)
|
||||||
text = "".join(lines)
|
text = "".join(lines)
|
||||||
|
@ -922,7 +936,7 @@ class FormattedExcinfo:
|
||||||
if e.__cause__ is not None and self.chain:
|
if e.__cause__ is not None and self.chain:
|
||||||
e = e.__cause__
|
e = e.__cause__
|
||||||
excinfo_ = (
|
excinfo_ = (
|
||||||
ExceptionInfo((type(e), e, e.__traceback__))
|
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
|
||||||
if e.__traceback__
|
if e.__traceback__
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
@ -932,7 +946,7 @@ class FormattedExcinfo:
|
||||||
):
|
):
|
||||||
e = e.__context__
|
e = e.__context__
|
||||||
excinfo_ = (
|
excinfo_ = (
|
||||||
ExceptionInfo((type(e), e, e.__traceback__))
|
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
|
||||||
if e.__traceback__
|
if e.__traceback__
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
|
@ -145,7 +145,7 @@ def main(
|
||||||
try:
|
try:
|
||||||
config = _prepareconfig(args, plugins)
|
config = _prepareconfig(args, plugins)
|
||||||
except ConftestImportFailure as e:
|
except ConftestImportFailure as e:
|
||||||
exc_info = ExceptionInfo(e.excinfo)
|
exc_info = ExceptionInfo.from_exc_info(e.excinfo)
|
||||||
tw = TerminalWriter(sys.stderr)
|
tw = TerminalWriter(sys.stderr)
|
||||||
tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)
|
tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)
|
||||||
exc_info.traceback = exc_info.traceback.filter(
|
exc_info.traceback = exc_info.traceback.filter(
|
||||||
|
|
|
@ -365,7 +365,7 @@ class DoctestItem(pytest.Item):
|
||||||
example, failure.got, report_choice
|
example, failure.got, report_choice
|
||||||
).split("\n")
|
).split("\n")
|
||||||
else:
|
else:
|
||||||
inner_excinfo = ExceptionInfo(failure.exc_info)
|
inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info)
|
||||||
lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
|
lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
|
||||||
lines += [
|
lines += [
|
||||||
x.strip("\n")
|
x.strip("\n")
|
||||||
|
|
|
@ -79,18 +79,18 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
# The value of the fixture -- return/yield of the fixture function (type variable).
|
# The value of the fixture -- return/yield of the fixture function (type variable).
|
||||||
_FixtureValue = TypeVar("_FixtureValue")
|
FixtureValue = TypeVar("FixtureValue")
|
||||||
# The type of the fixture function (type variable).
|
# The type of the fixture function (type variable).
|
||||||
_FixtureFunction = TypeVar("_FixtureFunction", bound=Callable[..., object])
|
FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object])
|
||||||
# The type of a fixture function (type alias generic in fixture value).
|
# The type of a fixture function (type alias generic in fixture value).
|
||||||
_FixtureFunc = Union[
|
_FixtureFunc = Union[
|
||||||
Callable[..., _FixtureValue], Callable[..., Generator[_FixtureValue, None, None]]
|
Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]]
|
||||||
]
|
]
|
||||||
# The type of FixtureDef.cached_result (type alias generic in fixture value).
|
# The type of FixtureDef.cached_result (type alias generic in fixture value).
|
||||||
_FixtureCachedResult = Union[
|
_FixtureCachedResult = Union[
|
||||||
Tuple[
|
Tuple[
|
||||||
# The result.
|
# The result.
|
||||||
_FixtureValue,
|
FixtureValue,
|
||||||
# Cache key.
|
# Cache key.
|
||||||
object,
|
object,
|
||||||
None,
|
None,
|
||||||
|
@ -106,8 +106,8 @@ _FixtureCachedResult = Union[
|
||||||
|
|
||||||
|
|
||||||
@attr.s(frozen=True)
|
@attr.s(frozen=True)
|
||||||
class PseudoFixtureDef(Generic[_FixtureValue]):
|
class PseudoFixtureDef(Generic[FixtureValue]):
|
||||||
cached_result = attr.ib(type="_FixtureCachedResult[_FixtureValue]")
|
cached_result = attr.ib(type="_FixtureCachedResult[FixtureValue]")
|
||||||
scope = attr.ib(type="_Scope")
|
scope = attr.ib(type="_Scope")
|
||||||
|
|
||||||
|
|
||||||
|
@ -928,11 +928,11 @@ def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn":
|
||||||
|
|
||||||
|
|
||||||
def call_fixture_func(
|
def call_fixture_func(
|
||||||
fixturefunc: "_FixtureFunc[_FixtureValue]", request: FixtureRequest, kwargs
|
fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs
|
||||||
) -> _FixtureValue:
|
) -> FixtureValue:
|
||||||
if is_generator(fixturefunc):
|
if is_generator(fixturefunc):
|
||||||
fixturefunc = cast(
|
fixturefunc = cast(
|
||||||
Callable[..., Generator[_FixtureValue, None, None]], fixturefunc
|
Callable[..., Generator[FixtureValue, None, None]], fixturefunc
|
||||||
)
|
)
|
||||||
generator = fixturefunc(**kwargs)
|
generator = fixturefunc(**kwargs)
|
||||||
try:
|
try:
|
||||||
|
@ -942,7 +942,7 @@ def call_fixture_func(
|
||||||
finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
|
finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
|
||||||
request.addfinalizer(finalizer)
|
request.addfinalizer(finalizer)
|
||||||
else:
|
else:
|
||||||
fixturefunc = cast(Callable[..., _FixtureValue], fixturefunc)
|
fixturefunc = cast(Callable[..., FixtureValue], fixturefunc)
|
||||||
fixture_result = fixturefunc(**kwargs)
|
fixture_result = fixturefunc(**kwargs)
|
||||||
return fixture_result
|
return fixture_result
|
||||||
|
|
||||||
|
@ -985,7 +985,7 @@ def _eval_scope_callable(
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
class FixtureDef(Generic[_FixtureValue]):
|
class FixtureDef(Generic[FixtureValue]):
|
||||||
"""A container for a factory definition."""
|
"""A container for a factory definition."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -993,7 +993,7 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
fixturemanager: "FixtureManager",
|
fixturemanager: "FixtureManager",
|
||||||
baseid: Optional[str],
|
baseid: Optional[str],
|
||||||
argname: str,
|
argname: str,
|
||||||
func: "_FixtureFunc[_FixtureValue]",
|
func: "_FixtureFunc[FixtureValue]",
|
||||||
scope: "Union[_Scope, Callable[[str, Config], _Scope]]",
|
scope: "Union[_Scope, Callable[[str, Config], _Scope]]",
|
||||||
params: Optional[Sequence[object]],
|
params: Optional[Sequence[object]],
|
||||||
unittest: bool = False,
|
unittest: bool = False,
|
||||||
|
@ -1026,7 +1026,7 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
)
|
)
|
||||||
self.unittest = unittest
|
self.unittest = unittest
|
||||||
self.ids = ids
|
self.ids = ids
|
||||||
self.cached_result: Optional[_FixtureCachedResult[_FixtureValue]] = None
|
self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None
|
||||||
self._finalizers: List[Callable[[], object]] = []
|
self._finalizers: List[Callable[[], object]] = []
|
||||||
|
|
||||||
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
|
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
|
||||||
|
@ -1055,7 +1055,7 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
self.cached_result = None
|
self.cached_result = None
|
||||||
self._finalizers = []
|
self._finalizers = []
|
||||||
|
|
||||||
def execute(self, request: SubRequest) -> _FixtureValue:
|
def execute(self, request: SubRequest) -> FixtureValue:
|
||||||
# Get required arguments and register our own finish()
|
# Get required arguments and register our own finish()
|
||||||
# with their finalization.
|
# with their finalization.
|
||||||
for argname in self.argnames:
|
for argname in self.argnames:
|
||||||
|
@ -1096,8 +1096,8 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
|
|
||||||
|
|
||||||
def resolve_fixture_function(
|
def resolve_fixture_function(
|
||||||
fixturedef: FixtureDef[_FixtureValue], request: FixtureRequest
|
fixturedef: FixtureDef[FixtureValue], request: FixtureRequest
|
||||||
) -> "_FixtureFunc[_FixtureValue]":
|
) -> "_FixtureFunc[FixtureValue]":
|
||||||
"""Get the actual callable that can be called to obtain the fixture
|
"""Get the actual callable that can be called to obtain the fixture
|
||||||
value, dealing with unittest-specific instances and bound methods."""
|
value, dealing with unittest-specific instances and bound methods."""
|
||||||
fixturefunc = fixturedef.func
|
fixturefunc = fixturedef.func
|
||||||
|
@ -1123,8 +1123,8 @@ def resolve_fixture_function(
|
||||||
|
|
||||||
|
|
||||||
def pytest_fixture_setup(
|
def pytest_fixture_setup(
|
||||||
fixturedef: FixtureDef[_FixtureValue], request: SubRequest
|
fixturedef: FixtureDef[FixtureValue], request: SubRequest
|
||||||
) -> _FixtureValue:
|
) -> FixtureValue:
|
||||||
"""Execution of fixture setup."""
|
"""Execution of fixture setup."""
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for argname in fixturedef.argnames:
|
for argname in fixturedef.argnames:
|
||||||
|
@ -1174,9 +1174,9 @@ def _params_converter(
|
||||||
|
|
||||||
|
|
||||||
def wrap_function_to_error_out_if_called_directly(
|
def wrap_function_to_error_out_if_called_directly(
|
||||||
function: _FixtureFunction,
|
function: FixtureFunction,
|
||||||
fixture_marker: "FixtureFunctionMarker",
|
fixture_marker: "FixtureFunctionMarker",
|
||||||
) -> _FixtureFunction:
|
) -> FixtureFunction:
|
||||||
"""Wrap the given fixture function so we can raise an error about it being called directly,
|
"""Wrap the given fixture function so we can raise an error about it being called directly,
|
||||||
instead of used as an argument in a test function."""
|
instead of used as an argument in a test function."""
|
||||||
message = (
|
message = (
|
||||||
|
@ -1194,7 +1194,7 @@ def wrap_function_to_error_out_if_called_directly(
|
||||||
# further than this point and lose useful wrappings like @mock.patch (#3774).
|
# further than this point and lose useful wrappings like @mock.patch (#3774).
|
||||||
result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined]
|
result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined]
|
||||||
|
|
||||||
return cast(_FixtureFunction, result)
|
return cast(FixtureFunction, result)
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
|
@ -1213,7 +1213,7 @@ class FixtureFunctionMarker:
|
||||||
)
|
)
|
||||||
name = attr.ib(type=Optional[str], default=None)
|
name = attr.ib(type=Optional[str], default=None)
|
||||||
|
|
||||||
def __call__(self, function: _FixtureFunction) -> _FixtureFunction:
|
def __call__(self, function: FixtureFunction) -> FixtureFunction:
|
||||||
if inspect.isclass(function):
|
if inspect.isclass(function):
|
||||||
raise ValueError("class fixtures not supported (maybe in the future)")
|
raise ValueError("class fixtures not supported (maybe in the future)")
|
||||||
|
|
||||||
|
@ -1241,7 +1241,7 @@ class FixtureFunctionMarker:
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def fixture(
|
def fixture(
|
||||||
fixture_function: _FixtureFunction,
|
fixture_function: FixtureFunction,
|
||||||
*,
|
*,
|
||||||
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ...,
|
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ...,
|
||||||
params: Optional[Iterable[object]] = ...,
|
params: Optional[Iterable[object]] = ...,
|
||||||
|
@ -1253,7 +1253,7 @@ def fixture(
|
||||||
]
|
]
|
||||||
] = ...,
|
] = ...,
|
||||||
name: Optional[str] = ...,
|
name: Optional[str] = ...,
|
||||||
) -> _FixtureFunction:
|
) -> FixtureFunction:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@ -1276,7 +1276,7 @@ def fixture(
|
||||||
|
|
||||||
|
|
||||||
def fixture(
|
def fixture(
|
||||||
fixture_function: Optional[_FixtureFunction] = None,
|
fixture_function: Optional[FixtureFunction] = None,
|
||||||
*,
|
*,
|
||||||
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
|
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
|
||||||
params: Optional[Iterable[object]] = None,
|
params: Optional[Iterable[object]] = None,
|
||||||
|
@ -1288,7 +1288,7 @@ def fixture(
|
||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
) -> Union[FixtureFunctionMarker, _FixtureFunction]:
|
) -> Union[FixtureFunctionMarker, FixtureFunction]:
|
||||||
"""Decorator to mark a fixture factory function.
|
"""Decorator to mark a fixture factory function.
|
||||||
|
|
||||||
This decorator can be used, with or without parameters, to define a
|
This decorator can be used, with or without parameters, to define a
|
||||||
|
|
|
@ -396,7 +396,7 @@ class Node(metaclass=NodeMeta):
|
||||||
from _pytest.fixtures import FixtureLookupError
|
from _pytest.fixtures import FixtureLookupError
|
||||||
|
|
||||||
if isinstance(excinfo.value, ConftestImportFailure):
|
if isinstance(excinfo.value, ConftestImportFailure):
|
||||||
excinfo = ExceptionInfo(excinfo.value.excinfo)
|
excinfo = ExceptionInfo.from_exc_info(excinfo.value.excinfo)
|
||||||
if isinstance(excinfo.value, fail.Exception):
|
if isinstance(excinfo.value, fail.Exception):
|
||||||
if not excinfo.value.pytrace:
|
if not excinfo.value.pytrace:
|
||||||
style = "value"
|
style = "value"
|
||||||
|
|
|
@ -573,31 +573,31 @@ def _as_numpy_array(obj: object) -> Optional["ndarray"]:
|
||||||
|
|
||||||
# builtin pytest.raises helper
|
# builtin pytest.raises helper
|
||||||
|
|
||||||
_E = TypeVar("_E", bound=BaseException)
|
E = TypeVar("E", bound=BaseException)
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def raises(
|
def raises(
|
||||||
expected_exception: Union[Type[_E], Tuple[Type[_E], ...]],
|
expected_exception: Union[Type[E], Tuple[Type[E], ...]],
|
||||||
*,
|
*,
|
||||||
match: Optional[Union[str, Pattern[str]]] = ...,
|
match: Optional[Union[str, Pattern[str]]] = ...,
|
||||||
) -> "RaisesContext[_E]":
|
) -> "RaisesContext[E]":
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def raises(
|
def raises(
|
||||||
expected_exception: Union[Type[_E], Tuple[Type[_E], ...]],
|
expected_exception: Union[Type[E], Tuple[Type[E], ...]],
|
||||||
func: Callable[..., Any],
|
func: Callable[..., Any],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> _pytest._code.ExceptionInfo[_E]:
|
) -> _pytest._code.ExceptionInfo[E]:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def raises(
|
def raises(
|
||||||
expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], *args: Any, **kwargs: Any
|
expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any
|
||||||
) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]:
|
) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]:
|
||||||
r"""Assert that a code block/function call raises ``expected_exception``
|
r"""Assert that a code block/function call raises ``expected_exception``
|
||||||
or raise a failure exception otherwise.
|
or raise a failure exception otherwise.
|
||||||
|
|
||||||
|
@ -711,7 +711,7 @@ def raises(
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
|
|
||||||
if isinstance(expected_exception, type):
|
if isinstance(expected_exception, type):
|
||||||
excepted_exceptions: Tuple[Type[_E], ...] = (expected_exception,)
|
excepted_exceptions: Tuple[Type[E], ...] = (expected_exception,)
|
||||||
else:
|
else:
|
||||||
excepted_exceptions = expected_exception
|
excepted_exceptions = expected_exception
|
||||||
for exc in excepted_exceptions:
|
for exc in excepted_exceptions:
|
||||||
|
@ -752,19 +752,19 @@ raises.Exception = fail.Exception # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
class RaisesContext(Generic[_E]):
|
class RaisesContext(Generic[E]):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
expected_exception: Union[Type[_E], Tuple[Type[_E], ...]],
|
expected_exception: Union[Type[E], Tuple[Type[E], ...]],
|
||||||
message: str,
|
message: str,
|
||||||
match_expr: Optional[Union[str, Pattern[str]]] = None,
|
match_expr: Optional[Union[str, Pattern[str]]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.expected_exception = expected_exception
|
self.expected_exception = expected_exception
|
||||||
self.message = message
|
self.message = message
|
||||||
self.match_expr = match_expr
|
self.match_expr = match_expr
|
||||||
self.excinfo: Optional[_pytest._code.ExceptionInfo[_E]] = None
|
self.excinfo: Optional[_pytest._code.ExceptionInfo[E]] = None
|
||||||
|
|
||||||
def __enter__(self) -> _pytest._code.ExceptionInfo[_E]:
|
def __enter__(self) -> _pytest._code.ExceptionInfo[E]:
|
||||||
self.excinfo = _pytest._code.ExceptionInfo.for_later()
|
self.excinfo = _pytest._code.ExceptionInfo.for_later()
|
||||||
return self.excinfo
|
return self.excinfo
|
||||||
|
|
||||||
|
@ -781,7 +781,7 @@ class RaisesContext(Generic[_E]):
|
||||||
if not issubclass(exc_type, self.expected_exception):
|
if not issubclass(exc_type, self.expected_exception):
|
||||||
return False
|
return False
|
||||||
# Cast to narrow the exception type now that it's verified.
|
# Cast to narrow the exception type now that it's verified.
|
||||||
exc_info = cast(Tuple[Type[_E], _E, TracebackType], (exc_type, exc_val, exc_tb))
|
exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb))
|
||||||
self.excinfo.fill_unfilled(exc_info)
|
self.excinfo.fill_unfilled(exc_info)
|
||||||
if self.match_expr is not None:
|
if self.match_expr is not None:
|
||||||
self.excinfo.match(self.match_expr)
|
self.excinfo.match(self.match_expr)
|
||||||
|
|
|
@ -209,7 +209,7 @@ class TestCaseFunction(Function):
|
||||||
# Unwrap potential exception info (see twisted trial support below).
|
# Unwrap potential exception info (see twisted trial support below).
|
||||||
rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo)
|
rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo)
|
||||||
try:
|
try:
|
||||||
excinfo = _pytest._code.ExceptionInfo(rawexcinfo) # type: ignore[arg-type]
|
excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info(rawexcinfo) # type: ignore[arg-type]
|
||||||
# Invoke the attributes to trigger storing the traceback
|
# Invoke the attributes to trigger storing the traceback
|
||||||
# trial causes some issue there.
|
# trial causes some issue there.
|
||||||
excinfo.value
|
excinfo.value
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"""pytest: unit and functional testing with Python."""
|
"""pytest: unit and functional testing with Python."""
|
||||||
from . import collect
|
from . import collect
|
||||||
from _pytest import __version__
|
from _pytest import __version__
|
||||||
|
from _pytest._code import ExceptionInfo
|
||||||
from _pytest.assertion import register_assert_rewrite
|
from _pytest.assertion import register_assert_rewrite
|
||||||
from _pytest.cacheprovider import Cache
|
from _pytest.cacheprovider import Cache
|
||||||
from _pytest.capture import CaptureFixture
|
from _pytest.capture import CaptureFixture
|
||||||
|
@ -79,6 +80,7 @@ __all__ = [
|
||||||
"console_main",
|
"console_main",
|
||||||
"deprecated_call",
|
"deprecated_call",
|
||||||
"exit",
|
"exit",
|
||||||
|
"ExceptionInfo",
|
||||||
"ExitCode",
|
"ExitCode",
|
||||||
"fail",
|
"fail",
|
||||||
"File",
|
"File",
|
||||||
|
|
|
@ -377,24 +377,32 @@ def test_testcase_adderrorandfailure_defers(pytester: Pytester, type: str) -> No
|
||||||
def test_testcase_custom_exception_info(pytester: Pytester, type: str) -> None:
|
def test_testcase_custom_exception_info(pytester: Pytester, type: str) -> None:
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
from typing import Generic, TypeVar
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
import py, pytest
|
import pytest, _pytest._code
|
||||||
import _pytest._code
|
|
||||||
class MyTestCase(TestCase):
|
class MyTestCase(TestCase):
|
||||||
def run(self, result):
|
def run(self, result):
|
||||||
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
||||||
# we fake an incompatible exception info
|
# We fake an incompatible exception info.
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
class FakeExceptionInfo(Generic[TypeVar("E")]):
|
||||||
mp = MonkeyPatch()
|
def __init__(self, *args, **kwargs):
|
||||||
def t(*args):
|
|
||||||
mp.undo()
|
mp.undo()
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
mp.setattr(_pytest._code, 'ExceptionInfo', t)
|
@classmethod
|
||||||
|
def from_current(cls):
|
||||||
|
return cls()
|
||||||
|
@classmethod
|
||||||
|
def from_exc_info(cls, *args, **kwargs):
|
||||||
|
return cls()
|
||||||
|
mp = pytest.MonkeyPatch()
|
||||||
|
mp.setattr(_pytest._code, 'ExceptionInfo', FakeExceptionInfo)
|
||||||
try:
|
try:
|
||||||
excinfo = excinfo._excinfo
|
excinfo = excinfo._excinfo
|
||||||
result.add%(type)s(self, excinfo)
|
result.add%(type)s(self, excinfo)
|
||||||
finally:
|
finally:
|
||||||
mp.undo()
|
mp.undo()
|
||||||
|
|
||||||
def test_hello(self):
|
def test_hello(self):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue