[py-dev] Decorators and funcargs in py.test
holger at merlinux.eu
Fri Jun 3 19:38:44 CEST 2011
On Fri, Jun 03, 2011 at 12:06 -0400, Vyacheslav Rafalskiy wrote:
> On Thu, Jun 2, 2011 at 12:46 AM, holger krekel <holger at merlinux.eu> wrote:
> > On Wed, Jun 01, 2011 at 17:16 -0400, Vyacheslav Rafalskiy wrote:
> >> >> #---------->> in test_1.py
> >> >> @pytest.mark.timeout(10)
> >> >> def test_f1():
> >> >> # test here
> >> >>
> >> >> #---------->> in conftest.py
> >> >> def pytest_runtest_call(item):
> >> >> if hasattr(item.obj, 'timeout'):
> >> >> timeout = item.obj.timeout.args
> >> >> item.obj = run_with_timeout(timeout)(item.obj)
> >> >>
> >> >> Your comments are welcome.
> >> >
> >> > it's basically ok but there are some bits that could
> >> > be improved. You are actually implementing the general
> >> > test item call. Also I am not sure why you assign
> >> > "item.obj = ...".
> >> I replace (or so I think) the original test function by the decorated
> >> one. It gets called elsewhere.
> > ah, of course :)
> >> > I'd probably write something like this:
> >> >
> >> > @pytest.mark.tryfirst
> >> > def pytest_pyfunc_call(pyfuncitem, __multicall__):
> >> > if 'timeout' in pyfuncitem.keywords:
> >> > timeout = pyfuncitem.keywords['timeout'].args
> >> > run_with_timeout(timeout)(__multicall__.execute)
> >> this will take a while to digest
> > it's actually wrong if run_with_timeout is only decorating
> > but not running the function. I think it makes sense to
> > rather directly call a helper which calls the function
> > (note that __multicall__.execute() will execute the remainder
> > of the hook chain one of which will actually execute the
> > function). Such a helper would look like
> > call_with_timeout(timeout, func) i guess.
> To call the function I need to know what arguments to give it to.
> The following seems to work, but this is just a guess:
> def pytest_runtest_call(item):
> if hasattr(item.obj, 'timeout'):
> timeout = item.obj.timeout.args
> I still don't quite get your example, specifically the
> __multicall__.execute part.
A few things that might help you to understand:
* There can be multiple hook functions implementing the same hook.
* MultiCall instances manage the calling into a list of hook
* A hook impl function can use zero or any number of available
arguments. i.e. if a hookspec is "pyest_myfunc(x,y,z)" then
a hook impl function can just receive only one of them "pytest_myfunc(x)"
This slicing is done from the MultiCall class.
* the actual call to a hook impl function is always done by
passing keyword arguments (i.e. **kwargs) and thus ordering
of parameters is irrelevant.
* a hook impl function can receive a "__multicall__" parameter
which is its managing class and which it can use to call
__multicall__.execute() to execute the rest of the hook implementations.
* with all this in mind the few lines of code in _pytest.core.MultiCall
hopefully make more sense :)
> Btw, the pytest_pyfunc_call() parameters seem to be in the wrong order
> based on _pytest.python.py and the prototype in _pytest.hookspec.py
> only lists one parameter.
More information about the Pytest-dev