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() seen = set()
while funcargnames: while funcargnames:
argname = funcargnames.pop(0) argname = funcargnames.pop(0)
if argname in seen: if argname in seen or argname == "request":
continue continue
seen.add(argname) seen.add(argname)
faclist = self.getfactorylist(argname, metafunc.parentid, faclist = self.getfactorylist(argname, metafunc.parentid,
metafunc.function, raising=False) metafunc.function, raising=False)
if faclist is None: if faclist is None:
continue # will raise FuncargLookupError at setup time continue # will raise FuncargLookupError at setup time
for fac in faclist: for facdef in faclist:
marker = getattr(fac, "funcarg", None) if facdef.params is not None:
if marker is not None: metafunc.parametrize(argname, facdef.params, indirect=True,
params = marker.kwargs.get("params") scope=facdef.scope)
scope = marker.kwargs.get("scope", "function") funcargnames.extend(facdef.funcargnames)
if params is not None:
metafunc.parametrize(argname, params, indirect=True,
scope=scope)
newfuncargnames = getfuncargnames(fac)
newfuncargnames.remove("request")
funcargnames.extend(newfuncargnames)
def pytest_collection_modifyitems(self, items): def pytest_collection_modifyitems(self, items):
# separate parametrized setups # separate parametrized setups
@ -520,11 +514,18 @@ class FuncargManager:
continue continue
# funcarg factories either have a pytest_funcarg__ prefix # funcarg factories either have a pytest_funcarg__ prefix
# or are "funcarg" marked # 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) assert not name.startswith(self._argprefix)
argname = name argname = name
scope = marker.kwargs.get("scope")
params = marker.kwargs.get("params")
elif name.startswith(self._argprefix): elif name.startswith(self._argprefix):
argname = name[len(self._argprefix):] argname = name[len(self._argprefix):]
scope = None
params = None
else: else:
# no funcargs. check if we have a setup function. # no funcargs. check if we have a setup function.
setup = getattr(obj, "setup", None) setup = getattr(obj, "setup", None)
@ -534,7 +535,9 @@ class FuncargManager:
self.setuplist.append(sf) self.setuplist.append(sf)
continue continue
faclist = self.arg2facspec.setdefault(argname, []) 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): def getsetuplist(self, nodeid):
l = [] l = []
@ -548,19 +551,19 @@ class FuncargManager:
def getfactorylist(self, argname, nodeid, function, raising=True): def getfactorylist(self, argname, nodeid, function, raising=True):
try: try:
factorydef = self.arg2facspec[argname] factorydeflist = self.arg2facspec[argname]
except KeyError: except KeyError:
if raising: if raising:
self._raiselookupfailed(argname, function, nodeid) self._raiselookupfailed(argname, function, nodeid)
else: else:
return self._matchfactories(factorydef, nodeid) return self._matchfactories(factorydeflist, nodeid)
def _matchfactories(self, factorydef, nodeid): def _matchfactories(self, factorydeflist, nodeid):
l = [] l = []
for baseid, factory in factorydef: for factorydef in factorydeflist:
#print "check", basepath, nodeid #print "check", basepath, nodeid
if nodeid.startswith(baseid): if nodeid.startswith(factorydef.baseid):
l.append(factory) l.append(factorydef)
return l return l
def _raiselookupfailed(self, argname, function, nodeid): def _raiselookupfailed(self, argname, function, nodeid):
@ -575,6 +578,17 @@ class FuncargManager:
raise FuncargLookupError(function, msg) 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: class SetupCall:
""" a container/helper for managing calls to setup functions. """ """ a container/helper for managing calls to setup functions. """
def __init__(self, funcargmanager, baseid, func, scope): def __init__(self, funcargmanager, baseid, func, scope):

View File

@ -586,6 +586,8 @@ class Metafunc:
if not isinstance(argnames, (tuple, list)): if not isinstance(argnames, (tuple, list)):
argnames = (argnames,) argnames = (argnames,)
argvalues = [(val,) for val in argvalues] argvalues = [(val,) for val in argvalues]
if scope is None:
scope = "function"
scopenum = scopes.index(scope) scopenum = scopes.index(scope)
if not indirect: if not indirect:
#XXX should we also check for the opposite case? #XXX should we also check for the opposite case?
@ -888,16 +890,15 @@ class FuncargRequest:
self._factorystack = [] self._factorystack = []
def _getfaclist(self, argname): def _getfaclist(self, argname):
faclist = self._name2factory.get(argname, None) facdeflist = self._name2factory.get(argname, None)
if faclist is None: if facdeflist is None:
faclist = self.funcargmanager.getfactorylist(argname, facdeflist = self.funcargmanager.getfactorylist(
self.parentid, argname, self.parentid, self.function)
self.function) self._name2factory[argname] = facdeflist
self._name2factory[argname] = faclist elif not facdeflist:
elif not faclist:
self.funcargmanager._raiselookupfailed(argname, self.function, self.funcargmanager._raiselookupfailed(argname, self.function,
self.parentid) self.parentid)
return faclist return facdeflist
def raiseerror(self, msg): def raiseerror(self, msg):
""" raise a FuncargLookupError with the given message. """ """ raise a FuncargLookupError with the given message. """
@ -1033,18 +1034,19 @@ class FuncargRequest:
return self._funcargs[argname] return self._funcargs[argname]
except KeyError: except KeyError:
pass pass
factorylist = self._getfaclist(argname) factorydeflist = self._getfaclist(argname)
funcargfactory = factorylist.pop() factorydef = factorydeflist.pop()
self._factorystack.append(funcargfactory) self._factorystack.append(factorydef)
try: try:
return self._getfuncargvalue(funcargfactory, argname) return self._getfuncargvalue(factorydef)
finally: finally:
self._factorystack.pop() self._factorystack.pop()
def _getfuncargvalue(self, funcargfactory, argname): def _getfuncargvalue(self, factorydef):
# collect funcargs from the factory # collect funcargs from the factory
newnames = getfuncargnames(funcargfactory) newnames = list(factorydef.funcargnames)
newnames.remove("request") newnames.remove("request")
argname = factorydef.argname
factory_kwargs = {"request": self} factory_kwargs = {"request": self}
def fillfactoryargs(): def fillfactoryargs():
for newname in newnames: for newname in newnames:
@ -1060,15 +1062,15 @@ class FuncargRequest:
else: else:
mp.setattr(self, 'param', param, raising=False) mp.setattr(self, 'param', param, raising=False)
# implement funcarg marker scope scope = factorydef.scope
scope = readscope(funcargfactory, "funcarg") funcargfactory = factorydef.func
if scope is not None: if scope is not None:
__tracebackhide__ = True __tracebackhide__ = True
if scopemismatch(self.scope, scope): if scopemismatch(self.scope, scope):
# try to report something helpful # try to report something helpful
lines = [] lines = []
for factory in self._factorystack: for factorydef in self._factorystack:
factory = factorydef.func
fs, lineno = getfslineno(factory) fs, lineno = getfslineno(factory)
p = self._pyfuncitem.session.fspath.bestrelpath(fs) p = self._pyfuncitem.session.fspath.bestrelpath(fs)
args = inspect.formatargspec(*inspect.getargspec(factory)) args = inspect.formatargspec(*inspect.getargspec(factory))

View File

@ -1723,7 +1723,7 @@ class TestFuncargManager:
faclist = fm.getfactorylist(name, item.nodeid, item.obj) faclist = fm.getfactorylist(name, item.nodeid, item.obj)
assert len(faclist) == 1 assert len(faclist) == 1
fac = faclist[0] fac = faclist[0]
assert fac.__name__ == "pytest_funcarg__" + name assert fac.func.__name__ == "pytest_funcarg__" + name
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@ -1739,9 +1739,9 @@ class TestFuncargManager:
faclist = fm.getfactorylist("hello", item.nodeid, item.obj) faclist = fm.getfactorylist("hello", item.nodeid, item.obj)
print faclist print faclist
assert len(faclist) == 3 assert len(faclist) == 3
assert faclist[0](item._request) == "conftest" assert faclist[0].func(item._request) == "conftest"
assert faclist[1](item._request) == "module" assert faclist[1].func(item._request) == "module"
assert faclist[2](item._request) == "class" assert faclist[2].func(item._request) == "class"
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)