如果确定是否在finally块中引发了异常,该如何确定?

时间:2018-03-04 19:42:36

标签: python logging exception-handling flow-control try-finally

finally条款中,是否可以判断是否存在异常?类似的东西:

try:
    funky code
finally:
    if ???:
        print('the funky code raised')

我希望这样做更干嘛:

try:
    funky code
except HandleThis:
    # handle it
    raised = True
except DontHandleThis:
    raised = True
    raise
else:
    raised = False
finally:
    logger.info('funky code raised %s', raised)

我不喜欢它需要捕获一个你不打算处理的异常,只是为了设置一个标志。

因为有些comments要求的人数较少" M"在MCVE中,这里有一些关于用例的更多背景知识。实际问题是关于日志记录级别的升级。

  • 时髦的代码是第三方,无法更改。
  • 失败异常和堆栈跟踪不包含任何有用的诊断信息,因此在except块中使用logger.exception在此处没有帮助。
  • 如果提出了时髦的代码,那么我需要查看的一些信息已经记录在DEBUG级别。我们没有也无法处理错误,但是想要升级DEBUG日志记录,因为所需的信息就在那里。
  • 大多数时候,时髦的代码不会引发。我不想为一般情况升级日志记录级别,因为它太冗长了。

因此,代码在日志捕获上下文(设置自定义处理程序以拦截日志记录)下运行,并且一些调试信息会被追溯重新记录:

try:
    with LogCapture() as log:
        funky_code()  # <-- third party badness
finally:
    mylog = mylogger.WARNING if <there was exception> else mylogger.DEBUG
    for record in log.captured:
        mylog(record.msg, record.args)

6 个答案:

答案 0 :(得分:40)

使用上下文管理器

您可以使用自定义上下文管理器,例如:

class DidWeRaise:
    __slots__ = ('exception_happened', )  # instances will take less memory

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # If no exception happened the `exc_type` is None
        self.exception_happened = exc_type is not None

然后在try

中使用它
try:
    with DidWeRaise() as error_state:
        # funky code
finally:
    if error_state.exception_happened:
        print('the funky code raised')

它仍然是一个额外的变量,但如果你想在多个地方使用它,它可能更容易重复使用。而且你不需要自己切换它。

使用变量

如果您不想要上下文管理器,我会反转触发器的逻辑并仅在没有异常发生时将其切换为 。这样,您就不需要except个案例来解决您不想处理的异常问题。最合适的地方是在else没有引发异常的情况下输入的try子句:

exception_happened = True
try:
    # funky code
except HandleThis:
    # handle this kind of exception
else:
    exception_happened = False
finally:
    if exception_happened:
        print('the funky code raised')

正如已经指出的那样,而不是一个&#34;切换&#34;变量你可以用所需的日志功能替换它(在这种情况下):

mylog = mylogger.WARNING
try:
    with LogCapture() as log:
        funky_code()
except HandleThis:
    # handle this kind of exception
else:
    # In case absolutely no exception was thrown in the try we can log on debug level
    mylog = mylogger.DEBUG
finally:
    for record in log.captured:
        mylog(record.msg, record.args)

当然,如果你把它放在你的try的末尾也会有用(正如这里建议的其他答案),但我更喜欢else条款,因为它有更多含义(&#34;该代码只有在try块&#34;)中没有异常时才会执行,并且从长远来看可能更容易维护。虽然它比上下文管理器更需要维护,因为变量在不同的地方设置和切换。

使用sys.exc_info(仅适用于未处理的例外)

我想提到的最后一种方法可能对你没有用,但对于那些只想知道是否存在未处理的异常(异常< >不在任何except块中捕获或已在except块内引发。在这种情况下,您可以使用sys.exc_info

import sys

try:
    # funky code
except HandleThis:
    pass
finally:
    if sys.exc_info()[0] is not None:
        # only entered if there's an *unhandled* exception, e.g. NOT a HandleThis exception
        print('funky code raised')

答案 1 :(得分:31)

mylog = WARNING
try:
    funky code
    mylog = DEBUG
except HandleThis:
    # handle it
finally:
    mylog(...)

鉴于在选择日志级别的问题中添加了额外的背景信息,这似乎很容易适应预期的用例:

{{1}}

答案 2 :(得分:3)

好的,听起来你实际上只想修改现有的上下文管理器,或者使用类似的方法:logbook实际上有一个名为FingersCrossedHandler的东西可以完全按照你想要的那样做。但你可以自己做,比如:

@contextmanager
def LogCapture():
    # your existing buffer code here
    level = logging.WARN
    try:
        yield
    except UselessException:
        level = logging.DEBUG
        raise  # Or don't, if you just want it to go away
    finally:
        # emit logs here

原始回复

你正在考虑这个问题。

打算处理异常 - 你通过设置一个标志来处理它。也许你不关心其他任何事情(这似乎是一个坏主意),但如果你在提出 异常时关心做某事,那么你想要明确它。 / p>

您正在设置变量,但希望异常继续,这意味着您真正想要的是从引发的异常中提出您自己的特定异常:

class MyPkgException(Exception):  pass
class MyError(PyPkgException): pass  # If there's another exception type, you can also inherit from that

def do_the_badness():
    try:
        raise FileNotFoundError('Or some other code that raises an error')
    except FileNotFoundError as e:
        raise MyError('File was not found, doh!') from e
    finally:
        do_some_cleanup()

try:
    do_the_badness()
except MyError as e:
    print('The error? Yeah, it happened')

这解决了:

  • 明确处理您要处理的例外
  • 使堆栈跟踪和原始异常可用
  • 允许您的代码在其他地方处理原始异常以处理您抛出的异常
  • 允许一些顶级异常处理代码捕获MyPkgException以捕获所有异常,以便它可以记录某些内容并退出状态良好而不是难看的堆栈跟踪

答案 3 :(得分:2)

您可以轻松地将捕获的异常分配给变量,并在finally块中使用它,例如:

>>> x = 1
>>> error = None
>>> try:
...     x.foo()
... except Exception as e:
...     error = e
... finally:
...     if error is not None:
...             print(error)
...
'int' object has no attribute 'foo'

答案 4 :(得分:-1)

如果是我,我会对您的代码进行一些重新排序。

raised = False
try:
    # funky code
except HandleThis:
    # handle it
    raised = True
except Exception as ex:
    # Don't Handle This 
    raise ex
finally:
    if raised:
        logger.info('funky code was raised')

我已将引发的布尔赋值放在try语句之外以确保范围,并使最终的except语句成为您不想处理的异常的一般异常处理程序。

此样式确定您的代码是否失败。另一种方法可能是我确定代码何时成功。

success = False
try:
    # funky code
    success = True
except HandleThis:
    # handle it
    pass
except Exception as ex:
    # Don't Handle This 
    raise ex
finally:
    if success:
        logger.info('funky code was successful')
    else:
        logger.info('funky code was raised')

答案 5 :(得分:-1)

如果发生异常->将此逻辑放在异常块中。
如果未发生异常->将此逻辑放在代码中可能发生异常的位置之后的try块中。

Finally blocks should be reserved for "cleanup actions," according to the Python language reference.当最终指定时,解释器将在 except 情况下进行如下操作:保存异常,然后 first首先执行finally块,最后引发异常。