5858from _pytest .config .argparsing import Parser
5959from _pytest .deprecated import check_ispytest
6060from _pytest .deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
61+ from _pytest .deprecated import INSTANCE_COLLECTOR
6162from _pytest .fixtures import FuncFixtureInfo
6263from _pytest .main import Session
6364from _pytest .mark import MARK_GEN
@@ -275,20 +276,14 @@ def cls(self):
275276 node = self .getparent (Class )
276277 return node .obj if node is not None else None
277278
278- @property
279- def instance (self ):
280- """Python instance object this node was collected from (can be None)."""
281- node = self .getparent (Instance )
282- return node .obj if node is not None else None
283-
284279 @property
285280 def obj (self ):
286281 """Underlying Python object."""
287282 obj = getattr (self , "_obj" , None )
288283 if obj is None :
289284 self ._obj = obj = self ._getobj ()
290285 # XXX evil hack
291- # used to avoid Instance collector marker duplication
286+ # used to avoid Function marker duplication
292287 if self ._ALLOW_MARKERS :
293288 self .own_markers .extend (get_unpacked_marks (self .obj ))
294289 return obj
@@ -310,8 +305,6 @@ def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) ->
310305 chain .reverse ()
311306 parts = []
312307 for node in chain :
313- if isinstance (node , Instance ):
314- continue
315308 name = node .name
316309 if isinstance (node , Module ):
317310 name = os .path .splitext (name )[0 ]
@@ -410,8 +403,9 @@ def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
410403
411404 # Avoid random getattrs and peek in the __dict__ instead.
412405 dicts = [getattr (self .obj , "__dict__" , {})]
413- for basecls in self .obj .__class__ .__mro__ :
414- dicts .append (basecls .__dict__ )
406+ if isinstance (self .obj , type ):
407+ for basecls in self .obj .__mro__ :
408+ dicts .append (basecls .__dict__ )
415409
416410 # In each class, nodes should be definition ordered. Since Python 3.6,
417411 # __dict__ is definition ordered.
@@ -491,7 +485,6 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
491485 self ,
492486 name = subname ,
493487 callspec = callspec ,
494- callobj = funcobj ,
495488 fixtureinfo = fixtureinfo ,
496489 keywords = {callspec .id : True },
497490 originalname = name ,
@@ -776,6 +769,9 @@ def from_parent(cls, parent, *, name, obj=None, **kw):
776769 """The public constructor."""
777770 return super ().from_parent (name = name , parent = parent , ** kw )
778771
772+ def newinstance (self ):
773+ return self .obj ()
774+
779775 def collect (self ) -> Iterable [Union [nodes .Item , nodes .Collector ]]:
780776 if not safe_getattr (self .obj , "__test__" , True ):
781777 return []
@@ -803,7 +799,9 @@ def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
803799 self ._inject_setup_class_fixture ()
804800 self ._inject_setup_method_fixture ()
805801
806- return [Instance .from_parent (self , name = "()" )]
802+ self .session ._fixturemanager .parsefactories (self .newinstance (), self .nodeid )
803+
804+ return super ().collect ()
807805
808806 def _inject_setup_class_fixture (self ) -> None :
809807 """Inject a hidden autouse, class scoped fixture into the collected class object
@@ -874,25 +872,22 @@ def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]:
874872 self .obj .__pytest_setup_method = xunit_setup_method_fixture
875873
876874
877- class Instance ( PyCollector ) :
878- _ALLOW_MARKERS = False # hack, destroy later
879- # Instances share the object with their parents in a way
880- # that duplicates markers instances if not taken out
881- # can be removed at node structure reorganization time.
875+ class InstanceDummy :
876+ """Instance used to be a node type between Class and Function. It has been
877+ removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
878+ only to ignore it; this dummy class keeps them working. This will be removed
879+ in pytest 8."""
882880
883- def _getobj (self ):
884- # TODO: Improve the type of `parent` such that assert/ignore aren't needed.
885- assert self .parent is not None
886- obj = self .parent .obj # type: ignore[attr-defined]
887- return obj ()
881+ pass
888882
889- def collect (self ) -> Iterable [Union [nodes .Item , nodes .Collector ]]:
890- self .session ._fixturemanager .parsefactories (self )
891- return super ().collect ()
892883
893- def newinstance (self ):
894- self .obj = self ._getobj ()
895- return self .obj
884+ # Note: module __getattr__ only works on Python>=3.7. Unfortunately
885+ # we can't provide this deprecation warning on Python 3.6.
886+ def __getattr__ (name : str ) -> object :
887+ if name == "Instance" :
888+ warnings .warn (INSTANCE_COLLECTOR , 2 )
889+ return InstanceDummy
890+ raise AttributeError (f"module { __name__ } has no attribute { name } " )
896891
897892
898893def hasinit (obj : object ) -> bool :
@@ -1686,9 +1681,23 @@ def function(self):
16861681 """Underlying python 'function' object."""
16871682 return getimfunc (self .obj )
16881683
1684+ @property
1685+ def instance (self ):
1686+ """Python instance object the function is bound to.
1687+
1688+ Returns None if not a test method, e.g. for a standalone test function
1689+ or a staticmethod.
1690+ """
1691+ return getattr (self .obj , "__self__" , None )
1692+
16891693 def _getobj (self ):
16901694 assert self .parent is not None
1691- return getattr (self .parent .obj , self .originalname ) # type: ignore[attr-defined]
1695+ if isinstance (self .parent , Class ):
1696+ # Each Function gets a fresh class instance.
1697+ parent_obj = self .parent .newinstance ()
1698+ else :
1699+ parent_obj = self .parent .obj # type: ignore[attr-defined]
1700+ return getattr (parent_obj , self .originalname )
16921701
16931702 @property
16941703 def _pyfuncitem (self ):
@@ -1700,9 +1709,6 @@ def runtest(self) -> None:
17001709 self .ihook .pytest_pyfunc_call (pyfuncitem = self )
17011710
17021711 def setup (self ) -> None :
1703- if isinstance (self .parent , Instance ):
1704- self .parent .newinstance ()
1705- self .obj = self ._getobj ()
17061712 self ._request ._fillfixtures ()
17071713
17081714 def _prunetraceback (self , excinfo : ExceptionInfo [BaseException ]) -> None :
0 commit comments