* 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:
parent
d1f24aa251
commit
dcee9bdd6e
|
@ -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
|
||||||
++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue