introduce a funcargcall object, holding meta information

This commit is contained in:
holger krekel 2012-07-30 12:39:45 +02:00
parent b57fb9fd47
commit 9dc79fd187
3 changed files with 58 additions and 42 deletions

View File

@ -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):

View File

@ -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))

View File

@ -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)