assertion: save/restore hooks on item (#6646)

This commit is contained in:
Daniel Hahler 2020-02-04 02:56:23 +01:00 committed by GitHub
parent 1480aa31a7
commit aa0328782f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 9 deletions

View File

@ -0,0 +1 @@
Assertion rewriting hooks are (re)stored for the current item, which fixes them being still used after e.g. pytester's :func:`testdir.runpytest <_pytest.pytester.Testdir.runpytest>` etc.

View File

@ -8,6 +8,7 @@ from _pytest.assertion import rewrite
from _pytest.assertion import truncate
from _pytest.assertion import util
from _pytest.compat import TYPE_CHECKING
from _pytest.config import hookimpl
if TYPE_CHECKING:
from _pytest.main import Session
@ -105,7 +106,8 @@ def pytest_collection(session: "Session") -> None:
assertstate.hook.set_session(session)
def pytest_runtest_setup(item):
@hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_protocol(item):
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks
The newinterpret and rewrite modules will use util._reprcompare if
@ -143,6 +145,7 @@ def pytest_runtest_setup(item):
return res
return None
saved_assert_hooks = util._reprcompare, util._assertion_pass
util._reprcompare = callbinrepr
if item.ihook.pytest_assertion_pass.get_hookimpls():
@ -154,10 +157,9 @@ def pytest_runtest_setup(item):
util._assertion_pass = call_assertion_pass_hook
yield
def pytest_runtest_teardown(item):
util._reprcompare = None
util._assertion_pass = None
util._reprcompare, util._assertion_pass = saved_assert_hooks
def pytest_sessionfinish(session):

View File

@ -27,7 +27,6 @@ from pluggy import HookspecMarker
from pluggy import PluginManager
import _pytest._code
import _pytest.assertion
import _pytest.deprecated
import _pytest.hookspec # the extension point definitions
from .exceptions import PrintHelp
@ -260,6 +259,8 @@ class PytestPluginManager(PluginManager):
"""
def __init__(self):
import _pytest.assertion
super().__init__("pytest")
# The objects are module objects, only used generically.
self._conftest_plugins = set() # type: Set[object]
@ -891,6 +892,8 @@ class Config:
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
mode = getattr(ns, "assertmode", "plain")
if mode == "rewrite":
import _pytest.assertion
try:
hook = _pytest.assertion.install_importhook(self)
except SystemError:

View File

@ -72,10 +72,19 @@ class TestImportHookInstallation:
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(
[
"E * AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
"E * assert"
" {'failed': 1, 'passed': 0, 'skipped': 0} =="
" {'failed': 0, 'passed': 1, 'skipped': 0}",
"> r.assertoutcome(passed=1)",
"E AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
"E assert {'failed': 1,... 'skipped': 0} == {'failed': 0,... 'skipped': 0}",
"E Omitting 1 identical items, use -vv to show",
"E Differing items:",
"E Use -v to get the full diff",
]
)
# XXX: unstable output.
result.stdout.fnmatch_lines_random(
[
"E {'failed': 1} != {'failed': 0}",
"E {'passed': 0} != {'passed': 1}",
]
)