11from __future__ import absolute_import , division , print_function
22
3+ import functools
34import inspect
45import sys
56import warnings
7+ from collections import OrderedDict
68
9+ import attr
710import py
811from py ._code .code import FormattedExcinfo
912
10- import attr
1113import _pytest
1214from _pytest import nodes
1315from _pytest ._code .code import TerminalRepr
2224from _pytest .outcomes import fail , TEST_OUTCOME
2325
2426
25- from collections import OrderedDict
26-
27-
2827def pytest_sessionstart (session ):
2928 import _pytest .python
3029
@@ -519,7 +518,7 @@ def _getfixturevalue(self, fixturedef):
519518 val = fixturedef .execute (request = subrequest )
520519 finally :
521520 # if fixture function failed it might have registered finalizers
522- self .session ._setupstate .addfinalizer (fixturedef .finish ,
521+ self .session ._setupstate .addfinalizer (functools . partial ( fixturedef .finish , request = subrequest ) ,
523522 subrequest .node )
524523 return val
525524
@@ -573,7 +572,6 @@ def __init__(self, request, scope, param, param_index, fixturedef):
573572 self .param_index = param_index
574573 self .scope = scope
575574 self ._fixturedef = fixturedef
576- self .addfinalizer = fixturedef .addfinalizer
577575 self ._pyfuncitem = request ._pyfuncitem
578576 self ._fixture_values = request ._fixture_values
579577 self ._fixture_defs = request ._fixture_defs
@@ -584,6 +582,9 @@ def __init__(self, request, scope, param, param_index, fixturedef):
584582 def __repr__ (self ):
585583 return "<SubRequest %r for %r>" % (self .fixturename , self ._pyfuncitem )
586584
585+ def addfinalizer (self , finalizer ):
586+ self ._fixturedef .addfinalizer (finalizer )
587+
587588
588589class ScopeMismatchError (Exception ):
589590 """ A fixture function tries to use a different fixture function which
@@ -734,17 +735,17 @@ def __init__(self, fixturemanager, baseid, argname, func, scope, params,
734735 self .argnames = getfuncargnames (func , is_method = unittest )
735736 self .unittest = unittest
736737 self .ids = ids
737- self ._finalizer = []
738+ self ._finalizers = []
738739
739740 def addfinalizer (self , finalizer ):
740- self ._finalizer .append (finalizer )
741+ self ._finalizers .append (finalizer )
741742
742- def finish (self ):
743+ def finish (self , request ):
743744 exceptions = []
744745 try :
745- while self ._finalizer :
746+ while self ._finalizers :
746747 try :
747- func = self ._finalizer .pop ()
748+ func = self ._finalizers .pop ()
748749 func ()
749750 except : # noqa
750751 exceptions .append (sys .exc_info ())
@@ -754,20 +755,23 @@ def finish(self):
754755 py .builtin ._reraise (* e )
755756
756757 finally :
757- ihook = self ._fixturemanager .session .ihook
758- ihook .pytest_fixture_post_finalizer (fixturedef = self )
758+ hook = self ._fixturemanager .session .gethookproxy ( request . node . fspath )
759+ hook .pytest_fixture_post_finalizer (fixturedef = self , request = request )
759760 # even if finalization fails, we invalidate
760- # the cached fixture value
761+ # the cached fixture value and remove
762+ # all finalizers because they may be bound methods which will
763+ # keep instances alive
761764 if hasattr (self , "cached_result" ):
762765 del self .cached_result
766+ self ._finalizers = []
763767
764768 def execute (self , request ):
765769 # get required arguments and register our own finish()
766770 # with their finalization
767771 for argname in self .argnames :
768772 fixturedef = request ._get_active_fixturedef (argname )
769773 if argname != "request" :
770- fixturedef .addfinalizer (self .finish )
774+ fixturedef .addfinalizer (functools . partial ( self .finish , request = request ) )
771775
772776 my_cache_key = request .param_index
773777 cached_result = getattr (self , "cached_result" , None )
@@ -780,11 +784,11 @@ def execute(self, request):
780784 return result
781785 # we have a previous but differently parametrized fixture instance
782786 # so we need to tear it down before creating a new one
783- self .finish ()
787+ self .finish (request )
784788 assert not hasattr (self , "cached_result" )
785789
786- ihook = self ._fixturemanager .session .ihook
787- return ihook .pytest_fixture_setup (fixturedef = self , request = request )
790+ hook = self ._fixturemanager .session .gethookproxy ( request . node . fspath )
791+ return hook .pytest_fixture_setup (fixturedef = self , request = request )
788792
789793 def __repr__ (self ):
790794 return ("<FixtureDef name=%r scope=%r baseid=%r >" %
0 commit comments