diff --git a/AUTHORS b/AUTHORS index 229e4315078..e2e0243de8d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -143,6 +143,7 @@ Denivy Braiam Rück Deysha Rivera Dheeraj C K Dhiren Serai +Dresden Diego Russo Dima Gerasimov Dmitry Dygalo diff --git a/changelog/14775.bugfix.rst b/changelog/14775.bugfix.rst new file mode 100644 index 00000000000..4b9ccc49df3 --- /dev/null +++ b/changelog/14775.bugfix.rst @@ -0,0 +1 @@ +Fixed an internal ``AssertionError`` that could occur when a class-scoped fixture defined as an instance method raised its deprecation warning as an error. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 1b1591d1f83..c24e7f0cc7a 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1205,11 +1205,6 @@ def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._finalizers.append(finalizer) def finish(self, request: SubRequest) -> None: - if self.cached_result is None: - # Already finished. It is assumed that finalizers cannot be added in - # this state. - return - exceptions: list[BaseException] = [] while self._finalizers: fin = self._finalizers.pop() @@ -1280,11 +1275,13 @@ def execute(self, request: SubRequest) -> FixtureValue: # Register the pytest_fixture_post_finalizer as the first finalizer, # which is executed last. assert not self._finalizers - self.addfinalizer( - lambda: request.node.ihook.pytest_fixture_post_finalizer( + + def post_finalizer() -> None: + request.node.ihook.pytest_fixture_post_finalizer( fixturedef=self, request=request ) - ) + + self.addfinalizer(post_finalizer) ihook = request.node.ihook try: @@ -1293,6 +1290,9 @@ def execute(self, request: SubRequest) -> FixtureValue: result: FixtureValue = ihook.pytest_fixture_setup( fixturedef=self, request=request ) + except BaseException: + self._finalizers.remove(post_finalizer) + raise finally: # Schedule our finalizer, even if the setup failed. request.node.addfinalizer(finalizer) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 8eed1fb3149..eb7db1eefcd 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -99,15 +99,19 @@ def fix(self): def test_foo(self, fix): pass + + def test_bar(self, fix): + pass """ ) result = pytester.runpytest("-Werror::pytest.PytestRemovedIn10Warning") - result.assert_outcomes(errors=1) + result.assert_outcomes(errors=2) result.stdout.fnmatch_lines( [ "*PytestRemovedIn10Warning: Class-scoped fixtures defined as instance methods*" ] ) + result.stdout.no_fnmatch_line("*AssertionError*") def test_class_scope_classmethod_fixture_not_deprecated(pytester: Pytester) -> None: