Creating APIs that work as both decorators and context managers

Hello all, I've put a recipe up on the Python cookbook for creating APIs that work as both decorators and context managers and wonder if it would be considered a useful addition to the functools module. http://code.activestate.com/recipes/577273-decorator-and-context-manager-fro... I wrote this after writing almost identical code the second time for "patch" in the mock module. (The patch decorator can be used as a decorator or as a context manager and I was writing a new variant.) Both py.test and django have similar code in places, so it is not an uncommon pattern. It is only 40 odd lines (ignore the ugly Python 2 & 3 compatibility hack), so I'm fine with it living on the cookbook - but it is at least slightly fiddly to write and has the added niceness of providing the optional exception handling semantics of __exit__ for decorators as well. Example use (really hope email doesn't swallow the whitespace - my apologies in advance if it does): from context import Context class mycontext(Context): def __init__(self, *args): """Normal initialiser""" def start(self): """ Called on entering the with block or starting the decorated function. If used in a with statement whatever this method returns will be the context manager. """ def finish(self, *exc): """ Called on exit. Arguments and return value of this method have the same meaning as the __exit__ method of a normal context manager. """ @mycontext('some', 'args') def function(): pass with mycontext('some', 'args') as something: pass I'm not entirely happy with the name of the class or the start and finish methods, so open to suggestions there. start and finish *could* be __enter__ and __exit__ - but that would make the class you implement *look* like a normal context manager and I thought it was better to distinguish them. Perhaps before and after? All the best, Michael Foord -- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.

On Fri, Jun 25, 2010 at 7:35 PM, Michael Foord <fuzzyman@voidspace.org.uk> wrote:
Hello all,
I've put a recipe up on the Python cookbook for creating APIs that work as both decorators and context managers and wonder if it would be considered a useful addition to the functools module. http://code.activestate.com/recipes/577273-decorator-and-context-manager-fro...
It's an interesting idea. I wanted almost exactly this a little while ago, while doing some experiments to add an IEEE 754-compliance wrapper to the decimal module (for my own use). It seems quite natural that one might want to wrap both functions and blocks in the same way. [1] In case anyone wants the details, this was for a 'delay-exceptions' operation, that allows you to execute some number of arithmetic operations, keeping track of the floating-point signals that they produce but not raising the corresponding exceptions until the end of the block; obviously this idea applies equally well to functions as to blocks. It's one of the recommended exception handling modes from section 8 of IEEE 754-2008. Mark

On 25/06/2010 19:35, Michael Foord wrote:
Hello all,
I've put a recipe up on the Python cookbook for creating APIs that work as both decorators and context managers and wonder if it would be considered a useful addition to the functools module.
http://code.activestate.com/recipes/577273-decorator-and-context-manager-fro...
Actually contextlib would be a much more sensible home for it. Michael
I wrote this after writing almost identical code the second time for "patch" in the mock module. (The patch decorator can be used as a decorator or as a context manager and I was writing a new variant.) Both py.test and django have similar code in places, so it is not an uncommon pattern.
It is only 40 odd lines (ignore the ugly Python 2 & 3 compatibility hack), so I'm fine with it living on the cookbook - but it is at least slightly fiddly to write and has the added niceness of providing the optional exception handling semantics of __exit__ for decorators as well.
Example use (really hope email doesn't swallow the whitespace - my apologies in advance if it does):
from context import Context
class mycontext(Context): def __init__(self, *args): """Normal initialiser"""
def start(self): """ Called on entering the with block or starting the decorated function.
If used in a with statement whatever this method returns will be the context manager. """
def finish(self, *exc): """ Called on exit. Arguments and return value of this method have the same meaning as the __exit__ method of a normal context manager. """
@mycontext('some', 'args') def function(): pass
with mycontext('some', 'args') as something: pass
I'm not entirely happy with the name of the class or the start and finish methods, so open to suggestions there. start and finish *could* be __enter__ and __exit__ - but that would make the class you implement *look* like a normal context manager and I thought it was better to distinguish them. Perhaps before and after?
All the best,
Michael Foord -- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
participants (2)
-
Mark Dickinson
-
Michael Foord