Skip to content

Commit db89537

Browse files
authored
Merge pull request #13801 from bluetech/rm-pseudo-fixturedef-2
fixtures: replace PseudoFixtureDef with RequestFixtureDef which is a real FixtureDef
2 parents c8a4618 + 6d17c85 commit db89537

File tree

2 files changed

+31
-22
lines changed

2 files changed

+31
-22
lines changed

src/_pytest/fixtures.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282

8383

8484
# The value of the fixture -- return/yield of the fixture function (type variable).
85-
FixtureValue = TypeVar("FixtureValue")
85+
FixtureValue = TypeVar("FixtureValue", covariant=True)
8686
# The type of the fixture function (type variable).
8787
FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object])
8888
# The type of a fixture function (type alias generic in fixture value).
@@ -106,12 +106,6 @@
106106
)
107107

108108

109-
@dataclasses.dataclass(frozen=True)
110-
class PseudoFixtureDef(Generic[FixtureValue]):
111-
cached_result: _FixtureCachedResult[FixtureValue]
112-
_scope: Scope
113-
114-
115109
def pytest_sessionstart(session: Session) -> None:
116110
session._fixturemanager = FixtureManager(session)
117111

@@ -420,7 +414,7 @@ def scope(self) -> _ScopeName:
420414
@abc.abstractmethod
421415
def _check_scope(
422416
self,
423-
requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object],
417+
requested_fixturedef: FixtureDef[object],
424418
requested_scope: Scope,
425419
) -> None:
426420
raise NotImplementedError()
@@ -559,12 +553,9 @@ def _iter_chain(self) -> Iterator[SubRequest]:
559553
yield current
560554
current = current._parent_request
561555

562-
def _get_active_fixturedef(
563-
self, argname: str
564-
) -> FixtureDef[object] | PseudoFixtureDef[object]:
556+
def _get_active_fixturedef(self, argname: str) -> FixtureDef[object]:
565557
if argname == "request":
566-
cached_result = (self, [0], None)
567-
return PseudoFixtureDef(cached_result, Scope.Function)
558+
return RequestFixtureDef(self)
568559

569560
# If we already finished computing a fixture by this name in this item,
570561
# return it.
@@ -696,7 +687,7 @@ def _scope(self) -> Scope:
696687

697688
def _check_scope(
698689
self,
699-
requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object],
690+
requested_fixturedef: FixtureDef[object],
700691
requested_scope: Scope,
701692
) -> None:
702693
# TopRequest always has function scope so always valid.
@@ -775,11 +766,9 @@ def node(self):
775766

776767
def _check_scope(
777768
self,
778-
requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object],
769+
requested_fixturedef: FixtureDef[object],
779770
requested_scope: Scope,
780771
) -> None:
781-
if isinstance(requested_fixturedef, PseudoFixtureDef):
782-
return
783772
if self._scope > requested_scope:
784773
# Try to report something helpful.
785774
argname = requested_fixturedef.argname
@@ -968,7 +957,6 @@ def _eval_scope_callable(
968957
return result
969958

970959

971-
@final
972960
class FixtureDef(Generic[FixtureValue]):
973961
"""A container for a fixture definition.
974962
@@ -1083,8 +1071,7 @@ def execute(self, request: SubRequest) -> FixtureValue:
10831071
# down first. This is generally handled by SetupState, but still currently
10841072
# needed when this fixture is not parametrized but depends on a parametrized
10851073
# fixture.
1086-
if not isinstance(fixturedef, PseudoFixtureDef):
1087-
requested_fixtures_that_should_finalize_us.append(fixturedef)
1074+
requested_fixtures_that_should_finalize_us.append(fixturedef)
10881075

10891076
# Check for (and return) cached value/exception.
10901077
if self.cached_result is not None:
@@ -1136,6 +1123,28 @@ def __repr__(self) -> str:
11361123
return f"<FixtureDef argname={self.argname!r} scope={self.scope!r} baseid={self.baseid!r}>"
11371124

11381125

1126+
class RequestFixtureDef(FixtureDef[FixtureRequest]):
1127+
"""A custom FixtureDef for the special "request" fixture.
1128+
1129+
A new one is generated on-demand whenever "request" is requested.
1130+
"""
1131+
1132+
def __init__(self, request: FixtureRequest) -> None:
1133+
super().__init__(
1134+
config=request.config,
1135+
baseid=None,
1136+
argname="request",
1137+
func=lambda: request,
1138+
scope=Scope.Function,
1139+
params=None,
1140+
_ispytest=True,
1141+
)
1142+
self.cached_result = (request, [0], None)
1143+
1144+
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
1145+
pass
1146+
1147+
11391148
def resolve_fixture_function(
11401149
fixturedef: FixtureDef[FixtureValue], request: FixtureRequest
11411150
) -> _FixtureFunc[FixtureValue]:

testing/python/fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ def test_request_garbage(self, pytester: Pytester) -> None:
750750
"""
751751
import sys
752752
import pytest
753-
from _pytest.fixtures import PseudoFixtureDef
753+
from _pytest.fixtures import RequestFixtureDef
754754
import gc
755755
756756
@pytest.fixture(autouse=True)
@@ -763,7 +763,7 @@ def something(request):
763763
764764
try:
765765
gc.collect()
766-
leaked = [x for _ in gc.garbage if isinstance(_, PseudoFixtureDef)]
766+
leaked = [x for _ in gc.garbage if isinstance(_, RequestFixtureDef)]
767767
assert leaked == []
768768
finally:
769769
gc.set_debug(original)

0 commit comments

Comments
 (0)