[py-dev] Decorators and funcargs in py.test
holger krekel
holger at merlinux.eu
Thu Jun 2 06:46:06 CEST 2011
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.
best,
holger
> > main differences:
> >
> > * only applies to python test function calls
> > * hook invocation will be "tried first" before other
> > pytest_pyfunc_call hook impls and it will call those
> > other hooks through the "__multicall__" bit
> > which actually represents the ongoing hook call
> > * will call other hook implementations
> >
> > How do you actually implement run_with_timeout, btw?
>
> def run_with_timeout(timeout=None):
> def _decorator(f):
> def _wrapper(*args, **kwargs):
> def _f(*args, **kwargs):
> try:
> _result = f(*args, **kwargs)
> except Exception as e:
> thread._exception = e
> else:
> thread._result = _result
>
> thread = threading.Thread(target=_f, args=args, kwargs=kwargs)
> thread.daemon = True
> thread._exception = None
>
> thread.start()
> thread.join(timeout=timeout)
> if thread.isAlive():
> raise RuntimeError('function *%s* exceeded configured
> timeout of %ss' % (f.__name__, timeout))
> if thread._exception is None:
> return thread._result
> else:
> raise thread._exception
> _wrapper.__name__ = f.__name__
> return _wrapper
> return _decorator
>
>
>
> >
> > best,
> > holger
> >
>
> Vyacheslav
>
More information about the Pytest-dev
mailing list