Skip to content

Commit 5ec4e42

Browse files
committed
WIP: setup_* methods as fixtures
1 parent 0da5531 commit 5ec4e42

File tree

3 files changed

+241
-62
lines changed

3 files changed

+241
-62
lines changed

src/_pytest/python.py

Lines changed: 180 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import sys
1111
import warnings
12+
from functools import partial
1213
from textwrap import dedent
1314

1415
import 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+
# setup_method = _get_xunit_setup_teardown(
446+
# self.obj, "setup_method", param_obj=self.obj
447+
# )
448+
setup_module = _get_xunit_func(self.obj, "setUpModule")
449+
if setup_module is None:
450+
setup_module = _get_xunit_func(self.obj, "setup_module")
451+
# if setup_module is not None:
452+
# setup_module()
453+
teardown_module = _get_xunit_func(self.obj, "tearDownModule")
454+
if teardown_module is None:
455+
teardown_module = _get_xunit_func(self.obj, "teardown_module")
456+
if setup_module is None and teardown_module is None:
457+
return
458+
# if teardown_module is not None:
459+
# self.addfinalizer(teardown_module)
460+
461+
@fixtures.fixture(autouse=True, scope="module")
462+
def xunit_setup_module_fixture(request):
463+
if setup_module is not None:
464+
_call_with_optional_argument(setup_module, request.module)
465+
# setup_method(request.instance, request.function)
466+
yield
467+
if teardown_module is not None:
468+
_call_with_optional_argument(teardown_module, request.module)
469+
# self.addfinalizer(lambda: fin_class(self.obj))
470+
471+
self.obj.__pytest_setup_module = xunit_setup_module_fixture
472+
473+
def _inject_setup_function_fixture(self):
474+
# setup_method = _get_xunit_setup_teardown(
475+
# self.obj, "setup_method", param_obj=self.obj
476+
# )
477+
setup_function = _get_xunit_func(self.obj, "setup_function")
478+
teardown_function = _get_xunit_func(self.obj, "teardown_function")
479+
if setup_function is None and teardown_function is None:
480+
return
481+
# if teardown_module is not None:
482+
# self.addfinalizer(teardown_module)
483+
484+
@fixtures.fixture(autouse=True, scope="function")
485+
def xunit_setup_function_fixture(request):
486+
if request.instance is not None:
487+
yield
488+
return # we need to let setup_method take over
489+
if setup_function is not None:
490+
_call_with_optional_argument(setup_function, request.function)
491+
# setup_method(request.instance, request.function)
492+
yield
493+
if teardown_function is not None:
494+
_call_with_optional_argument(teardown_function, request.function)
495+
# self.addfinalizer(lambda: fin_class(self.obj))
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,18 +546,19 @@ 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)
549+
# def setup(self):
550+
# return
551+
# setup_module = _get_xunit_setup_teardown(self.obj, "setUpModule")
552+
# if setup_module is None:
553+
# setup_module = _get_xunit_setup_teardown(self.obj, "setup_module")
554+
# if setup_module is not None:
555+
# setup_module()
556+
#
557+
# teardown_module = _get_xunit_setup_teardown(self.obj, "tearDownModule")
558+
# if teardown_module is None:
559+
# teardown_module = _get_xunit_setup_teardown(self.obj, "teardown_module")
560+
# if teardown_module is not None:
561+
# self.addfinalizer(teardown_module)
503562

504563

505564
class Package(Module):
@@ -513,6 +572,22 @@ def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
513572
self._norecursepatterns = session._norecursepatterns
514573
self.fspath = fspath
515574

575+
def setup(self):
576+
# not using fixtures to call setup_module here because autouse fixtures
577+
# from packages are not called automatically (#4085)
578+
setup_module = _get_xunit_func(self.obj, "setUpModule")
579+
if setup_module is None:
580+
setup_module = _get_xunit_func(self.obj, "setup_module")
581+
if setup_module is not None:
582+
_call_with_optional_argument(setup_module, self.obj)
583+
584+
teardown_module = _get_xunit_func(self.obj, "tearDownModule")
585+
if teardown_module is None:
586+
teardown_module = _get_xunit_func(self.obj, "teardown_module")
587+
if teardown_module is not None:
588+
func = partial(_call_with_optional_argument, teardown_module, self.obj)
589+
self.addfinalizer(func)
590+
516591
def _recurse(self, dirpath):
517592
if dirpath.basename == "__pycache__":
518593
return False
@@ -599,6 +674,7 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
599674
when the callable is called without arguments, defaults to the ``holder`` object.
600675
Return ``None`` if a suitable callable is not found.
601676
"""
677+
# TODO: only needed because of Package!
602678
param_obj = param_obj if param_obj is not None else holder
603679
result = _get_xunit_func(holder, attr_name)
604680
if result is not None:
@@ -611,6 +687,16 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
611687
return result
612688

613689

690+
def _call_with_optional_argument(func, arg):
691+
arg_count = func.__code__.co_argcount
692+
if inspect.ismethod(func):
693+
arg_count -= 1
694+
if arg_count:
695+
func(arg)
696+
else:
697+
func()
698+
699+
614700
def _get_xunit_func(obj, name):
615701
"""Return the attribute from the given object to be used as a setup/teardown
616702
xunit-style function, but only if not marked as a fixture to
@@ -643,18 +729,67 @@ def collect(self):
643729
)
644730
)
645731
return []
732+
733+
self._inject_setup_class_fixture()
734+
self._inject_setup_method_fixture()
735+
646736
return [Instance(name="()", parent=self)]
647737

648-
def setup(self):
738+
def _inject_setup_class_fixture(self):
739+
649740
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)
741+
teardown_class = getattr(self.obj, "teardown_class", None)
742+
if setup_class is None and teardown_class is None:
743+
return
744+
745+
@fixtures.fixture(autouse=True, scope="class")
746+
def xunit_setup_class_fixture(cls):
747+
if setup_class is not None:
748+
func = getimfunc(setup_class)
749+
_call_with_optional_argument(func, self.obj)
750+
yield
751+
if teardown_class is not None:
752+
func = getimfunc(teardown_class)
753+
_call_with_optional_argument(func, self.obj)
754+
# self.addfinalizer(lambda: fin_class(self.obj))
755+
756+
self.obj.__pytest_setup_class = xunit_setup_class_fixture
757+
758+
def _inject_setup_method_fixture(self):
759+
# setup_method = _get_xunit_setup_teardown(
760+
# self.obj, "setup_method", param_obj=self.obj
761+
# )
762+
setup_method = _get_xunit_func(self.obj, "setup_method")
763+
teardown_method = getattr(self.obj, "teardown_method", None)
764+
if setup_method is None and teardown_method is None:
765+
return
653766

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))
767+
@fixtures.fixture(autouse=True, scope="function")
768+
def xunit_setup_method_fixture(self, request):
769+
method = request.function
770+
if setup_method is not None:
771+
func = getattr(self, "setup_method")
772+
_call_with_optional_argument(func, method)
773+
# setup_method(request.instance, request.function)
774+
yield
775+
if teardown_method is not None:
776+
func = getattr(self, "teardown_method")
777+
_call_with_optional_argument(func, method)
778+
# self.addfinalizer(lambda: fin_class(self.obj))
779+
780+
self.obj.__pytest_setup_method = xunit_setup_method_fixture
781+
782+
# def setup(self):
783+
# return
784+
# setup_class = _get_xunit_func(self.obj, "setup_class")
785+
# if setup_class is not None:
786+
# setup_class = getimfunc(setup_class)
787+
# setup_class(self.obj)
788+
#
789+
# fin_class = getattr(self.obj, "teardown_class", None)
790+
# if fin_class is not None:
791+
# fin_class = getimfunc(fin_class)
792+
# self.addfinalizer(lambda: fin_class(self.obj))
658793

659794

660795
class Instance(PyCollector):
@@ -681,29 +816,32 @@ class FunctionMixin(PyobjMixin):
681816

682817
def setup(self):
683818
""" 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()
819+
# if hasattr(self, "_preservedparent"):
820+
# obj = self._preservedparent
821+
# pass
822+
if isinstance(self.parent, Instance):
823+
self.parent.newinstance()
688824
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)
825+
# else:
826+
# obj = self.parent.obj
827+
# if inspect.ismethod(self.obj):
828+
# # setup_name = "setup_method"
829+
# # teardown_name = "teardown_method"
830+
# return
831+
# else:
832+
# setup_name = "setup_function"
833+
# teardown_name = "teardown_function"
834+
# return
835+
# setup_func_or_method = _get_xunit_setup_teardown(
836+
# obj, setup_name, param_obj=self.obj
837+
# )
838+
# if setup_func_or_method is not None:
839+
# setup_func_or_method()
840+
# teardown_func_or_method = _get_xunit_setup_teardown(
841+
# obj, teardown_name, param_obj=self.obj
842+
# )
843+
# if teardown_func_or_method is not None:
844+
# self.addfinalizer(teardown_func_or_method)
707845

708846
def _prunetraceback(self, excinfo):
709847
if hasattr(self, "_obj") and not self.config.option.fulltrace:

0 commit comments

Comments
 (0)