6969from _pytest .warning_types import PytestWarning
7070
7171
72- if sys .version_info < (3 , 11 ):
73- from exceptiongroup import BaseExceptionGroup
74-
75-
7672if TYPE_CHECKING :
7773 from _pytest .python import CallSpec2
7874 from _pytest .python import Function
@@ -1035,7 +1031,6 @@ def __init__(
10351031 # If the fixture was executed, the current value of the fixture.
10361032 # Can change if the fixture is executed with different parameters.
10371033 self .cached_result : _FixtureCachedResult [FixtureValue ] | None = None
1038- self ._finalizers : Final [nodes .FinalizerStorage ] = {}
10391034 # The request object with which the fixture was set up.
10401035 self ._cached_request : SubRequest | None = None
10411036 # Handles to remove our finalizer from various scopes.
@@ -1050,39 +1045,20 @@ def scope(self) -> _ScopeName:
10501045 return self ._scope .value
10511046
10521047 def addfinalizer (self , finalizer : Callable [[], object ]) -> Callable [[], None ]:
1053- return nodes .append_finalizer (self ._finalizers , finalizer )
1048+ assert self ._cached_request is not None
1049+ setupstate = self ._cached_request .session ._setupstate
1050+ return setupstate .fixture_addfinalizer (finalizer , self )
10541051
10551052 def finish (self , request : SubRequest ) -> None :
1056- exceptions : list [BaseException ] = []
1057- while self ._finalizers :
1058- _ , fin = self ._finalizers .popitem ()
1059- try :
1060- fin ()
1061- except TEST_OUTCOME as e :
1062- exceptions .append (e )
1063- node = request .node
10641053 try :
1065- node .ihook .pytest_fixture_post_finalizer (fixturedef = self , request = request )
1066- except TEST_OUTCOME as e :
1067- exceptions .append (e )
1068-
1069- # Even if finalization fails, we invalidate the cached fixture
1070- # value and remove all finalizers because they may be bound methods
1071- # which will keep instances alive.
1072- self .cached_result = None
1073- self ._finalizers .clear ()
1074- request ._fixturemanager ._on_fixture_finished (self )
1075- self ._cached_request = None
1076- # Avoid accumulating garbage finalizers in nodes and fixturedefs (#4871).
1077- for handle in self ._self_finalizer_handles :
1078- handle ()
1079- self ._self_finalizer_handles .clear ()
1080-
1081- if len (exceptions ) == 1 :
1082- raise exceptions [0 ]
1083- elif len (exceptions ) > 1 :
1084- msg = f'errors while tearing down fixture "{ self .argname } " of { node } '
1085- raise BaseExceptionGroup (msg , exceptions [::- 1 ])
1054+ request .session ._setupstate .fixture_teardown (self , request .node )
1055+ finally :
1056+ self .cached_result = None
1057+ self ._cached_request = None
1058+ # Avoid accumulating garbage finalizers in nodes and fixturedefs (#4871).
1059+ for handle in self ._self_finalizer_handles :
1060+ handle ()
1061+ self ._self_finalizer_handles .clear ()
10861062
10871063 def execute (self , request : SubRequest ) -> FixtureValue :
10881064 """Return the value of this fixture, executing it if not cached."""
@@ -1095,6 +1071,11 @@ def execute(self, request: SubRequest) -> FixtureValue:
10951071 for argname in self .argnames :
10961072 request ._get_active_fixturedef (argname )
10971073
1074+ self ._cached_request = request
1075+ setupstate = request .session ._setupstate
1076+ setupstate .fixture_setup (self )
1077+ setupstate .fixture_addfinalizer (self ._run_post_finalizer , self )
1078+
10981079 ihook = request .node .ihook
10991080 try :
11001081 # Setup the fixture, run the code in it, and cache the value
@@ -1103,8 +1084,6 @@ def execute(self, request: SubRequest) -> FixtureValue:
11031084 fixturedef = self , request = request
11041085 )
11051086 finally :
1106- self ._cached_request = request
1107- request ._fixturemanager ._on_fixture_setup (self )
11081087 # Schedule our finalizer, even if the setup failed.
11091088 fin = functools .partial (self .finish , request )
11101089 self ._self_finalizer_handles .append (request .node .addfinalizer (fin ))
@@ -1113,6 +1092,12 @@ def execute(self, request: SubRequest) -> FixtureValue:
11131092
11141093 return result
11151094
1095+ def _run_post_finalizer (self ) -> None :
1096+ request = self ._cached_request
1097+ assert request is not None
1098+ ihook = request .node .ihook
1099+ ihook .pytest_fixture_post_finalizer (fixturedef = self , request = request )
1100+
11161101 def _is_cache_hit (self , old_cache_key : object , new_cache_key : object ) -> bool :
11171102 try :
11181103 # Attempt to make a normal == check: this might fail for objects
@@ -1649,8 +1634,6 @@ def __init__(self, session: Session) -> None:
16491634 self ._nodeid_autousenames : Final [dict [str , list [str ]]] = {
16501635 "" : self .config .getini ("usefixtures" ),
16511636 }
1652- # Using dict for keeping insertion order.
1653- self ._active_fixturedefs : Final [dict [FixtureDef [Any ], None ]] = {}
16541637 session .config .pluginmanager .register (self , "funcmanage" )
16551638
16561639 def getfixtureinfo (
@@ -1998,25 +1981,6 @@ def _matchfactories(
19981981 if fixturedef .baseid in parentnodeids :
19991982 yield fixturedef
20001983
2001- def _teardown_stale_fixtures (self , nextitem : nodes .Item ) -> None :
2002- exceptions : list [BaseException ] = []
2003- for fixturedef in reversed (list (self ._active_fixturedefs .keys ())):
2004- try :
2005- fixturedef ._finish_if_param_changed (nextitem )
2006- except TEST_OUTCOME as e :
2007- exceptions .append (e )
2008- if len (exceptions ) == 1 :
2009- raise exceptions [0 ]
2010- elif len (exceptions ) > 1 :
2011- msg = f'errors while tearing down fixtures for "{ nextitem .nodeid } "'
2012- raise BaseExceptionGroup (msg , exceptions [::- 1 ])
2013-
2014- def _on_fixture_setup (self , fixturedef : FixtureDef [Any ]) -> None :
2015- self ._active_fixturedefs [fixturedef ] = None
2016-
2017- def _on_fixture_finished (self , fixturedef : FixtureDef [Any ]) -> None :
2018- del self ._active_fixturedefs [fixturedef ]
2019-
20201984
20211985def show_fixtures_per_test (config : Config ) -> int | ExitCode :
20221986 from _pytest .main import wrap_session
0 commit comments