diff --git a/py/_com.py b/py/_com.py index 2c26338fc..3404844de 100644 --- a/py/_com.py +++ b/py/_com.py @@ -29,7 +29,7 @@ class MultiCall: NONEASRESULT = object() def __init__(self, methods, *args, **kwargs): - self.methods = methods + self.methods = methods[:] self.args = args self.kwargs = kwargs self.results = [] @@ -69,6 +69,7 @@ class MultiCall: def exclude_other_results(self): self._ex1 = True + class PyPlugins: """ Manage Plugins: Load plugins and manage calls to plugins. @@ -79,7 +80,6 @@ class PyPlugins: if plugins is None: plugins = [] self._plugins = plugins - self._callbacks = [] def import_module(self, modspec): # XXX allow modspec to specify version / lookup diff --git a/py/misc/testing/test_com.py b/py/misc/testing/test_com.py index 1aa8f1d73..b66651f58 100644 --- a/py/misc/testing/test_com.py +++ b/py/misc/testing/test_com.py @@ -6,6 +6,13 @@ from py._com import PyPlugins, MultiCall pytest_plugins = "xfail" class TestMultiCall: + def test_uses_copy_of_methods(self): + l = [lambda: 42] + mc = MultiCall(l) + l[:] = [] + res = mc.execute() + return res == 42 + def test_call_passing(self): class P1: def m(self, __call__, x): diff --git a/py/test/config.py b/py/test/config.py index e33ae08de..565b019dd 100644 --- a/py/test/config.py +++ b/py/test/config.py @@ -41,6 +41,7 @@ class Config(object): self.pytestplugins = pytestplugins self._conftest = Conftest(onimport=self._onimportconftest) self._setupstate = SetupState() + self._funcarg2maker = {} def _onimportconftest(self, conftestmodule): self.trace("loaded conftestmodule %r" %(conftestmodule,)) @@ -285,7 +286,23 @@ class Config(object): roots.append(pydir) return roots - + def register_funcargmaker(self, argname, maker): + """ register a setup method for the given argument name. """ + self._funcarg2maker.setdefault(argname, []).append(maker) + + def _makefuncarg(self, argname, pyfuncitem): + makerlist = self._getmakerlist(argname) + mcall = py._com.MultiCall(makerlist, pyfuncitem) + return mcall.execute(firstresult=True) + + def _getmakerlist(self, argname): + makerlist = self._funcarg2maker.get(argname, None) + if makerlist is None: + msg = "funcarg %r not registered, available are: %s" % ( + argname, ", ".join(self._funcarg2maker.keys())) + raise KeyError(msg) + assert makerlist + return makerlist[:] # # helpers # diff --git a/py/test/plugin/pytest_monkeypatch.py b/py/test/plugin/pytest_monkeypatch.py index 8d8f41382..f6a518eb6 100644 --- a/py/test/plugin/pytest_monkeypatch.py +++ b/py/test/plugin/pytest_monkeypatch.py @@ -2,7 +2,10 @@ import os class MonkeypatchPlugin: """ setattr-monkeypatching with automatical reversal after test. """ - def pytest_pyfuncarg_monkeypatch(self, pyfuncitem): + def pytest_configure(self, config): + config.register_funcargmaker("monkeypatch", self.argmaker) + + def argmaker(self, pyfuncitem): monkeypatch = MonkeyPatch() pyfuncitem.addfinalizer(monkeypatch.finalize) return monkeypatch diff --git a/py/test/plugin/pytest_pytester.py b/py/test/plugin/pytest_pytester.py index d38e68911..d4f18a3ef 100644 --- a/py/test/plugin/pytest_pytester.py +++ b/py/test/plugin/pytest_pytester.py @@ -7,21 +7,20 @@ from py.__.test import event from py.__.test.config import Config as pytestConfig class PytesterPlugin: - def pytest_pyfuncarg_linecomp(self, pyfuncitem): - return LineComp() + def pytest_configure(self, config): + config.register_funcargmaker("linecomp", lambda x: LineComp()) + config.register_funcargmaker("LineMatcher", lambda x: LineMatcher) + config.register_funcargmaker("EventRecorder", lambda x: EventRecorder) - def pytest_pyfuncarg_LineMatcher(self, pyfuncitem): - return LineMatcher + config.register_funcargmaker("testdir", self.maketestdir) + config.register_funcargmaker("eventrecorder", self.makeeventrecorder) - def pytest_pyfuncarg_testdir(self, pyfuncitem): + def maketestdir(self, pyfuncitem): tmptestdir = TmpTestdir(pyfuncitem) pyfuncitem.addfinalizer(tmptestdir.finalize) return tmptestdir - def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): - return EventRecorder - - def pytest_pyfuncarg_eventrecorder(self, pyfuncitem): + def makeeventrecorder(self, pyfuncitem): evrec = EventRecorder(py._com.pyplugins) pyfuncitem.addfinalizer(lambda: evrec.pyplugins.unregister(evrec)) return evrec diff --git a/py/test/pycollect.py b/py/test/pycollect.py index 57a54a7e7..df16e4d0f 100644 --- a/py/test/pycollect.py +++ b/py/test/pycollect.py @@ -375,14 +375,23 @@ class Function(FunctionMixin, py.test.collect.Item): return kwargs def lookup_onearg(self, argname): - value = self.config.pytestplugins.call_firstresult( - "pytest_pyfuncarg_" + argname, pyfuncitem=self) + try: + makerlist = self.config._getmakerlist(argname) + except KeyError: + makerlist = [] + l = self.config.pytestplugins.listattr("pytest_pyfuncarg_" + argname) + makerlist.extend(l) + mc = py._com.MultiCall(makerlist, self) + #print "mc.methods", mc.methods + value = mc.execute(firstresult=True) if value is not None: return value else: metainfo = self.repr_metainfo() #self.config.bus.notify("pyfuncarg_lookuperror", argname) - raise LookupError("funcargument %r not found for: %s" %(argname,metainfo.verboseline())) + msg = "funcargument %r not found for: %s" %(argname,metainfo.verboseline()) + msg += "\n list of makers: %r" %(l,) + raise LookupError(msg) def __eq__(self, other): try: diff --git a/py/test/testing/test_config.py b/py/test/testing/test_config.py index 25f410d99..bee463fc9 100644 --- a/py/test/testing/test_config.py +++ b/py/test/testing/test_config.py @@ -1,5 +1,33 @@ import py +class TestFuncArgsSetup: + def test_register_funcarg_simple(self, testdir): + item = testdir.getitem("def test_func(hello): pass") + def maker(pyfuncitem): + assert item == pyfuncitem + return 42 + item.config.register_funcargmaker("hello", maker) + arg = item.config._makefuncarg("hello", item) + assert arg == 42 + + def test_register_funcarg_two(self, testdir): + item = testdir.getitem("def test_func(hello): pass") + def maker1(pyfuncitem): + assert item == pyfuncitem + return 1 + def maker2(__call__, pyfuncitem): + assert item == pyfuncitem + res = __call__.execute(firstresult=True) + return res + 1 + item.config.register_funcargmaker("two", maker1) + item.config.register_funcargmaker("two", maker2) + arg = item.config._makefuncarg("two", item) + assert arg == 2 + + def test_register_funcarg_error(self, testdir): + item = testdir.getitem("def test_func(hello): pass") + config = item.config + py.test.raises(KeyError, 'item.config._makefuncarg("notexist", item)') class TestConfigCmdlineParsing: def test_config_cmdline_options(self, testdir):