Switch to new-style pluggy hook wrappers

Fix #11122.
This commit is contained in:
Ran Benita
2023-06-12 22:30:06 +03:00
parent 7008385253
commit b41acaea12
34 changed files with 334 additions and 275 deletions

View File

@@ -112,8 +112,8 @@ def pytest_collection(session: "Session") -> None:
assertstate.hook.set_session(session)
@hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
@hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks.
The rewrite module will use util._reprcompare if it exists to use custom
@@ -162,10 +162,11 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
util._assertion_pass = call_assertion_pass_hook
yield
util._reprcompare, util._assertion_pass = saved_assert_hooks
util._config = None
try:
return (yield)
finally:
util._reprcompare, util._assertion_pass = saved_assert_hooks
util._config = None
def pytest_sessionfinish(session: "Session") -> None:

View File

@@ -217,12 +217,12 @@ class LFPluginCollWrapper:
self.lfplugin = lfplugin
self._collected_at_least_one_failure = False
@hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector: nodes.Collector):
@hookimpl(wrapper=True)
def pytest_make_collect_report(
self, collector: nodes.Collector
) -> Generator[None, CollectReport, CollectReport]:
res = yield
if isinstance(collector, (Session, Package)):
out = yield
res: CollectReport = out.get_result()
# Sort any lf-paths to the beginning.
lf_paths = self.lfplugin._last_failed_paths
@@ -240,19 +240,16 @@ class LFPluginCollWrapper:
key=sort_key,
reverse=True,
)
return
elif isinstance(collector, File):
if collector.path in self.lfplugin._last_failed_paths:
out = yield
res = out.get_result()
result = res.result
lastfailed = self.lfplugin.lastfailed
# Only filter with known failures.
if not self._collected_at_least_one_failure:
if not any(x.nodeid in lastfailed for x in result):
return
return res
self.lfplugin.config.pluginmanager.register(
LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip"
)
@@ -268,8 +265,8 @@ class LFPluginCollWrapper:
# Keep all sub-collectors.
or isinstance(x, nodes.Collector)
]
return
yield
return res
class LFPluginCollSkipfiles:
@@ -342,14 +339,14 @@ class LFPlugin:
else:
self.lastfailed[report.nodeid] = True
@hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, config: Config, items: List[nodes.Item]
) -> Generator[None, None, None]:
yield
res = yield
if not self.active:
return
return res
if self.lastfailed:
previously_failed = []
@@ -394,6 +391,8 @@ class LFPlugin:
else:
self._report_status += "not deselecting items."
return res
def pytest_sessionfinish(self, session: Session) -> None:
config = self.config
if config.getoption("cacheshow") or hasattr(config, "workerinput"):
@@ -414,11 +413,11 @@ class NFPlugin:
assert config.cache is not None
self.cached_nodeids = set(config.cache.get("cache/nodeids", []))
@hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, items: List[nodes.Item]
) -> Generator[None, None, None]:
yield
res = yield
if self.active:
new_items: Dict[str, nodes.Item] = {}
@@ -436,6 +435,8 @@ class NFPlugin:
else:
self.cached_nodeids.update(item.nodeid for item in items)
return res
def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]:
return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) # type: ignore[no-any-return]

View File

@@ -36,6 +36,7 @@ from _pytest.fixtures import SubRequest
from _pytest.nodes import Collector
from _pytest.nodes import File
from _pytest.nodes import Item
from _pytest.reports import CollectReport
_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"]
@@ -130,8 +131,8 @@ def _windowsconsoleio_workaround(stream: TextIO) -> None:
sys.stderr = _reopen_stdio(sys.stderr, "wb")
@hookimpl(hookwrapper=True)
def pytest_load_initial_conftests(early_config: Config):
@hookimpl(wrapper=True)
def pytest_load_initial_conftests(early_config: Config) -> Generator[None, None, None]:
ns = early_config.known_args_namespace
if ns.capture == "fd":
_windowsconsoleio_workaround(sys.stdout)
@@ -145,12 +146,16 @@ def pytest_load_initial_conftests(early_config: Config):
# Finally trigger conftest loading but while capturing (issue #93).
capman.start_global_capturing()
outcome = yield
capman.suspend_global_capture()
if outcome.excinfo is not None:
try:
try:
yield
finally:
capman.suspend_global_capture()
except BaseException:
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stderr.write(err)
raise
# IO Helpers.
@@ -841,41 +846,45 @@ class CaptureManager:
self.deactivate_fixture()
self.suspend_global_capture(in_=False)
out, err = self.read_global_capture()
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
out, err = self.read_global_capture()
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
# Hooks
@hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector: Collector):
@hookimpl(wrapper=True)
def pytest_make_collect_report(
self, collector: Collector
) -> Generator[None, CollectReport, CollectReport]:
if isinstance(collector, File):
self.resume_global_capture()
outcome = yield
self.suspend_global_capture()
try:
rep = yield
finally:
self.suspend_global_capture()
out, err = self.read_global_capture()
rep = outcome.get_result()
if out:
rep.sections.append(("Captured stdout", out))
if err:
rep.sections.append(("Captured stderr", err))
else:
yield
rep = yield
return rep
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("setup", item):
yield
return (yield)
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("call", item):
yield
return (yield)
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("teardown", item):
yield
return (yield)
@hookimpl(tryfirst=True)
def pytest_keyboard_interrupt(self) -> None:

View File

@@ -1341,12 +1341,14 @@ class Config:
else:
raise
@hookimpl(hookwrapper=True)
def pytest_collection(self) -> Generator[None, None, None]:
@hookimpl(wrapper=True)
def pytest_collection(self) -> Generator[None, object, object]:
# Validate invalid ini keys after collection is done so we take in account
# options added by late-loading conftest files.
yield
self._validate_config_options()
try:
return (yield)
finally:
self._validate_config_options()
def _checkversion(self) -> None:
import pytest
@@ -1448,7 +1450,7 @@ class Config:
"""Issue and handle a warning during the "configure" stage.
During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item``
function because it is not possible to have hookwrappers around ``pytest_configure``.
function because it is not possible to have hook wrappers around ``pytest_configure``.
This function is mainly intended for plugins that need to issue warnings during
``pytest_configure`` (or similar stages).

View File

@@ -304,10 +304,10 @@ class PdbInvoke:
class PdbTrace:
@hookimpl(hookwrapper=True)
def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, None, None]:
@hookimpl(wrapper=True)
def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, object, object]:
wrap_pytest_function_for_tracing(pyfuncitem)
yield
return (yield)
def wrap_pytest_function_for_tracing(pyfuncitem):

View File

@@ -62,8 +62,8 @@ def get_timeout_config_value(config: Config) -> float:
return float(config.getini("faulthandler_timeout") or 0.0)
@pytest.hookimpl(hookwrapper=True, trylast=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
@pytest.hookimpl(wrapper=True, trylast=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
timeout = get_timeout_config_value(item.config)
if timeout > 0:
import faulthandler
@@ -71,11 +71,11 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
stderr = item.config.stash[fault_handler_stderr_fd_key]
faulthandler.dump_traceback_later(timeout, file=stderr)
try:
yield
return (yield)
finally:
faulthandler.cancel_dump_traceback_later()
else:
yield
return (yield)
@pytest.hookimpl(tryfirst=True)

View File

@@ -2,6 +2,7 @@
import os
import sys
from argparse import Action
from typing import Generator
from typing import List
from typing import Optional
from typing import Union
@@ -97,10 +98,9 @@ def pytest_addoption(parser: Parser) -> None:
)
@pytest.hookimpl(hookwrapper=True)
def pytest_cmdline_parse():
outcome = yield
config: Config = outcome.get_result()
@pytest.hookimpl(wrapper=True)
def pytest_cmdline_parse() -> Generator[None, Config, Config]:
config = yield
if config.option.debug:
# --debug | --debug <file.log> was provided.
@@ -128,6 +128,8 @@ def pytest_cmdline_parse():
config.add_cleanup(unset_tracing)
return config
def showversion(config: Config) -> None:
if config.option.version > 1:

View File

@@ -60,7 +60,7 @@ def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None:
:param pytest.PytestPluginManager pluginmanager: The pytest plugin manager.
.. note::
This hook is incompatible with ``hookwrapper=True``.
This hook is incompatible with hook wrappers.
"""
@@ -74,7 +74,7 @@ def pytest_plugin_registered(
:param pytest.PytestPluginManager manager: pytest plugin manager.
.. note::
This hook is incompatible with ``hookwrapper=True``.
This hook is incompatible with hook wrappers.
"""
@@ -113,7 +113,7 @@ def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") ->
attribute or can be retrieved as the ``pytestconfig`` fixture.
.. note::
This hook is incompatible with ``hookwrapper=True``.
This hook is incompatible with hook wrappers.
"""
@@ -128,7 +128,7 @@ def pytest_configure(config: "Config") -> None:
imported.
.. note::
This hook is incompatible with ``hookwrapper=True``.
This hook is incompatible with hook wrappers.
:param pytest.Config config: The pytest config object.
"""

View File

@@ -738,27 +738,26 @@ class LoggingPlugin:
return True
@hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(wrapper=True, tryfirst=True)
def pytest_sessionstart(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("sessionstart")
with catching_logs(self.log_cli_handler, level=self.log_cli_level):
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
return (yield)
@hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("collection")
with catching_logs(self.log_cli_handler, level=self.log_cli_level):
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
return (yield)
@hookimpl(hookwrapper=True)
def pytest_runtestloop(self, session: Session) -> Generator[None, None, None]:
@hookimpl(wrapper=True)
def pytest_runtestloop(self, session: Session) -> Generator[None, object, object]:
if session.config.option.collectonly:
yield
return
return (yield)
if self._log_cli_enabled() and self._config.getoption("verbose") < 1:
# The verbose flag is needed to avoid messy test progress output.
@@ -766,7 +765,7 @@ class LoggingPlugin:
with catching_logs(self.log_cli_handler, level=self.log_cli_level):
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield # Run all the tests.
return (yield) # Run all the tests.
@hookimpl
def pytest_runtest_logstart(self) -> None:
@@ -791,12 +790,13 @@ class LoggingPlugin:
item.stash[caplog_records_key][when] = caplog_handler.records
item.stash[caplog_handler_key] = caplog_handler
yield
try:
yield
finally:
log = report_handler.stream.getvalue().strip()
item.add_report_section(when, "log", log)
log = report_handler.stream.getvalue().strip()
item.add_report_section(when, "log", log)
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("setup")
@@ -804,31 +804,33 @@ class LoggingPlugin:
item.stash[caplog_records_key] = empty
yield from self._runtest_for(item, "setup")
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("call")
yield from self._runtest_for(item, "call")
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("teardown")
yield from self._runtest_for(item, "teardown")
del item.stash[caplog_records_key]
del item.stash[caplog_handler_key]
try:
yield from self._runtest_for(item, "teardown")
finally:
del item.stash[caplog_records_key]
del item.stash[caplog_handler_key]
@hookimpl
def pytest_runtest_logfinish(self) -> None:
self.log_cli_handler.set_when("finish")
@hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(wrapper=True, tryfirst=True)
def pytest_sessionfinish(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("sessionfinish")
with catching_logs(self.log_cli_handler, level=self.log_cli_level):
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
return (yield)
@hookimpl
def pytest_unconfigure(self) -> None:

View File

@@ -160,29 +160,31 @@ class LsofFdLeakChecker:
else:
return True
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]:
@hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_protocol(self, item: Item) -> Generator[None, object, object]:
lines1 = self.get_open_files()
yield
if hasattr(sys, "pypy_version_info"):
gc.collect()
lines2 = self.get_open_files()
try:
return (yield)
finally:
if hasattr(sys, "pypy_version_info"):
gc.collect()
lines2 = self.get_open_files()
new_fds = {t[0] for t in lines2} - {t[0] for t in lines1}
leaked_files = [t for t in lines2 if t[0] in new_fds]
if leaked_files:
error = [
"***** %s FD leakage detected" % len(leaked_files),
*(str(f) for f in leaked_files),
"*** Before:",
*(str(f) for f in lines1),
"*** After:",
*(str(f) for f in lines2),
"***** %s FD leakage detected" % len(leaked_files),
"*** function %s:%s: %s " % item.location,
"See issue #2366",
]
item.warn(PytestWarning("\n".join(error)))
new_fds = {t[0] for t in lines2} - {t[0] for t in lines1}
leaked_files = [t for t in lines2 if t[0] in new_fds]
if leaked_files:
error = [
"***** %s FD leakage detected" % len(leaked_files),
*(str(f) for f in leaked_files),
"*** Before:",
*(str(f) for f in lines1),
"*** After:",
*(str(f) for f in lines2),
"***** %s FD leakage detected" % len(leaked_files),
"*** function %s:%s: %s " % item.location,
"See issue #2366",
]
item.warn(PytestWarning("\n".join(error)))
# used at least by pytest-xdist plugin

View File

@@ -249,6 +249,9 @@ class TestReport(BaseReport):
"""
__test__ = False
# Defined by skipping plugin.
# xfail reason if xfailed, otherwise not defined. Use hasattr to distinguish.
wasxfail: str
def __init__(
self,

View File

@@ -28,24 +28,26 @@ def pytest_addoption(parser: Parser) -> None:
)
@pytest.hookimpl(hookwrapper=True)
@pytest.hookimpl(wrapper=True)
def pytest_fixture_setup(
fixturedef: FixtureDef[object], request: SubRequest
) -> Generator[None, None, None]:
yield
if request.config.option.setupshow:
if hasattr(request, "param"):
# Save the fixture parameter so ._show_fixture_action() can
# display it now and during the teardown (in .finish()).
if fixturedef.ids:
if callable(fixturedef.ids):
param = fixturedef.ids(request.param)
) -> Generator[None, object, object]:
try:
return (yield)
finally:
if request.config.option.setupshow:
if hasattr(request, "param"):
# Save the fixture parameter so ._show_fixture_action() can
# display it now and during the teardown (in .finish()).
if fixturedef.ids:
if callable(fixturedef.ids):
param = fixturedef.ids(request.param)
else:
param = fixturedef.ids[request.param_index]
else:
param = fixturedef.ids[request.param_index]
else:
param = request.param
fixturedef.cached_param = param # type: ignore[attr-defined]
_show_fixture_action(fixturedef, "SETUP")
param = request.param
fixturedef.cached_param = param # type: ignore[attr-defined]
_show_fixture_action(fixturedef, "SETUP")
def pytest_fixture_post_finalizer(fixturedef: FixtureDef[object]) -> None:

View File

@@ -19,6 +19,7 @@ from _pytest.outcomes import fail
from _pytest.outcomes import skip
from _pytest.outcomes import xfail
from _pytest.reports import BaseReport
from _pytest.reports import TestReport
from _pytest.runner import CallInfo
from _pytest.stash import StashKey
@@ -243,7 +244,7 @@ def pytest_runtest_setup(item: Item) -> None:
xfail("[NOTRUN] " + xfailed.reason)
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_runtest_call(item: Item) -> Generator[None, None, None]:
xfailed = item.stash.get(xfailed_key, None)
if xfailed is None:
@@ -252,18 +253,20 @@ def pytest_runtest_call(item: Item) -> Generator[None, None, None]:
if xfailed and not item.config.option.runxfail and not xfailed.run:
xfail("[NOTRUN] " + xfailed.reason)
yield
# The test run may have added an xfail mark dynamically.
xfailed = item.stash.get(xfailed_key, None)
if xfailed is None:
item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item)
try:
return (yield)
finally:
# The test run may have added an xfail mark dynamically.
xfailed = item.stash.get(xfailed_key, None)
if xfailed is None:
item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item)
@hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
outcome = yield
rep = outcome.get_result()
@hookimpl(wrapper=True)
def pytest_runtest_makereport(
item: Item, call: CallInfo[None]
) -> Generator[None, TestReport, TestReport]:
rep = yield
xfailed = item.stash.get(xfailed_key, None)
if item.config.option.runxfail:
pass # don't interfere
@@ -286,6 +289,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
else:
rep.outcome = "passed"
rep.wasxfail = xfailed.reason
return rep
def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]:

View File

@@ -15,7 +15,6 @@ from functools import partial
from pathlib import Path
from typing import Any
from typing import Callable
from typing import cast
from typing import ClassVar
from typing import Dict
from typing import final
@@ -849,12 +848,11 @@ class TerminalReporter:
for line in doc.splitlines():
self._tw.line("{}{}".format(indent + " ", line))
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_sessionfinish(
self, session: "Session", exitstatus: Union[int, ExitCode]
):
outcome = yield
outcome.get_result()
) -> Generator[None, None, None]:
result = yield
self._tw.line("")
summary_exit_codes = (
ExitCode.OK,
@@ -875,17 +873,20 @@ class TerminalReporter:
elif session.shouldstop:
self.write_sep("!", str(session.shouldstop), red=True)
self.summary_stats()
return result
@hookimpl(hookwrapper=True)
@hookimpl(wrapper=True)
def pytest_terminal_summary(self) -> Generator[None, None, None]:
self.summary_errors()
self.summary_failures()
self.summary_warnings()
self.summary_passes()
yield
self.short_test_summary()
# Display any extra warnings from teardown here (if any).
self.summary_warnings()
try:
return (yield)
finally:
self.short_test_summary()
# Display any extra warnings from teardown here (if any).
self.summary_warnings()
def pytest_keyboard_interrupt(self, excinfo: ExceptionInfo[BaseException]) -> None:
self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True)
@@ -1466,7 +1467,7 @@ def _get_raw_skip_reason(report: TestReport) -> str:
The string is just the part given by the user.
"""
if hasattr(report, "wasxfail"):
reason = cast(str, report.wasxfail)
reason = report.wasxfail
if reason.startswith("reason: "):
reason = reason[len("reason: ") :]
return reason

View File

@@ -59,30 +59,34 @@ class catch_threading_exception:
def thread_exception_runtest_hook() -> Generator[None, None, None]:
with catch_threading_exception() as cm:
yield
if cm.args:
thread_name = "<unknown>" if cm.args.thread is None else cm.args.thread.name
msg = f"Exception in thread {thread_name}\n\n"
msg += "".join(
traceback.format_exception(
cm.args.exc_type,
cm.args.exc_value,
cm.args.exc_traceback,
try:
yield
finally:
if cm.args:
thread_name = (
"<unknown>" if cm.args.thread is None else cm.args.thread.name
)
)
warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg))
msg = f"Exception in thread {thread_name}\n\n"
msg += "".join(
traceback.format_exception(
cm.args.exc_type,
cm.args.exc_value,
cm.args.exc_traceback,
)
)
warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg))
@pytest.hookimpl(hookwrapper=True, trylast=True)
@pytest.hookimpl(wrapper=True, trylast=True)
def pytest_runtest_setup() -> Generator[None, None, None]:
yield from thread_exception_runtest_hook()
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_call() -> Generator[None, None, None]:
yield from thread_exception_runtest_hook()
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_teardown() -> Generator[None, None, None]:
yield from thread_exception_runtest_hook()

View File

@@ -28,7 +28,7 @@ from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Item
from _pytest.reports import CollectReport
from _pytest.reports import TestReport
from _pytest.stash import StashKey
tmppath_result_key = StashKey[Dict[str, bool]]()
@@ -309,10 +309,12 @@ def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]):
cleanup_dead_symlinks(basetemp)
@hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item: Item, call):
outcome = yield
result: CollectReport = outcome.get_result()
@hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_makereport(
item: Item, call
) -> Generator[None, TestReport, TestReport]:
rep = yield
assert rep.when is not None
empty: Dict[str, bool] = {}
item.stash.setdefault(tmppath_result_key, empty)[result.when] = result.passed
item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed
return rep

View File

@@ -376,8 +376,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None:
# Twisted trial support.
@hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
@hookimpl(wrapper=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules:
ut: Any = sys.modules["twisted.python.failure"]
Failure__init__ = ut.Failure.__init__
@@ -400,10 +400,13 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
Failure__init__(self, exc_value, exc_type, exc_tb)
ut.Failure.__init__ = excstore
yield
ut.Failure.__init__ = Failure__init__
try:
res = yield
finally:
ut.Failure.__init__ = Failure__init__
else:
yield
res = yield
return res
def check_testcase_implements_trial_reporter(done: List[int] = []) -> None:

View File

@@ -61,33 +61,35 @@ class catch_unraisable_exception:
def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
with catch_unraisable_exception() as cm:
yield
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
try:
yield
finally:
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
)
)
)
warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_setup() -> Generator[None, None, None]:
yield from unraisable_exception_runtest_hook()
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_call() -> Generator[None, None, None]:
yield from unraisable_exception_runtest_hook()
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_teardown() -> Generator[None, None, None]:
yield from unraisable_exception_runtest_hook()

View File

@@ -60,17 +60,18 @@ def catch_warnings_for_item(
for arg in mark.args:
warnings.filterwarnings(*parse_warning_filter(arg, escape=False))
yield
for warning_message in log:
ihook.pytest_warning_recorded.call_historic(
kwargs=dict(
warning_message=warning_message,
nodeid=nodeid,
when=when,
location=None,
try:
yield
finally:
for warning_message in log:
ihook.pytest_warning_recorded.call_historic(
kwargs=dict(
warning_message=warning_message,
nodeid=nodeid,
when=when,
location=None,
)
)
)
def warning_record_to_str(warning_message: warnings.WarningMessage) -> str:
@@ -103,24 +104,24 @@ def warning_record_to_str(warning_message: warnings.WarningMessage) -> str:
return msg
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
with catch_warnings_for_item(
config=item.config, ihook=item.ihook, when="runtest", item=item
):
yield
return (yield)
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(session: Session) -> Generator[None, None, None]:
@pytest.hookimpl(wrapper=True, tryfirst=True)
def pytest_collection(session: Session) -> Generator[None, object, object]:
config = session.config
with catch_warnings_for_item(
config=config, ihook=config.hook, when="collect", item=None
):
yield
return (yield)
@pytest.hookimpl(hookwrapper=True)
@pytest.hookimpl(wrapper=True)
def pytest_terminal_summary(
terminalreporter: TerminalReporter,
) -> Generator[None, None, None]:
@@ -128,23 +129,23 @@ def pytest_terminal_summary(
with catch_warnings_for_item(
config=config, ihook=config.hook, when="config", item=None
):
yield
return (yield)
@pytest.hookimpl(hookwrapper=True)
@pytest.hookimpl(wrapper=True)
def pytest_sessionfinish(session: Session) -> Generator[None, None, None]:
config = session.config
with catch_warnings_for_item(
config=config, ihook=config.hook, when="config", item=None
):
yield
return (yield)
@pytest.hookimpl(hookwrapper=True)
@pytest.hookimpl(wrapper=True)
def pytest_load_initial_conftests(
early_config: "Config",
) -> Generator[None, None, None]:
with catch_warnings_for_item(
config=early_config, ihook=early_config.hook, when="config", item=None
):
yield
return (yield)