99import os
1010import sys
1111import warnings
12+ from functools import partial
1213from textwrap import dedent
1314
1415import py
@@ -435,9 +436,66 @@ def _getobj(self):
435436 return self ._importtestmodule ()
436437
437438 def collect (self ):
439+ self ._inject_setup_module_fixture ()
440+ self ._inject_setup_function_fixture ()
438441 self .session ._fixturemanager .parsefactories (self )
439442 return super (Module , self ).collect ()
440443
444+ def _inject_setup_module_fixture (self ):
445+ """Injects a hidden autouse, module scoped fixture into the collected module object
446+ that invokes setUpModule/tearDownModule if either or both are available.
447+
448+ Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
449+ other fixtures (#517).
450+ """
451+ setup_module = _get_non_fixture_func (self .obj , "setUpModule" )
452+ if setup_module is None :
453+ setup_module = _get_non_fixture_func (self .obj , "setup_module" )
454+
455+ teardown_module = _get_non_fixture_func (self .obj , "tearDownModule" )
456+ if teardown_module is None :
457+ teardown_module = _get_non_fixture_func (self .obj , "teardown_module" )
458+
459+ if setup_module is None and teardown_module is None :
460+ return
461+
462+ @fixtures .fixture (autouse = True , scope = "module" )
463+ def xunit_setup_module_fixture (request ):
464+ if setup_module is not None :
465+ _call_with_optional_argument (setup_module , request .module )
466+ yield
467+ if teardown_module is not None :
468+ _call_with_optional_argument (teardown_module , request .module )
469+
470+ self .obj .__pytest_setup_module = xunit_setup_module_fixture
471+
472+ def _inject_setup_function_fixture (self ):
473+ """Injects a hidden autouse, function scoped fixture into the collected module object
474+ that invokes setup_function/teardown_function if either or both are available.
475+
476+ Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
477+ other fixtures (#517).
478+ """
479+ setup_function = _get_non_fixture_func (self .obj , "setup_function" )
480+ teardown_function = _get_non_fixture_func (self .obj , "teardown_function" )
481+ if setup_function is None and teardown_function is None :
482+ return
483+
484+ @fixtures .fixture (autouse = True , scope = "function" )
485+ def xunit_setup_function_fixture (request ):
486+ if request .instance is not None :
487+ # in this case we are bound to an instance, so we need to let
488+ # setup_method handle this
489+ yield
490+ return
491+ if setup_function is not None :
492+ _call_with_optional_argument (setup_function , request .function )
493+ yield
494+ if teardown_function is not None :
495+ _call_with_optional_argument (teardown_function , request .function )
496+
497+ self .obj .__pytest_setup_function = xunit_setup_function_fixture
498+
441499 def _importtestmodule (self ):
442500 # we assume we are only called once per module
443501 importmode = self .config .getoption ("--import-mode" )
@@ -488,19 +546,6 @@ def _importtestmodule(self):
488546 self .config .pluginmanager .consider_module (mod )
489547 return mod
490548
491- def setup (self ):
492- setup_module = _get_xunit_setup_teardown (self .obj , "setUpModule" )
493- if setup_module is None :
494- setup_module = _get_xunit_setup_teardown (self .obj , "setup_module" )
495- if setup_module is not None :
496- setup_module ()
497-
498- teardown_module = _get_xunit_setup_teardown (self .obj , "tearDownModule" )
499- if teardown_module is None :
500- teardown_module = _get_xunit_setup_teardown (self .obj , "teardown_module" )
501- if teardown_module is not None :
502- self .addfinalizer (teardown_module )
503-
504549
505550class Package (Module ):
506551 def __init__ (self , fspath , parent = None , config = None , session = None , nodeid = None ):
@@ -513,6 +558,22 @@ def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
513558 self ._norecursepatterns = session ._norecursepatterns
514559 self .fspath = fspath
515560
561+ def setup (self ):
562+ # not using fixtures to call setup_module here because autouse fixtures
563+ # from packages are not called automatically (#4085)
564+ setup_module = _get_non_fixture_func (self .obj , "setUpModule" )
565+ if setup_module is None :
566+ setup_module = _get_non_fixture_func (self .obj , "setup_module" )
567+ if setup_module is not None :
568+ _call_with_optional_argument (setup_module , self .obj )
569+
570+ teardown_module = _get_non_fixture_func (self .obj , "tearDownModule" )
571+ if teardown_module is None :
572+ teardown_module = _get_non_fixture_func (self .obj , "teardown_module" )
573+ if teardown_module is not None :
574+ func = partial (_call_with_optional_argument , teardown_module , self .obj )
575+ self .addfinalizer (func )
576+
516577 def _recurse (self , dirpath ):
517578 if dirpath .basename == "__pycache__" :
518579 return False
@@ -599,8 +660,9 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
599660 when the callable is called without arguments, defaults to the ``holder`` object.
600661 Return ``None`` if a suitable callable is not found.
601662 """
663+ # TODO: only needed because of Package!
602664 param_obj = param_obj if param_obj is not None else holder
603- result = _get_xunit_func (holder , attr_name )
665+ result = _get_non_fixture_func (holder , attr_name )
604666 if result is not None :
605667 arg_count = result .__code__ .co_argcount
606668 if inspect .ismethod (result ):
@@ -611,7 +673,19 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
611673 return result
612674
613675
614- def _get_xunit_func (obj , name ):
676+ def _call_with_optional_argument (func , arg ):
677+ """Call the given function with the given argument if func accepts one argument, otherwise
678+ calls func without arguments"""
679+ arg_count = func .__code__ .co_argcount
680+ if inspect .ismethod (func ):
681+ arg_count -= 1
682+ if arg_count :
683+ func (arg )
684+ else :
685+ func ()
686+
687+
688+ def _get_non_fixture_func (obj , name ):
615689 """Return the attribute from the given object to be used as a setup/teardown
616690 xunit-style function, but only if not marked as a fixture to
617691 avoid calling it twice.
@@ -643,18 +717,60 @@ def collect(self):
643717 )
644718 )
645719 return []
720+
721+ self ._inject_setup_class_fixture ()
722+ self ._inject_setup_method_fixture ()
723+
646724 return [Instance (name = "()" , parent = self )]
647725
648- def setup (self ):
649- setup_class = _get_xunit_func (self .obj , "setup_class" )
650- if setup_class is not None :
651- setup_class = getimfunc (setup_class )
652- setup_class (self .obj )
726+ def _inject_setup_class_fixture (self ):
727+ """Injects a hidden autouse, class scoped fixture into the collected class object
728+ that invokes setup_class/teardown_class if either or both are available.
653729
654- fin_class = getattr (self .obj , "teardown_class" , None )
655- if fin_class is not None :
656- fin_class = getimfunc (fin_class )
657- self .addfinalizer (lambda : fin_class (self .obj ))
730+ Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
731+ other fixtures (#517).
732+ """
733+ setup_class = _get_non_fixture_func (self .obj , "setup_class" )
734+ teardown_class = getattr (self .obj , "teardown_class" , None )
735+ if setup_class is None and teardown_class is None :
736+ return
737+
738+ @fixtures .fixture (autouse = True , scope = "class" )
739+ def xunit_setup_class_fixture (cls ):
740+ if setup_class is not None :
741+ func = getimfunc (setup_class )
742+ _call_with_optional_argument (func , self .obj )
743+ yield
744+ if teardown_class is not None :
745+ func = getimfunc (teardown_class )
746+ _call_with_optional_argument (func , self .obj )
747+
748+ self .obj .__pytest_setup_class = xunit_setup_class_fixture
749+
750+ def _inject_setup_method_fixture (self ):
751+ """Injects a hidden autouse, function scoped fixture into the collected class object
752+ that invokes setup_method/teardown_method if either or both are available.
753+
754+ Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
755+ other fixtures (#517).
756+ """
757+ setup_method = _get_non_fixture_func (self .obj , "setup_method" )
758+ teardown_method = getattr (self .obj , "teardown_method" , None )
759+ if setup_method is None and teardown_method is None :
760+ return
761+
762+ @fixtures .fixture (autouse = True , scope = "function" )
763+ def xunit_setup_method_fixture (self , request ):
764+ method = request .function
765+ if setup_method is not None :
766+ func = getattr (self , "setup_method" )
767+ _call_with_optional_argument (func , method )
768+ yield
769+ if teardown_method is not None :
770+ func = getattr (self , "teardown_method" )
771+ _call_with_optional_argument (func , method )
772+
773+ self .obj .__pytest_setup_method = xunit_setup_method_fixture
658774
659775
660776class Instance (PyCollector ):
@@ -681,29 +797,9 @@ class FunctionMixin(PyobjMixin):
681797
682798 def setup (self ):
683799 """ perform setup for this test function. """
684- if hasattr (self , "_preservedparent" ):
685- obj = self ._preservedparent
686- elif isinstance (self .parent , Instance ):
687- obj = self .parent .newinstance ()
800+ if isinstance (self .parent , Instance ):
801+ self .parent .newinstance ()
688802 self .obj = self ._getobj ()
689- else :
690- obj = self .parent .obj
691- if inspect .ismethod (self .obj ):
692- setup_name = "setup_method"
693- teardown_name = "teardown_method"
694- else :
695- setup_name = "setup_function"
696- teardown_name = "teardown_function"
697- setup_func_or_method = _get_xunit_setup_teardown (
698- obj , setup_name , param_obj = self .obj
699- )
700- if setup_func_or_method is not None :
701- setup_func_or_method ()
702- teardown_func_or_method = _get_xunit_setup_teardown (
703- obj , teardown_name , param_obj = self .obj
704- )
705- if teardown_func_or_method is not None :
706- self .addfinalizer (teardown_func_or_method )
707803
708804 def _prunetraceback (self , excinfo ):
709805 if hasattr (self , "_obj" ) and not self .config .option .fulltrace :
0 commit comments