Function acting as both decorator and context manager in Python? Function acting as both decorator and context manager in Python? python python

Function acting as both decorator and context manager in Python?


Starting in Python 3.2, support for this is even included in the standard library. Deriving from the class contextlib.ContextDecorator makes it easy to write classes that can be used as both, a decorator or a context manager. This functionality could be easily backported to Python 2.x -- here is a basic implementation:

class ContextDecorator(object):    def __call__(self, f):        @functools.wraps(f)        def decorated(*args, **kwds):            with self:                return f(*args, **kwds)        return decorated

Derive your context manager from this class and define the __enter__() and __exit__() methods as usual.


In Python 3.2+, you can define a context manager that is also a decorator using @contextlib.contextmanager.

From the docs:

contextmanager() uses ContextDecorator so the context managers it creates can be used as decorators as well as in with statements

Example usage:

>>> from contextlib import contextmanager>>> @contextmanager... def example_manager(message):...     print('Starting', message)...     try:...         yield...     finally:...         print('Done', message)... >>> with example_manager('printing Hello World'):...     print('Hello, World!')... Starting printing Hello WorldHello, World!Done printing Hello World>>> >>> @example_manager('running my function')... def some_function():...     print('Inside my function')... >>> some_function()Starting running my functionInside my functionDone running my function


class Decontext(object):    """    makes a context manager also act as decorator    """    def __init__(self, context_manager):        self._cm = context_manager    def __enter__(self):        return self._cm.__enter__()    def __exit__(self, *args, **kwds):        return self._cm.__exit__(*args, **kwds)    def __call__(self, func):        def wrapper(*args, **kwds):            with self:                return func(*args, **kwds)        return wrapper

now you can do:

mydeco = Decontext(some_context_manager)

and that allows both

@mydecodef foo(...):    do_bar()foo(...)

and

with mydeco:    do_bar()