* 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 specific test function. Request objects allow providers
to access test configuration and test context: to access test configuration and test context:
``request.argname``: name of the requested function argument
``request.function``: python function object requesting the argument ``request.function``: python function object requesting the argument
``request.cls``: class object where the test function is defined in or None. ``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`_ ``request.param``: if exists was passed by a `parametrizing test generator`_
perform scoped setup and teardown 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()) request.addfinalizer(lambda: myfile.close())
return myfile 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 decorating other funcarg providers
++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++

View File

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

View File

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

View File

@ -85,8 +85,7 @@ class TestRequest:
def pytest_funcarg__something(request): pass def pytest_funcarg__something(request): pass
def test_func(something): pass def test_func(something): pass
""") """)
req = funcargs.FuncargRequest(item, argname="other") req = funcargs.FuncargRequest(item)
assert req.argname == "other"
assert req.function == item.obj assert req.function == item.obj
assert hasattr(req.module, 'test_func') assert hasattr(req.module, 'test_func')
assert req.cls is None assert req.cls is None
@ -100,11 +99,11 @@ class TestRequest:
def test_func(self, something): def test_func(self, something):
pass pass
""") """)
req = funcargs.FuncargRequest(item, argname="something") req = funcargs.FuncargRequest(item)
assert req.cls.__name__ == "TestB" assert req.cls.__name__ == "TestB"
assert req.instance.__class__ == req.cls 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(""" modcol = testdir.getmodulecol("""
def pytest_funcarg__something(request): def pytest_funcarg__something(request):
pass pass
@ -114,32 +113,72 @@ class TestRequest:
""") """)
item1, = testdir.genitems([modcol]) item1, = testdir.genitems([modcol])
assert item1.name == "test_method" assert item1.name == "test_method"
provider = funcargs.FuncargRequest(item1, "something")._provider provider = funcargs.FuncargRequest(item1)._provider
assert len(provider) == 1 assert len(provider) == 1
assert provider[0].__name__ == "pytest_funcarg__something" assert provider[0].__name__ == "pytest_funcarg__something"
def test_request_call_next_provider(self, testdir): def test_request_call_next_provider(self, testdir):
item = testdir.getitem(""" item = testdir.getitem("""
def pytest_funcarg__something(request): pass def pytest_funcarg__something(request): return 1
def test_func(something): pass def test_func(something): pass
""") """)
req = funcargs.FuncargRequest(item, "something") req = funcargs.FuncargRequest(item)
val = req.call_next_provider() val = req.getfuncargvalue("something")
assert val is None assert val == 1
py.test.raises(req.Error, "req.call_next_provider()") 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): def test_request_addfinalizer(self, testdir):
item = testdir.getitem(""" item = testdir.getitem("""
def pytest_funcarg__something(request): pass def pytest_funcarg__something(request): pass
def test_func(something): pass def test_func(something): pass
""") """)
req = funcargs.FuncargRequest(item, "something") req = funcargs.FuncargRequest(item)
py.test.raises(ValueError, "req.addfinalizer(None, scope='xyz')") py.test.raises(ValueError, "req.addfinalizer(None, scope='xyz')")
l = [1] l = [1]
req.addfinalizer(l.pop) req.addfinalizer(l.pop)
req.config._setupstate._teardown(item) req.config._setupstate._teardown(item)
assert not l 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): def test_request_cachedsetup(self, testdir):
item1,item2 = testdir.getitems(""" item1,item2 = testdir.getitems("""
class TestClass: class TestClass:
@ -148,7 +187,7 @@ class TestRequest:
def test_func2(self, something): def test_func2(self, something):
pass pass
""") """)
req1 = funcargs.FuncargRequest(item1, "something") req1 = funcargs.FuncargRequest(item1)
l = ["hello"] l = ["hello"]
def setup(): def setup():
return l.pop() return l.pop()
@ -156,13 +195,13 @@ class TestRequest:
assert ret1 == "hello" assert ret1 == "hello"
ret1b = req1.cached_setup(setup) ret1b = req1.cached_setup(setup)
assert ret1 == ret1b assert ret1 == ret1b
req2 = funcargs.FuncargRequest(item2, "something") req2 = funcargs.FuncargRequest(item2)
ret2 = req2.cached_setup(setup) ret2 = req2.cached_setup(setup)
assert ret2 == ret1 assert ret2 == ret1
def test_request_cachedsetup_extrakey(self, testdir): def test_request_cachedsetup_extrakey(self, testdir):
item1 = testdir.getitem("def test_func(): pass") item1 = testdir.getitem("def test_func(): pass")
req1 = funcargs.FuncargRequest(item1, "something") req1 = funcargs.FuncargRequest(item1)
l = ["hello", "world"] l = ["hello", "world"]
def setup(): def setup():
return l.pop() return l.pop()
@ -202,11 +241,6 @@ class TestRequest:
"*3 passed*" "*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: class TestMetafunc:
def test_no_funcargs(self, testdir): def test_no_funcargs(self, testdir):

View File

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