I would like to have:
# Simple example, one could replace try/except by any other nested construct
def mycontextmanager_generator(foo):
try:
yield
except:
print 'bar'
raise
mycontextmanager = build_contextmanager(mycontextmanager_generator)
mydecorator = build_decorator(mycontextmanager_generator)
>>> with mycontextmanager():
>>> raise Exception('baz gone bar in context manager')
... bar
>>> @mydecorator()
>>> def bazzer():
>>> raise Exception('baz gone bar in decorator')
>>> bazzer()
... bar
In this example, I build a context manager from a generator function, and a decorator from the same function. This is what I tried to achieve in an unsuccessful way.
More generally, what I want is to be DRY: write once the try/except
block, and re-use it through both a decorator and a context manager Again: by writing the try/except bloc only once , whether in a generator function or any other wrapper.
The ContextDecorator
thing (in contextlib
in py3 / contextlib2
in py2) is only usable with classes, but it seems to be useless in that case... Am I missing something? Is there a means to implement my try/except block with a class-based ContextManager using __enter__
and __exit__
?
Or is there a possibility to transform a contextmanager built with yield
syntax into a decorator?
Or the contrary (decorator to contextmanager)?
If no, would be happy to know what is the limitation of Python for that matter.
To my understanding, the yield
syntax is very tightly bound to the Python interpreter and context switching, and I don't know if it is possible to change its behavior on that point.
You can easily achieve what you need by combining the class that contextmanager
uses to manage its contexts ( _GeneratorContextManager
) and the ContextDecorator
class. eg.
from contextlib import ContextDecorator, _GeneratorContextManager
from functools import wraps
class MyContextManager(_GeneratorContextManager, ContextDecorator):
pass
def contextmanager(func):
@wraps(func)
def helper(*args, **kwds):
return MyContextManager(func, args, kwds)
return helper
@contextmanager
def print_bar_on_error():
try:
yield
except:
print('bar')
raise
with print_bar_on_error():
raise Exception('baz gone bar in context manager')
produces:
bar
Traceback (most recent call last):
File "run.py", line 28, in <module>
raise Exception('baz gone bar in context manager')
Exception: baz gone bar in context manager
And when used as a decorator
@print_bar_on_error()
def bazzer():
raise Exception('baz gone bar in decorator')
bazzer()
produces:
bar
Traceback (most recent call last):
File "run.py", line 32, in <module>
bazzer()
File "c:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\contextlib.py", line 30, in inner
return func(*args, **kwds)
File "run.py", line 31, in bazzer
raise Exception('baz gone bar in decorator')
Exception: baz gone bar in decorator
return func(*args, **kwds)
Exception: baz gone bar in decorator
A simpler to understand solution than Dunes' one, albeit not taking advantage of ContextDecorator
double-syntax.
import contextlib
import functools
def handler():
try:
yield
except:
print 'bar'
my_contextmanager = contextlib.contextmanager(handler)
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with my_contextmanager():
func(*args, **kwargs)
return wrapper
with my_contextmanager():
raise Exception('baz')
@my_decorator
def f():
raise Exception('baz')
f()
gives:
bar
bar
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.