Skip to content

Commit 1c5675c

Browse files
Do the improvement
1 parent 9ccae9a commit 1c5675c

File tree

3 files changed

+105
-21
lines changed

3 files changed

+105
-21
lines changed

src/_pytest/doctest.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,16 @@ def __init__(
261261
self.runner = runner
262262
self.dtest = dtest
263263
self.obj = None
264-
self.fixture_request: Optional[FixtureRequest] = None
264+
265+
def func() -> None:
266+
pass
267+
268+
self.funcargs: Dict[str, object] = {} # type: ignore[attr-defined]
269+
fm = self.session._fixturemanager
270+
self._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
271+
node=self, func=func, cls=None, funcargs=False
272+
)
273+
self._request = FixtureRequest(self, _ispytest=True)
265274

266275
@classmethod
267276
def from_parent( # type: ignore
@@ -278,9 +287,9 @@ def from_parent( # type: ignore
278287

279288
def setup(self) -> None:
280289
if self.dtest is not None:
281-
self.fixture_request = _setup_fixtures(self)
282-
globs = dict(getfixture=self.fixture_request.getfixturevalue)
283-
for name, value in self.fixture_request.getfixturevalue(
290+
self._request._fillfixtures()
291+
globs = dict(getfixture=self._request.getfixturevalue)
292+
for name, value in self._request.getfixturevalue(
284293
"doctest_namespace"
285294
).items():
286295
globs[name] = value
@@ -571,22 +580,6 @@ def _find(
571580
)
572581

573582

574-
def _setup_fixtures(doctest_item: DoctestItem) -> FixtureRequest:
575-
"""Used by DoctestTextfile and DoctestItem to setup fixture information."""
576-
577-
def func() -> None:
578-
pass
579-
580-
doctest_item.funcargs = {} # type: ignore[attr-defined]
581-
fm = doctest_item.session._fixturemanager
582-
doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
583-
node=doctest_item, func=func, cls=None, funcargs=False
584-
)
585-
fixture_request = FixtureRequest(doctest_item, _ispytest=True)
586-
fixture_request._fillfixtures()
587-
return fixture_request
588-
589-
590583
def _init_checker_class() -> Type["doctest.OutputChecker"]:
591584
import doctest
592585
import re

src/_pytest/fixtures.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ def fail_fixturefunc(fixturefunc, msg: str) -> NoReturn:
891891

892892

893893
def call_fixture_func(
894-
fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs
894+
fixturefunc: "_FixtureFunc[FixtureValue]", request: SubRequest, kwargs
895895
) -> FixtureValue:
896896
if is_generator(fixturefunc):
897897
fixturefunc = cast(
@@ -1679,3 +1679,17 @@ def _matchfactories(
16791679
for fixturedef in fixturedefs:
16801680
if fixturedef.baseid in parentnodeids:
16811681
yield fixturedef
1682+
1683+
1684+
def pytest_runtest_teardown(item: nodes.Item, nextitem: nodes.Item) -> None:
1685+
if (
1686+
type(item).__name__ != "TestCaseFunction"
1687+
and hasattr(item, "_request") # For YamlItem in testing/
1688+
and nextitem
1689+
):
1690+
for fixture_name, fixture_def in item._request._fixture_defs.items():
1691+
if (
1692+
fixture_def._scope in HIGH_SCOPES
1693+
and fixture_name not in nextitem._request.fixturenames # type: ignore[attr-defined]
1694+
):
1695+
item.addfinalizer(functools.partial(fixture_def.finish, item._request))

testing/python/fixtures.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from _pytest.pytester import get_public_names
1313
from _pytest.pytester import Pytester
1414
from _pytest.python import Function
15+
from _pytest.scope import HIGH_SCOPES
1516

1617

1718
def test_getfuncargnames_functions():
@@ -4472,3 +4473,79 @@ def test_fixt(custom):
44724473
result.assert_outcomes(errors=1)
44734474
result.stdout.fnmatch_lines([expected])
44744475
assert result.ret == ExitCode.TESTS_FAILED
4476+
4477+
4478+
def test_teardown_high_scope_fixture_at_last_dependent_item(pytester: Pytester) -> None:
4479+
pytester.makepyfile(
4480+
**{
4481+
"tests/conftest.py": "import pytest\n"
4482+
+ "\n".join(
4483+
[
4484+
f"""
4485+
@pytest.fixture(scope='{scope.value}', params=[None])
4486+
def {scope.value}_scope_fixture(request):
4487+
yield None
4488+
print("Tearing down {scope.value}_scope_fixture")
4489+
"""
4490+
for scope in HIGH_SCOPES
4491+
]
4492+
),
4493+
"tests/test_module_a.py": """
4494+
class TestClass:
4495+
def test_class1(self, class_scope_fixture):
4496+
pass
4497+
4498+
def test_class2(self):
4499+
print("class_scope_fixture should have been torn down")
4500+
4501+
def teardown_class(self):
4502+
print("Tearing down TestClass")
4503+
4504+
def test_module1(module_scope_fixture):
4505+
pass
4506+
4507+
4508+
def test_module2():
4509+
print("module_scope_fixture should have been torn down")
4510+
4511+
def teardown_module():
4512+
print("Tearing down test_module_a")
4513+
4514+
def test_package1(package_scope_fixture):
4515+
pass
4516+
""",
4517+
"tests/test_module_b.py": """
4518+
import pytest
4519+
4520+
def test_package2():
4521+
print("package_scope_fixture should have been torn down")
4522+
4523+
def test_session1(session_scope_fixture):
4524+
pass
4525+
4526+
def test_session2():
4527+
print("session_scope_fixture should have been torn down")
4528+
""",
4529+
"tests/__init__.py": """
4530+
def teardown_module():
4531+
print("Tearing down package tests")
4532+
""",
4533+
}
4534+
)
4535+
result = pytester.runpytest("-s")
4536+
assert result.ret == 0
4537+
result.stdout.fnmatch_lines(
4538+
[
4539+
"*Tearing down class_scope_fixture*",
4540+
"*class_scope_fixture should have been torn down*",
4541+
"*Tearing down TestClass*",
4542+
"*Tearing down module_scope_fixture*",
4543+
"*module_scope_fixture should have been torn down*",
4544+
"*Tearing down test_module_a*",
4545+
"*Tearing down package_scope_fixture*",
4546+
"*package_scope_fixture should have been torn down*",
4547+
"*Tearing down session_scope_fixture*",
4548+
"*session_scope_fixture should have been torn down*",
4549+
"*Tearing down package tests*",
4550+
]
4551+
)

0 commit comments

Comments
 (0)