* create funcarg Request object only once per function run setup

* add getfuncargvalue() for retrieving arbitrary funcargs from a provider

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-05-21 09:45:43 +02:00
parent d1f24aa251
commit dcee9bdd6e
5 changed files with 117 additions and 48 deletions

View File

@ -108,8 +108,6 @@ encapsulate a request for a function argument for a
specific test function. Request objects allow providers
to access test configuration and test context:
``request.argname``: name of the requested function argument
``request.function``: python function object requesting the argument
``request.cls``: class object where the test function is defined in or None.
@ -120,6 +118,7 @@ to access test configuration and test context:
``request.param``: if exists was passed by a `parametrizing test generator`_
perform scoped setup and teardown
---------------------------------------------
@ -171,6 +170,21 @@ object that is to be closed when the test function finishes.
request.addfinalizer(lambda: myfile.close())
return myfile
requesting values of other funcargs
---------------------------------------------
While setting up one function argument you may
want to retrieve another function argument.
.. sourcecode:: python
def getfuncargvalue(name):
""" Lookup and call function argument provider for the given name.
Each function argument is only requested once per function setup.
"""
Note that it does not matter if the test function
specifies the requested function argument.
decorating other funcarg providers
++++++++++++++++++++++++++++++++++++++++

View File

@ -10,16 +10,8 @@ def getfuncargnames(function):
def fillfuncargs(function):
""" fill missing funcargs. """
argnames = getfuncargnames(function.obj)
if argnames:
assert not function._args, "yielded functions cannot have funcargs"
for argname in argnames:
if argname not in function.funcargs:
request = FuncargRequest(pyfuncitem=function, argname=argname)
try:
function.funcargs[argname] = request.call_next_provider()
except request.Error:
request._raiselookupfailed()
request = FuncargRequest(pyfuncitem=function)
request._fillfuncargs()
_notexists = object()
@ -78,13 +70,13 @@ class FunctionCollector(py.test.collect.Collector):
class FuncargRequest:
_argprefix = "pytest_funcarg__"
_argname = None
class Error(LookupError):
""" error on performing funcarg request. """
def __init__(self, pyfuncitem, argname):
def __init__(self, pyfuncitem):
self._pyfuncitem = pyfuncitem
self.argname = argname
self.function = pyfuncitem.obj
self.module = pyfuncitem.getparent(py.test.collect.Module).obj
self.cls = getattr(self.function, 'im_class', None)
@ -97,14 +89,20 @@ class FuncargRequest:
self._plugins.append(self.module)
if self.instance is not None:
self._plugins.append(self.instance)
self._provider = self.config.pluginmanager.listattr(
plugins=self._plugins,
attrname=self._argprefix + str(argname)
)
self._funcargs = self._pyfuncitem.funcargs.copy()
self._provider = {}
def _fillfuncargs(self):
argnames = getfuncargnames(self.function)
if argnames:
assert not self._pyfuncitem._args, "yielded functions cannot have funcargs"
for argname in argnames:
if argname not in self._pyfuncitem.funcargs:
self._pyfuncitem.funcargs[argname] = self.getfuncargvalue(argname)
def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
if not hasattr(self.config, '_setupcache'):
self.config._setupcache = {}
self.config._setupcache = {} # XXX weakref?
cachekey = (self._getscopeitem(scope), extrakey)
cache = self.config._setupcache
try:
@ -117,11 +115,34 @@ class FuncargRequest:
return val
def call_next_provider(self):
if not self._provider:
raise self.Error("no provider methods left")
next_provider = self._provider.pop()
if not self._provider[self._argname]:
raise self.Error("no provider methods left for %r" % self._argname)
next_provider = self._provider[self._argname].pop()
return next_provider(request=self)
def getfuncargvalue(self, argname):
try:
return self._funcargs[argname]
except KeyError:
pass
assert argname not in self._provider
self._provider[argname] = self.config.pluginmanager.listattr(
plugins=self._plugins,
attrname=self._argprefix + str(argname)
)
# during call_next_provider() we keep state about the current
# argument on the request object - we may go for instantiating
# request objects per each funcargname if neccessary
oldname = self._argname
self._argname = argname
try:
self._funcargs[argname] = res = self.call_next_provider()
except self.Error:
self._raiselookupfailed(argname)
if oldname:
self._argname = oldname
return res
def _getscopeitem(self, scope):
if scope == "function":
return self._pyfuncitem
@ -134,9 +155,9 @@ class FuncargRequest:
self.config._setupstate.addfinalizer(finalizer=finalizer, colitem=colitem)
def __repr__(self):
return "<FuncargRequest %r for %r>" %(self.argname, self._pyfuncitem)
return "<FuncargRequest for %r>" %(self._pyfuncitem)
def _raiselookupfailed(self):
def _raiselookupfailed(self, argname):
available = []
for plugin in self._plugins:
for name in vars(plugin):
@ -146,7 +167,7 @@ class FuncargRequest:
available.append(name)
fspath, lineno, msg = self._pyfuncitem.reportinfo()
line = "%s:%s" %(fspath, lineno)
msg = "funcargument %r not found for: %s" %(self.argname, line)
msg = "funcargument %r not found for: %s" %(argname, line)
msg += "\n available funcargs: %s" %(", ".join(available),)
raise LookupError(msg)

View File

@ -27,7 +27,7 @@ def test_generic(plugintester):
def test_funcarg(testdir):
from py.__.test.funcargs import FuncargRequest
item = testdir.getitem("def test_func(tmpdir): pass")
p = pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir"))
p = pytest_funcarg__tmpdir(FuncargRequest(item))
assert p.check()
bn = p.basename.strip("0123456789-")
assert bn.endswith("test_func")

View File

@ -85,8 +85,7 @@ class TestRequest:
def pytest_funcarg__something(request): pass
def test_func(something): pass
""")
req = funcargs.FuncargRequest(item, argname="other")
assert req.argname == "other"
req = funcargs.FuncargRequest(item)
assert req.function == item.obj
assert hasattr(req.module, 'test_func')
assert req.cls is None
@ -100,46 +99,86 @@ class TestRequest:
def test_func(self, something):
pass
""")
req = funcargs.FuncargRequest(item, argname="something")
req = funcargs.FuncargRequest(item)
assert req.cls.__name__ == "TestB"
assert req.instance.__class__ == req.cls
def test_request_contains_funcargs_provider(self, testdir):
def XXXtest_request_contains_funcargs_provider(self, testdir):
modcol = testdir.getmodulecol("""
def pytest_funcarg__something(request):
pass
class TestClass:
def test_method(self, something):
pass
pass
""")
item1, = testdir.genitems([modcol])
assert item1.name == "test_method"
provider = funcargs.FuncargRequest(item1, "something")._provider
provider = funcargs.FuncargRequest(item1)._provider
assert len(provider) == 1
assert provider[0].__name__ == "pytest_funcarg__something"
def test_request_call_next_provider(self, testdir):
item = testdir.getitem("""
def pytest_funcarg__something(request): pass
def pytest_funcarg__something(request): return 1
def test_func(something): pass
""")
req = funcargs.FuncargRequest(item, "something")
val = req.call_next_provider()
assert val is None
req = funcargs.FuncargRequest(item)
val = req.getfuncargvalue("something")
assert val == 1
py.test.raises(req.Error, "req.call_next_provider()")
def test_getfuncargvalue(self, testdir):
item = testdir.getitem("""
l = [2]
def pytest_funcarg__something(request): return 1
def pytest_funcarg__other(request):
return l.pop()
def test_func(something): pass
""")
req = funcargs.FuncargRequest(item)
val = req.getfuncargvalue("something")
assert val == 1
val = req.getfuncargvalue("something")
assert val == 1
val2 = req.getfuncargvalue("other")
assert val2 == 2
val2 = req.getfuncargvalue("other") # see about caching
assert val2 == 2
req._fillfuncargs()
assert item.funcargs == {'something': 1}
def test_request_addfinalizer(self, testdir):
item = testdir.getitem("""
def pytest_funcarg__something(request): pass
def test_func(something): pass
""")
req = funcargs.FuncargRequest(item, "something")
req = funcargs.FuncargRequest(item)
py.test.raises(ValueError, "req.addfinalizer(None, scope='xyz')")
l = [1]
req.addfinalizer(l.pop)
req.config._setupstate._teardown(item)
assert not l
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
item, = testdir.genitems([modcol])
req = funcargs.FuncargRequest(item)
assert req.fspath == modcol.fspath
class TestRequestProtocol:
@py.test.mark.xfail
def test_protocol(self, testdir):
item = testdir.getitem("""
def pytest_funcarg_arg1(request): return 1
def pytest_funcarg_arg2(request): return 2
def test_func(arg1, arg2): pass
""")
req = funcargs.FuncargRequest(item)
req._fillargs()
#assert item.funcreq.
class TestRequestCachedSetup:
def test_request_cachedsetup(self, testdir):
item1,item2 = testdir.getitems("""
class TestClass:
@ -148,7 +187,7 @@ class TestRequest:
def test_func2(self, something):
pass
""")
req1 = funcargs.FuncargRequest(item1, "something")
req1 = funcargs.FuncargRequest(item1)
l = ["hello"]
def setup():
return l.pop()
@ -156,13 +195,13 @@ class TestRequest:
assert ret1 == "hello"
ret1b = req1.cached_setup(setup)
assert ret1 == ret1b
req2 = funcargs.FuncargRequest(item2, "something")
req2 = funcargs.FuncargRequest(item2)
ret2 = req2.cached_setup(setup)
assert ret2 == ret1
def test_request_cachedsetup_extrakey(self, testdir):
item1 = testdir.getitem("def test_func(): pass")
req1 = funcargs.FuncargRequest(item1, "something")
req1 = funcargs.FuncargRequest(item1)
l = ["hello", "world"]
def setup():
return l.pop()
@ -202,11 +241,6 @@ class TestRequest:
"*3 passed*"
])
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
item, = testdir.genitems([modcol])
req = funcargs.FuncargRequest(item, "xxx")
assert req.fspath == modcol.fspath
class TestMetafunc:
def test_no_funcargs(self, testdir):

View File

@ -4,10 +4,10 @@ from py.__.test import parseopt
pytest_plugins = 'pytest_iocapture'
class TestParser:
def test_init(self, stdcapture):
def test_init(self, capsys):
parser = parseopt.Parser(usage="xyz")
py.test.raises(SystemExit, 'parser.parse(["-h"])')
out, err = stdcapture.reset()
out, err = capsys.reset()
assert out.find("xyz") != -1
def test_group_add_and_get(self):