Skip to content

Commit

Permalink
WIP: setup_* methods as fixtures
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Jan 8, 2019
1 parent 0da5531 commit 5ec4e42
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 62 deletions.
222 changes: 180 additions & 42 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import os
import sys
import warnings
from functools import partial
from textwrap import dedent

import py
Expand Down Expand Up @@ -435,9 +436,66 @@ def _getobj(self):
return self._importtestmodule()

def collect(self):
self._inject_setup_module_fixture()
self._inject_setup_function_fixture()
self.session._fixturemanager.parsefactories(self)
return super(Module, self).collect()

def _inject_setup_module_fixture(self):
# setup_method = _get_xunit_setup_teardown(
# self.obj, "setup_method", param_obj=self.obj
# )
setup_module = _get_xunit_func(self.obj, "setUpModule")
if setup_module is None:
setup_module = _get_xunit_func(self.obj, "setup_module")
# if setup_module is not None:
# setup_module()
teardown_module = _get_xunit_func(self.obj, "tearDownModule")
if teardown_module is None:
teardown_module = _get_xunit_func(self.obj, "teardown_module")
if setup_module is None and teardown_module is None:
return
# if teardown_module is not None:
# self.addfinalizer(teardown_module)

@fixtures.fixture(autouse=True, scope="module")
def xunit_setup_module_fixture(request):
if setup_module is not None:
_call_with_optional_argument(setup_module, request.module)
# setup_method(request.instance, request.function)
yield
if teardown_module is not None:
_call_with_optional_argument(teardown_module, request.module)
# self.addfinalizer(lambda: fin_class(self.obj))

self.obj.__pytest_setup_module = xunit_setup_module_fixture

def _inject_setup_function_fixture(self):
# setup_method = _get_xunit_setup_teardown(
# self.obj, "setup_method", param_obj=self.obj
# )
setup_function = _get_xunit_func(self.obj, "setup_function")
teardown_function = _get_xunit_func(self.obj, "teardown_function")
if setup_function is None and teardown_function is None:
return
# if teardown_module is not None:
# self.addfinalizer(teardown_module)

@fixtures.fixture(autouse=True, scope="function")
def xunit_setup_function_fixture(request):
if request.instance is not None:
yield
return # we need to let setup_method take over
if setup_function is not None:
_call_with_optional_argument(setup_function, request.function)
# setup_method(request.instance, request.function)
yield
if teardown_function is not None:
_call_with_optional_argument(teardown_function, request.function)
# self.addfinalizer(lambda: fin_class(self.obj))

self.obj.__pytest_setup_function = xunit_setup_function_fixture

def _importtestmodule(self):
# we assume we are only called once per module
importmode = self.config.getoption("--import-mode")
Expand Down Expand Up @@ -488,18 +546,19 @@ def _importtestmodule(self):
self.config.pluginmanager.consider_module(mod)
return mod

def setup(self):
setup_module = _get_xunit_setup_teardown(self.obj, "setUpModule")
if setup_module is None:
setup_module = _get_xunit_setup_teardown(self.obj, "setup_module")
if setup_module is not None:
setup_module()

teardown_module = _get_xunit_setup_teardown(self.obj, "tearDownModule")
if teardown_module is None:
teardown_module = _get_xunit_setup_teardown(self.obj, "teardown_module")
if teardown_module is not None:
self.addfinalizer(teardown_module)
# def setup(self):
# return
# setup_module = _get_xunit_setup_teardown(self.obj, "setUpModule")
# if setup_module is None:
# setup_module = _get_xunit_setup_teardown(self.obj, "setup_module")
# if setup_module is not None:
# setup_module()
#
# teardown_module = _get_xunit_setup_teardown(self.obj, "tearDownModule")
# if teardown_module is None:
# teardown_module = _get_xunit_setup_teardown(self.obj, "teardown_module")
# if teardown_module is not None:
# self.addfinalizer(teardown_module)


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

def setup(self):
# not using fixtures to call setup_module here because autouse fixtures
# from packages are not called automatically (#4085)
setup_module = _get_xunit_func(self.obj, "setUpModule")
if setup_module is None:
setup_module = _get_xunit_func(self.obj, "setup_module")
if setup_module is not None:
_call_with_optional_argument(setup_module, self.obj)

teardown_module = _get_xunit_func(self.obj, "tearDownModule")
if teardown_module is None:
teardown_module = _get_xunit_func(self.obj, "teardown_module")
if teardown_module is not None:
func = partial(_call_with_optional_argument, teardown_module, self.obj)
self.addfinalizer(func)

def _recurse(self, dirpath):
if dirpath.basename == "__pycache__":
return False
Expand Down Expand Up @@ -599,6 +674,7 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
when the callable is called without arguments, defaults to the ``holder`` object.
Return ``None`` if a suitable callable is not found.
"""
# TODO: only needed because of Package!
param_obj = param_obj if param_obj is not None else holder
result = _get_xunit_func(holder, attr_name)
if result is not None:
Expand All @@ -611,6 +687,16 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
return result


def _call_with_optional_argument(func, arg):
arg_count = func.__code__.co_argcount
if inspect.ismethod(func):
arg_count -= 1
if arg_count:
func(arg)
else:
func()


def _get_xunit_func(obj, name):
"""Return the attribute from the given object to be used as a setup/teardown
xunit-style function, but only if not marked as a fixture to
Expand Down Expand Up @@ -643,18 +729,67 @@ def collect(self):
)
)
return []

self._inject_setup_class_fixture()
self._inject_setup_method_fixture()

return [Instance(name="()", parent=self)]

def setup(self):
def _inject_setup_class_fixture(self):

setup_class = _get_xunit_func(self.obj, "setup_class")
if setup_class is not None:
setup_class = getimfunc(setup_class)
setup_class(self.obj)
teardown_class = getattr(self.obj, "teardown_class", None)
if setup_class is None and teardown_class is None:
return

@fixtures.fixture(autouse=True, scope="class")
def xunit_setup_class_fixture(cls):
if setup_class is not None:
func = getimfunc(setup_class)
_call_with_optional_argument(func, self.obj)
yield
if teardown_class is not None:
func = getimfunc(teardown_class)
_call_with_optional_argument(func, self.obj)
# self.addfinalizer(lambda: fin_class(self.obj))

self.obj.__pytest_setup_class = xunit_setup_class_fixture

def _inject_setup_method_fixture(self):
# setup_method = _get_xunit_setup_teardown(
# self.obj, "setup_method", param_obj=self.obj
# )
setup_method = _get_xunit_func(self.obj, "setup_method")
teardown_method = getattr(self.obj, "teardown_method", None)
if setup_method is None and teardown_method is None:
return

fin_class = getattr(self.obj, "teardown_class", None)
if fin_class is not None:
fin_class = getimfunc(fin_class)
self.addfinalizer(lambda: fin_class(self.obj))
@fixtures.fixture(autouse=True, scope="function")
def xunit_setup_method_fixture(self, request):
method = request.function
if setup_method is not None:
func = getattr(self, "setup_method")
_call_with_optional_argument(func, method)
# setup_method(request.instance, request.function)
yield
if teardown_method is not None:
func = getattr(self, "teardown_method")
_call_with_optional_argument(func, method)
# self.addfinalizer(lambda: fin_class(self.obj))

self.obj.__pytest_setup_method = xunit_setup_method_fixture

# def setup(self):
# return
# setup_class = _get_xunit_func(self.obj, "setup_class")
# if setup_class is not None:
# setup_class = getimfunc(setup_class)
# setup_class(self.obj)
#
# fin_class = getattr(self.obj, "teardown_class", None)
# if fin_class is not None:
# fin_class = getimfunc(fin_class)
# self.addfinalizer(lambda: fin_class(self.obj))


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

def setup(self):
""" perform setup for this test function. """
if hasattr(self, "_preservedparent"):
obj = self._preservedparent
elif isinstance(self.parent, Instance):
obj = self.parent.newinstance()
# if hasattr(self, "_preservedparent"):
# obj = self._preservedparent
# pass
if isinstance(self.parent, Instance):
self.parent.newinstance()
self.obj = self._getobj()
else:
obj = self.parent.obj
if inspect.ismethod(self.obj):
setup_name = "setup_method"
teardown_name = "teardown_method"
else:
setup_name = "setup_function"
teardown_name = "teardown_function"
setup_func_or_method = _get_xunit_setup_teardown(
obj, setup_name, param_obj=self.obj
)
if setup_func_or_method is not None:
setup_func_or_method()
teardown_func_or_method = _get_xunit_setup_teardown(
obj, teardown_name, param_obj=self.obj
)
if teardown_func_or_method is not None:
self.addfinalizer(teardown_func_or_method)
# else:
# obj = self.parent.obj
# if inspect.ismethod(self.obj):
# # setup_name = "setup_method"
# # teardown_name = "teardown_method"
# return
# else:
# setup_name = "setup_function"
# teardown_name = "teardown_function"
# return
# setup_func_or_method = _get_xunit_setup_teardown(
# obj, setup_name, param_obj=self.obj
# )
# if setup_func_or_method is not None:
# setup_func_or_method()
# teardown_func_or_method = _get_xunit_setup_teardown(
# obj, teardown_name, param_obj=self.obj
# )
# if teardown_func_or_method is not None:
# self.addfinalizer(teardown_func_or_method)

def _prunetraceback(self, excinfo):
if hasattr(self, "_obj") and not self.config.option.fulltrace:
Expand Down
Loading

0 comments on commit 5ec4e42

Please sign in to comment.