我正在努力解决如何编写一个上下文管理器,该上下文管理器在处理任何异常的同时处理一些日志的问题。我要解决的问题是制作这样的代码:
try:
# code that can raise exception here
except Exception as e:
print('failed', e)
print('all good')
这是我代码中的重复模式,我认为最好使用上下文管理器来处理,例如:
with my_ctx_manager(success_msg='all good', failed_msg='failed):
# code that can raise exception here
这看起来好多了,但是我不知道如何编写实际的上下文管理器来处理上下文中可能出现的任何异常。
@contextlib.contextmanager
def my_ctx_manager(success_msg, failed_msg):
try:
# if no exception then print(success_msg)
# How do I catch any exception here
except Exception:
print(failed_msg)
# I need the exception to propagate as well
raise
我想我的问题更多是类型:如何确保上下文管理器正确捕获,记录并重新引发正在包装的代码的任何异常?
@contextmanager
装饰器的工作方式,您应该在上下文管理器函数中编写一次@contextmanager
,以便在yield
语句暂停函数执行的同时执行with
块。这意味着,如果yield
块引发异常,则可以通过将with
包装在yield
/ try
块中来捕获它:
except
输出:
from contextlib import contextmanager
@contextmanager
def example():
print('entered the context manager')
managed_resource = 'some resource'
try:
yield managed_resource
except Exception as e:
print('caught:', e)
# any cleanup that should only be done on failure
raise
else:
# any cleanup that should only be done on success
print('no exception was thrown')
finally:
# any cleanup that should always be done
print('exited the context manager')
with example() as resource:
print('resource:', resource)
raise ValueError('some error message')
如果要捕获所有内容(而不仅仅是entered the context manager
resource: some resource
caught: some error message
exited the context manager
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
ValueError: some error message
,则可以编写一个裸露的Exception
块并使用except:
获取异常信息。
而不是使用sys.exc_info()
,我发现此任务有点笨拙(您需要伪造一个带有yield的生成器以记录异常),而是自己编写一个上下文管理器。它只是一个带有两个特殊方法的类。使用预定义的sys.exc_info()
,您只需实现其中之一即可:
contextmanager