From 9dc79fd1872ee1a78dce506245cb2efb35d2c25c Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 30 Jul 2012 12:39:45 +0200 Subject: [PATCH] introduce a funcargcall object, holding meta information --- _pytest/main.py | 54 ++++++++++++++++++++++++++---------------- _pytest/python.py | 38 +++++++++++++++-------------- testing/test_python.py | 8 +++---- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/_pytest/main.py b/_pytest/main.py index 7c32a6e0d..91cf9b23c 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -454,24 +454,18 @@ class FuncargManager: seen = set() while funcargnames: argname = funcargnames.pop(0) - if argname in seen: + if argname in seen or argname == "request": continue seen.add(argname) faclist = self.getfactorylist(argname, metafunc.parentid, metafunc.function, raising=False) if faclist is None: continue # will raise FuncargLookupError at setup time - for fac in faclist: - marker = getattr(fac, "funcarg", None) - if marker is not None: - params = marker.kwargs.get("params") - scope = marker.kwargs.get("scope", "function") - if params is not None: - metafunc.parametrize(argname, params, indirect=True, - scope=scope) - newfuncargnames = getfuncargnames(fac) - newfuncargnames.remove("request") - funcargnames.extend(newfuncargnames) + for facdef in faclist: + if facdef.params is not None: + metafunc.parametrize(argname, facdef.params, indirect=True, + scope=facdef.scope) + funcargnames.extend(facdef.funcargnames) def pytest_collection_modifyitems(self, items): # separate parametrized setups @@ -520,11 +514,18 @@ class FuncargManager: continue # funcarg factories either have a pytest_funcarg__ prefix # or are "funcarg" marked - if hasattr(obj, "funcarg"): + if not callable(obj): + continue + marker = getattr(obj, "funcarg", None) + if marker is not None and isinstance(marker, MarkInfo): assert not name.startswith(self._argprefix) argname = name + scope = marker.kwargs.get("scope") + params = marker.kwargs.get("params") elif name.startswith(self._argprefix): argname = name[len(self._argprefix):] + scope = None + params = None else: # no funcargs. check if we have a setup function. setup = getattr(obj, "setup", None) @@ -534,7 +535,9 @@ class FuncargManager: self.setuplist.append(sf) continue faclist = self.arg2facspec.setdefault(argname, []) - faclist.append((nodeid, obj)) + factorydef = FactoryDef(self, nodeid, argname, obj, scope, params) + faclist.append(factorydef) + ### check scope/params mismatch? def getsetuplist(self, nodeid): l = [] @@ -548,19 +551,19 @@ class FuncargManager: def getfactorylist(self, argname, nodeid, function, raising=True): try: - factorydef = self.arg2facspec[argname] + factorydeflist = self.arg2facspec[argname] except KeyError: if raising: self._raiselookupfailed(argname, function, nodeid) else: - return self._matchfactories(factorydef, nodeid) + return self._matchfactories(factorydeflist, nodeid) - def _matchfactories(self, factorydef, nodeid): + def _matchfactories(self, factorydeflist, nodeid): l = [] - for baseid, factory in factorydef: + for factorydef in factorydeflist: #print "check", basepath, nodeid - if nodeid.startswith(baseid): - l.append(factory) + if nodeid.startswith(factorydef.baseid): + l.append(factorydef) return l def _raiselookupfailed(self, argname, function, nodeid): @@ -575,6 +578,17 @@ class FuncargManager: raise FuncargLookupError(function, msg) +class FactoryDef: + """ A container for a factory definition. """ + def __init__(self, funcargmanager, baseid, argname, func, scope, params): + self.funcargmanager = funcargmanager + self.baseid = baseid + self.func = func + self.argname = argname + self.scope = scope + self.params = params + self.funcargnames = getfuncargnames(func) + class SetupCall: """ a container/helper for managing calls to setup functions. """ def __init__(self, funcargmanager, baseid, func, scope): diff --git a/_pytest/python.py b/_pytest/python.py index fa7a32bcf..a5a616661 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -586,6 +586,8 @@ class Metafunc: if not isinstance(argnames, (tuple, list)): argnames = (argnames,) argvalues = [(val,) for val in argvalues] + if scope is None: + scope = "function" scopenum = scopes.index(scope) if not indirect: #XXX should we also check for the opposite case? @@ -888,16 +890,15 @@ class FuncargRequest: self._factorystack = [] def _getfaclist(self, argname): - faclist = self._name2factory.get(argname, None) - if faclist is None: - faclist = self.funcargmanager.getfactorylist(argname, - self.parentid, - self.function) - self._name2factory[argname] = faclist - elif not faclist: + facdeflist = self._name2factory.get(argname, None) + if facdeflist is None: + facdeflist = self.funcargmanager.getfactorylist( + argname, self.parentid, self.function) + self._name2factory[argname] = facdeflist + elif not facdeflist: self.funcargmanager._raiselookupfailed(argname, self.function, self.parentid) - return faclist + return facdeflist def raiseerror(self, msg): """ raise a FuncargLookupError with the given message. """ @@ -1033,18 +1034,19 @@ class FuncargRequest: return self._funcargs[argname] except KeyError: pass - factorylist = self._getfaclist(argname) - funcargfactory = factorylist.pop() - self._factorystack.append(funcargfactory) + factorydeflist = self._getfaclist(argname) + factorydef = factorydeflist.pop() + self._factorystack.append(factorydef) try: - return self._getfuncargvalue(funcargfactory, argname) + return self._getfuncargvalue(factorydef) finally: self._factorystack.pop() - def _getfuncargvalue(self, funcargfactory, argname): + def _getfuncargvalue(self, factorydef): # collect funcargs from the factory - newnames = getfuncargnames(funcargfactory) + newnames = list(factorydef.funcargnames) newnames.remove("request") + argname = factorydef.argname factory_kwargs = {"request": self} def fillfactoryargs(): for newname in newnames: @@ -1060,15 +1062,15 @@ class FuncargRequest: else: mp.setattr(self, 'param', param, raising=False) - # implement funcarg marker scope - scope = readscope(funcargfactory, "funcarg") - + scope = factorydef.scope + funcargfactory = factorydef.func if scope is not None: __tracebackhide__ = True if scopemismatch(self.scope, scope): # try to report something helpful lines = [] - for factory in self._factorystack: + for factorydef in self._factorystack: + factory = factorydef.func fs, lineno = getfslineno(factory) p = self._pyfuncitem.session.fspath.bestrelpath(fs) args = inspect.formatargspec(*inspect.getargspec(factory)) diff --git a/testing/test_python.py b/testing/test_python.py index 43e470697..dda597891 100644 --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1723,7 +1723,7 @@ class TestFuncargManager: faclist = fm.getfactorylist(name, item.nodeid, item.obj) assert len(faclist) == 1 fac = faclist[0] - assert fac.__name__ == "pytest_funcarg__" + name + assert fac.func.__name__ == "pytest_funcarg__" + name """) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) @@ -1739,9 +1739,9 @@ class TestFuncargManager: faclist = fm.getfactorylist("hello", item.nodeid, item.obj) print faclist assert len(faclist) == 3 - assert faclist[0](item._request) == "conftest" - assert faclist[1](item._request) == "module" - assert faclist[2](item._request) == "class" + assert faclist[0].func(item._request) == "conftest" + assert faclist[1].func(item._request) == "module" + assert faclist[2].func(item._request) == "class" """) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1)