简体   繁体   English

处理异常的上下文管理器

[英]Context Manager that handles exceptions

I am trying to wrap my head around how to write a context manager that deals with writing some logs while handling any exceptions.我试图围绕如何编写一个上下文管理器来处理在处理任何异常的同时编写一些日志。 The problem I am trying to solve is to make code like this:我试图解决的问题是编写这样的代码:

try:
    # code that can raise exception here
except Exception as e:
    print('failed', e)

print('all good')

This is a repeated pattern I have in the code and I think it's best handled with a context manager like:这是我在代码中的重复模式,我认为最好使用上下文管理器处理,例如:

with my_ctx_manager(success_msg='all good', failed_msg='failed):
    # code that can raise exception here

this looks much better, but I don't know how to write the actual context manager to deal with any exceptions that could rise inside the context.这看起来好多了,但我不知道如何编写实际的上下文管理器来处理可能在上下文中出现的任何异常。

@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

I guess my question is more of the type: How do I make sure that the context manager correctly catches, logs and re-raise any exception for the code that is wrapping ?我想我的问题更多是这样的:如何确保上下文管理器正确捕获、记录并重新引发正在包装的代码的任何异常?

The way the @contextmanager decorator works, you should write yield once within your context manager function, so that the with block will be executed while the yield statement pauses your function's execution. @contextmanager装饰器的工作方式是,您应该在上下文管理器函数中编写一次yield ,以便在yield语句暂停函数执行时执行with块。 That means if the with block throws an exception, you can catch it by wrapping yield in a try / except block:这意味着如果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')

Output:输出:

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

If you want to catch everything (not just Exception ), then you can write a bare except: block and use sys.exc_info() to get the exception information.如果您想捕获所有内容(不仅仅是Exception ),那么您可以编写一个裸的except:块并使用sys.exc_info()获取异常信息。

Instead of using contextmanager , which I find a bit clunky for this task (you need to fake a generator with a yield just to log an exception), write a context manager yourself.而不是使用contextmanager ,我觉得这个任务有点笨拙(你需要伪造一个带有 yield 的生成器来记录异常),而是自己编写一个上下文管理器。 Its simply a class with two special methods.它只是一个具有两个特殊方法的类。 And with the predefined AbstractContextManager you only need to implement one of them:使用预定义的AbstractContextManager您只需要实现其中之一:

import contextlib

class ExceptionLogger(contextlib.AbstractContextManager):

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            print("***Logging exception {}***".format((exc_type, exc_value, 
                traceback)))

with ExceptionLogger():
    raise ValueError("foo")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM