[py-dev] Decorators and funcargs in py.test

Vyacheslav Rafalskiy rafalskiy at gmail.com
Mon Jun 6 15:24:55 CEST 2011


Thanks Holger,

It is clearer now.

Vyacheslav

On Fri, Jun 3, 2011 at 1:38 PM, holger krekel <holger at merlinux.eu> wrote:
> 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[0]
>> >> >>         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[0]
>> >> >            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[0]
>>         run_with_timeout(timeout)(item.obj)(**item.funcargs)
>>
>> 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
>  implementation functions.
> * 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 :)
>
> best,
> holger
>
>> 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.
>>
>>
>>  Vyacheslav
>>
>



More information about the Pytest-dev mailing list