diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 6a57fffd1..0521d7361 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -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 = {} diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 41c12a2cc..b0b8fd53d 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -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. """ diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index ae76a2472..e43dd2dc8 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -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: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 3d78ebf58..3d5099c53 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -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) diff --git a/testing/test_runner.py b/testing/test_runner.py index e3f286307..abb87c6d3 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -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"):