Merge pull request #8323 from bluetech/setupstate-refactor-3

runner: a few more tweaks to SetupState
This commit is contained in:
Ran Benita 2021-02-08 13:56:04 +02:00 committed by GitHub
commit 1003beaffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 26 deletions

View File

@ -372,7 +372,7 @@ def _fill_fixtures_impl(function: "Function") -> None:
fi = fm.getfixtureinfo(function.parent, function.obj, None)
function._fixtureinfo = fi
request = function._request = FixtureRequest(function, _ispytest=True)
fm.session._setupstate.prepare(function)
fm.session._setupstate.setup(function)
request._fillfixtures()
# Prune out funcargs for jstests.
newfuncargs = {}

View File

@ -509,9 +509,9 @@ def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None:
:param nextitem:
The scheduled-to-be-next test item (None if no further test item is
scheduled). This argument can be used to perform exact teardowns,
i.e. calling just enough finalizers so that nextitem only needs to
call setup-functions.
scheduled). This argument is used to perform exact teardowns, i.e.
calling just enough finalizers so that nextitem only needs to call
setup functions.
"""

View File

@ -120,6 +120,8 @@ def runtestprotocol(
) -> List[TestReport]:
hasrequest = hasattr(item, "_request")
if hasrequest and not item._request: # type: ignore[attr-defined]
# This only happens if the item is re-run, as is done by
# pytest-rerunfailures.
item._initrequest() # type: ignore[attr-defined]
rep = call_and_report(item, "setup", log)
reports = [rep]
@ -151,7 +153,7 @@ def show_test_item(item: Item) -> None:
def pytest_runtest_setup(item: Item) -> None:
_update_current_test_var(item, "setup")
item.session._setupstate.prepare(item)
item.session._setupstate.setup(item)
def pytest_runtest_call(item: Item) -> None:
@ -417,7 +419,7 @@ class SetupState:
[]
During the setup phase of item1, prepare(item1) is called. What it does
During the setup phase of item1, setup(item1) is called. What it does
is:
push session to stack, run session.setup()
@ -441,7 +443,7 @@ class SetupState:
[session]
During the setup phase of item2, prepare(item2) is called. What it does
During the setup phase of item2, setup(item2) is called. What it does
is:
push mod2 to stack, run mod2.setup()
@ -477,23 +479,26 @@ class SetupState:
],
] = {}
def prepare(self, item: Item) -> None:
def setup(self, item: Item) -> None:
"""Setup objects along the collector chain to the item."""
needed_collectors = item.listchain()
# If a collector fails its setup, fail its entire subtree of items.
# The setup is not retried for each item - the same exception is used.
for col, (finalizers, prepare_exc) in self.stack.items():
if prepare_exc:
raise prepare_exc
for col, (finalizers, exc) in self.stack.items():
assert col in needed_collectors, "previous item was not torn down properly"
if exc:
raise exc
needed_collectors = item.listchain()
for col in needed_collectors[len(self.stack) :]:
assert col not in self.stack
# Push onto the stack.
self.stack[col] = ([col.teardown], None)
try:
col.setup()
except TEST_OUTCOME as e:
self.stack[col] = (self.stack[col][0], e)
raise e
except TEST_OUTCOME as exc:
self.stack[col] = (self.stack[col][0], exc)
raise exc
def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None:
"""Attach a finalizer to the given node.
@ -517,7 +522,7 @@ class SetupState:
while self.stack:
if list(self.stack.keys()) == needed_collectors[: len(self.stack)]:
break
node, (finalizers, prepare_exc) = self.stack.popitem()
node, (finalizers, _) = self.stack.popitem()
while finalizers:
fin = finalizers.pop()
try:

View File

@ -131,7 +131,7 @@ class TestFillFixtures:
item = pytester.getitem(Path("test_funcarg_basic.py"))
assert isinstance(item, Function)
# Execute's item's setup, which fills fixtures.
item.session._setupstate.prepare(item)
item.session._setupstate.setup(item)
del item.funcargs["request"]
assert len(get_public_names(item.funcargs)) == 2
assert item.funcargs["some"] == "test_func"
@ -827,7 +827,7 @@ class TestRequestBasic:
req = item._request
# Execute item's setup.
item.session._setupstate.prepare(item)
item.session._setupstate.setup(item)
with pytest.raises(pytest.FixtureLookupError):
req.getfixturevalue("notexists")
@ -855,7 +855,7 @@ class TestRequestBasic:
"""
)
assert isinstance(item, Function)
item.session._setupstate.prepare(item)
item.session._setupstate.setup(item)
item._request._fillfixtures()
# successively check finalization calls
parent = item.getparent(pytest.Module)

View File

@ -25,7 +25,7 @@ class TestSetupState:
item = pytester.getitem("def test_func(): pass")
ss = item.session._setupstate
values = [1]
ss.prepare(item)
ss.setup(item)
ss.addfinalizer(values.pop, item)
assert values
ss.teardown_exact(None)
@ -34,7 +34,7 @@ class TestSetupState:
def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None:
item = pytester.getitem("def test_func(): pass")
ss = item.session._setupstate
ss.prepare(item)
ss.setup(item)
ss.teardown_exact(None)
ss.teardown_exact(None)
ss.teardown_exact(None)
@ -49,9 +49,9 @@ class TestSetupState:
)
ss = item.session._setupstate
with pytest.raises(ValueError):
ss.prepare(item)
ss.setup(item)
with pytest.raises(ValueError):
ss.prepare(item)
ss.setup(item)
def test_teardown_multiple_one_fails(self, pytester: Pytester) -> None:
r = []
@ -67,7 +67,7 @@ class TestSetupState:
item = pytester.getitem("def test_func(): pass")
ss = item.session._setupstate
ss.prepare(item)
ss.setup(item)
ss.addfinalizer(fin1, item)
ss.addfinalizer(fin2, item)
ss.addfinalizer(fin3, item)
@ -87,7 +87,7 @@ class TestSetupState:
item = pytester.getitem("def test_func(): pass")
ss = item.session._setupstate
ss.prepare(item)
ss.setup(item)
ss.addfinalizer(fin1, item)
ss.addfinalizer(fin2, item)
with pytest.raises(Exception) as err:
@ -106,7 +106,7 @@ class TestSetupState:
item = pytester.getitem("def test_func(): pass")
mod = item.listchain()[-2]
ss = item.session._setupstate
ss.prepare(item)
ss.setup(item)
ss.addfinalizer(fin_module, mod)
ss.addfinalizer(fin_func, item)
with pytest.raises(Exception, match="oops1"):