¿Función que actúa como decorador y administrador de contexto en Python?

Esto podría estar empujando las cosas un poco demasiado lejos, pero sobre todo por curiosidad ..

¿Sería posible tener un objeto llamable (función / clase) que actúe como administrador de contexto y decorador al mismo tiempo?

def xxx(*args, **kw): # or as a class @xxx(foo, bar) def im_decorated(a, b): print('do the stuff') with xxx(foo, bar): print('do the stuff') 

A partir de Python 3.2, el soporte para esto se incluye incluso en la biblioteca estándar. Derivar de la clase contextlib.ContextDecorator facilita la escritura de clases que se pueden usar como decorador o administrador de contexto. Esta funcionalidad podría ser fácilmente devuelta a Python 2.x – aquí hay una implementación básica:

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

Derive su administrador de contexto de esta clase y defina los __enter__() y __exit__() como de costumbre.

En Python 3.2+, puede definir un administrador de contexto que también es un decorador que usa @contextlib.contextmanager .

De los documentos:

contextmanager() usa ContextDecorator para que los gestores de contexto que crea puedan usarse como decoradores, así como en declaraciones.

Ejemplo de uso:

 >>> 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 World Hello, World! Done printing Hello World >>> >>> @example_manager('running my function') ... def some_function(): ...  print('Inside my function') ... >>> some_function() Starting running my function Inside my function Done 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 

ahora puedes hacer

 mydeco = Decontext(some_context_manager) 

y eso permite tanto

 @mydeco def foo(...): do_bar() foo(...) 

y

 with mydeco: do_bar() 

Aquí hay un ejemplo:

 class ContextDecorator(object): def __init__(self, foo, bar): self.foo = foo self.bar = bar print "init", foo, bar def __call__(self, f): print "call" def wrapped_f(): print "about to call" f() print "done calling" return wrapped_f def __enter__(self): print "enter" def __exit__(self, exc_type, exc_val, exc_tb): print "exit" with ContextDecorator(1, 2): print "with" @ContextDecorator(3, 4) def sample(): print "sample" sample() 

Esto imprime:

 init 1 2 enter with exit init 3 4 call about to call sample done calling