@@ -882,9 +882,7 @@ def finish(self, request):
882
882
self ._finalizers = []
883
883
884
884
def execute (self , request ):
885
- # get required arguments and register our own finish()
886
- # with their finalization
887
- for argname in self .argnames :
885
+ for argname in self ._dependee_fixture_argnames (request ):
888
886
fixturedef = request ._get_active_fixturedef (argname )
889
887
if argname != "request" :
890
888
fixturedef .addfinalizer (functools .partial (self .finish , request = request ))
@@ -907,6 +905,61 @@ def execute(self, request):
907
905
hook = self ._fixturemanager .session .gethookproxy (request .node .fspath )
908
906
return hook .pytest_fixture_setup (fixturedef = self , request = request )
909
907
908
+ def _dependee_fixture_argnames (self , request ):
909
+ """A list of argnames for fixtures that this fixture depends on.
910
+
911
+ Given a request, this looks at the currently known list of fixture argnames, and
912
+ attempts to determine what slice of the list contains fixtures that it can know
913
+ should execute before it. This information is necessary so that this fixture can
914
+ know what fixtures to register its finalizer with to make sure that if they
915
+ would be torn down, they would tear down this fixture before themselves. It's
916
+ crucial for fixtures to be torn down in the inverse order that they were set up
917
+ in so that they don't try to clean up something that another fixture is still
918
+ depending on.
919
+
920
+ When autouse fixtures are involved, it can be tricky to figure out when fixtures
921
+ should be torn down. To solve this, this method leverages the ``fixturenames``
922
+ list provided by the ``request`` object, as this list is at least somewhat
923
+ sorted (in terms of the order fixtures are set up in) by the time this method is
924
+ reached. It's sorted enough that the starting point of fixtures that depend on
925
+ this one can be found using the ``self._parent_request`` stack.
926
+
927
+ If a request in the ``self._parent_request`` stack has a ``:class:FixtureDef``
928
+ associated with it, then that fixture is dependent on this one, so any fixture
929
+ names that appear in the list of fixture argnames that come after it can also be
930
+ ruled out. The argnames of all fixtures associated with a request in the
931
+ ``self._parent_request`` stack are found, and the lowest index argname is
932
+ considered the earliest point in the list of fixture argnames where everything
933
+ from that point onward can be considered to execute after this fixture.
934
+ Everything before this point can be considered fixtures that this fixture
935
+ depends on, and so this fixture should register its finalizer with all of them
936
+ to ensure that if any of them are to be torn down, they will tear this fixture
937
+ down first.
938
+
939
+ This is the first part of the list of fixture argnames that is returned. The last
940
+ part of the list is everything in ``self.argnames`` as those are explicit
941
+ dependees of this fixture, so this fixture should definitely register its
942
+ finalizer with them.
943
+ """
944
+ all_fix_names = request .fixturenames
945
+ try :
946
+ current_fix_index = all_fix_names .index (self .argname )
947
+ except ValueError :
948
+ current_fix_index = len (request .fixturenames )
949
+ parent_fixture_indexes = set ()
950
+
951
+ parent_request = request ._parent_request
952
+ while hasattr (parent_request , "_parent_request" ):
953
+ if hasattr (parent_request , "_fixturedef" ):
954
+ parent_fix_name = parent_request ._fixturedef .argname
955
+ if parent_fix_name in all_fix_names :
956
+ parent_fixture_indexes .add (all_fix_names .index (parent_fix_name ))
957
+ parent_request = parent_request ._parent_request
958
+
959
+ stack_slice_index = min ([current_fix_index , * parent_fixture_indexes ])
960
+ active_fixture_argnames = all_fix_names [:stack_slice_index ]
961
+ return {* active_fixture_argnames , * self .argnames }
962
+
910
963
def cache_key (self , request ):
911
964
return request .param_index if not hasattr (request , "param" ) else request .param
912
965
0 commit comments