Re: [py-dev] Using funcargs with decorators
On 10/12/2012 06:50 PM, Floris Bruynooghe wrote: > Are these being addressed by PEP 362? I must admit I haven't had time > to read this yet, but if functools.wraps gets updated it was my > understanding that the *args, **kwargs inspection issue should be > solved and if you can set attributes on the signature object it could > stay persistent in the face of composition of decorators? I was not aware of PEP 362, which seems a good step in that direction, but indeed if functools.wraps does not update the signature automatically, it won't be used much in practice I fear. ciao, Anto ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
On 12 October 2012 10:11, Antonio Cuni wrote: > On 10/11/2012 11:53 PM, holger krekel wrote: >> >> On a sidenote, i am not sure Python's decorator design was such >> a great idea. Maybe it should have been restricted to setting attributes >> (like C# and also java IIRC) and then a way to get those attributed >> functions on a per-class, per-module or even global basis. > > well, there are obviously things which you can't do by just setting > attributes, and moreover nothing would stop people to do the old "fn = > decor(fn)" trick. > > I think that in general the *args, **kwargs pattern works well enough. The > only two drawbacks I can see in day-to-day usage are: > > - that the name of the decorated function is different than the original one, > although nowadays we have @functools.wraps (but it's still not widely used). > > - that decorators are not composable is one wraps and the other sets an > attribute: depending on the order they are applied we might set the attribute > on the wrapper or wrapped object. Are these being addressed by PEP 362? I must admit I haven't had time to read this yet, but if functools.wraps gets updated it was my understanding that the *args, **kwargs inspection issue should be solved and if you can set attributes on the signature object it could stay persistent in the face of composition of decorators? Regards, Floris ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
On 10/11/2012 11:53 PM, holger krekel wrote: > > On a sidenote, i am not sure Python's decorator design was such > a great idea. Maybe it should have been restricted to setting attributes > (like C# and also java IIRC) and then a way to get those attributed > functions on a per-class, per-module or even global basis. well, there are obviously things which you can't do by just setting attributes, and moreover nothing would stop people to do the old "fn = decor(fn)" trick. I think that in general the *args, **kwargs pattern works well enough. The only two drawbacks I can see in day-to-day usage are: - that the name of the decorated function is different than the original one, although nowadays we have @functools.wraps (but it's still not widely used). - that decorators are not composable is one wraps and the other sets an attribute: depending on the order they are applied we might set the attribute on the wrapper or wrapped object. For more advanced usages, it would be nice to have an automatic way to "copy" the signature from the wrapped to the wrapper: this alone would solve both the pypy and the OP problems. Additional bonus: a standard way to declare (via a function attribute) that this function object is a wrapper for this other, to be used e.g. by debuggers to show the "correct" source code. E.g., I have a "source" command in pdb++ which breaks in case of wrappers: @mydecor def foo(...): ... (pdb++) source foo def mydecor(fn): def wrapped(*args, **kwargs): return fn(*args, **kwargs) return wrapped ciao, Anto ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
Hi Anto, On Thu, Oct 11, 2012 at 22:10 +0200, Antonio Cuni wrote: > Hi Holger, Sebastian, > > On 10/11/2012 03:16 PM, holger krekel wrote: > > ah, now i get it. You want to assign the function back. > > That is indeed not going to work as pytest then sees the rollback > > function (i assume you return another function from this decorator). > > What is the decorator-returned function doing? > > I admit I did not follow the discussion deeply. However, if the problem is > that py.test sees the decorated function (which presumably uses *args and > **kwargs) instead of the original one, it can be solved by using the same > technique I used for enforceargs in pypy: > > https://bitbucket.org/pypy/pypy/src/7f6d5c878b90/pypy/rlib/objectmodel.py#cl-170 > > in practice, the trick is to exec() a function def with the correct argument > list instead of just relying on *args, **kwargs. This way, py.test should be > able to find the correct signature. I agree that is one way to solve it. However, if "hiding" the function can be avoided alltogether, then it's even better. On a sidenote, i am not sure Python's decorator design was such a great idea. Maybe it should have been restricted to setting attributes (like C# and also java IIRC) and then a way to get those attributed functions on a per-class, per-module or even global basis. best, holger ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
Hi Holger, Sebastian, On 10/11/2012 03:16 PM, holger krekel wrote: > ah, now i get it. You want to assign the function back. > That is indeed not going to work as pytest then sees the rollback > function (i assume you return another function from this decorator). > What is the decorator-returned function doing? I admit I did not follow the discussion deeply. However, if the problem is that py.test sees the decorated function (which presumably uses *args and **kwargs) instead of the original one, it can be solved by using the same technique I used for enforceargs in pypy: https://bitbucket.org/pypy/pypy/src/7f6d5c878b90/pypy/rlib/objectmodel.py#cl-170 in practice, the trick is to exec() a function def with the correct argument list instead of just relying on *args, **kwargs. This way, py.test should be able to find the correct signature. ciao, anto ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
Hi Sebastian, On Thu, Oct 11, 2012 at 14:44 +0200, Sebastian Rahlf wrote: > Hi Holger! > > >> At work we use a decorator @rollback on selected test functions which > >> will rollback any db changes made during that test. > >> > >> I've recently started using pytest's dependency injection for a few > >> use cases, both with @pytest.mark.parametrize(...) and the > >> pytest_funcarg__XXX hook. > >> Unfortunately, this clashes with our decorated test functions. > >> > >> How can I make this work? > >> > >> My first idea was using a custom marker, say @pytest.mark.rollback and > >> do something like: > >> > >> def rollback(meth): > >> """Original rollback function""" > >> ... > >> > >> def pytest_runtest_setup(item): > >> if not isinstance(item, pytest.Function): > >> return > >> if hasattr(item.obj, 'rollback'): > >> item = rollback(item) > >> > >> Would an approach like this actually work? > > > > I think so - probably you need to call "rollback(item.obj") though. > > Thanks for your feedback. I've tried it again with the following code: > > # conftest.py > import pytest > from unittests import rollback > > def pytest_configure(config): > # register an additional marker > config.addinivalue_line("markers", > "rollback: rollback any db changes after test") > > def pytest_runtest_setup(item): > if not isinstance(item, pytest.Function): > return > if hasattr(item.obj, 'rollback'): > item.obj = rollback(item.obj) > > # test_my_tests.py > > import pytest > > @pytest.mark.rollback > def test_rollback(monkepatch): > # ... > assert True > > What I get is a "TypeError: test_rollback() takes exactly 1 argument (0 > given)". > How can I make this work? ah, now i get it. You want to assign the function back. That is indeed not going to work as pytest then sees the rollback function (i assume you return another function from this decorator). What is the decorator-returned function doing? Did you check out the transact example in http://pytest.org/dev/fixture.html that i referenced in the stackoverflow anwser? best, holger ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
Hi Holger! >> At work we use a decorator @rollback on selected test functions which >> will rollback any db changes made during that test. >> >> I've recently started using pytest's dependency injection for a few >> use cases, both with @pytest.mark.parametrize(...) and the >> pytest_funcarg__XXX hook. >> Unfortunately, this clashes with our decorated test functions. >> >> How can I make this work? >> >> My first idea was using a custom marker, say @pytest.mark.rollback and >> do something like: >> >> def rollback(meth): >> """Original rollback function""" >> ... >> >> def pytest_runtest_setup(item): >> if not isinstance(item, pytest.Function): >> return >> if hasattr(item.obj, 'rollback'): >> item = rollback(item) >> >> Would an approach like this actually work? > > I think so - probably you need to call "rollback(item.obj") though. Thanks for your feedback. I've tried it again with the following code: # conftest.py import pytest from unittests import rollback def pytest_configure(config): # register an additional marker config.addinivalue_line("markers", "rollback: rollback any db changes after test") def pytest_runtest_setup(item): if not isinstance(item, pytest.Function): return if hasattr(item.obj, 'rollback'): item.obj = rollback(item.obj) # test_my_tests.py import pytest @pytest.mark.rollback def test_rollback(monkepatch): # ... assert True What I get is a "TypeError: test_rollback() takes exactly 1 argument (0 given)". How can I make this work? Cheers, Seb. ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev
Re: [py-dev] Using funcargs with decorators
Hi Sebastian, On Thu, Oct 11, 2012 at 11:47 +0200, Sebastian Rahlf wrote: > Hi! > > At work we use a decorator @rollback on selected test functions which > will rollback any db changes made during that test. > > I've recently started using pytest's dependency injection for a few > use cases, both with @pytest.mark.parametrize(...) and the > pytest_funcarg__XXX hook. > Unfortunately, this clashes with our decorated test functions. > > How can I make this work? > > My first idea was using a custom marker, say @pytest.mark.rollback and > do something like: > > def rollback(meth): > """Original rollback function""" > ... > > def pytest_runtest_setup(item): > if not isinstance(item, pytest.Function): > return > if hasattr(item.obj, 'rollback'): > item = rollback(item) > > Would an approach like this actually work? I think so - probably you need to call "rollback(item.obj") though. > Sebastian > > P.S. I've posted this to stackoverflow before I remembered that there > is a mailing list > http://stackoverflow.com/questions/12836134/pytest-using-dependency-injection-with-decorators I answered there as well. best, holger ___ > py-dev mailing list > py-dev@codespeak.net > http://codespeak.net/mailman/listinfo/py-dev > ___ py-dev mailing list py-dev@codespeak.net http://codespeak.net/mailman/listinfo/py-dev