为什么不重新抛出 try-except-else 中 else 中的异常?

时间:2021-06-01 08:05:21

标签: python error-handling try-catch

我在 python 中使用 try|except|else|finally。 如果 else 中的代码抛出异常,我希望我的整个脚本失败(在执行 finally 之后)。 我发现这不会发生。 else 内的异常正在被抑制。为什么?

MWE

import requests
def foo():
    try:
        resp = requests.get("https://example.com")
        status = resp.status_code
        assert resp.status_code < 300, "Bad status code"
    except requests.exceptions.BaseHTTPError:
        status = None
    else:
        print("Starting else branch")

        # this should fail
        # because the body is not json
        print(f"Response body was {resp.json()}")

        print("Finishing else branch")
    finally:
        # save the result persistently,
        # regardless of if it was good or bad
        with open('log.txt', 'w') as f:
            f.write(str(status))
        return status

print(f"foo() gracefully returned {foo()}")

我已经在 python 3.6.9、3.9.5 和 3.8 中尝试过。

期望的行为:

resp.json()simplejson.errors.JSONDecodeError 中抛出一个 else 异常,然后被捕获,运行 finally,但是来自 else 的异常是 重新抛出,因此整个脚本失败并显示 JSONDecodeError

stdout 显示:

<块引用>

启动 else 分支

然后抛出异常,回溯到 resp.json()

The python docs 表示 else|try|except|else 中的 finally 说:

<块引用>

使用 else 子句比在 try 子句中添加额外代码要好,因为它避免了意外捕获不是由 try ... except 语句保护的代码引发的异常。

所以听起来 else 子句的目的是将代码放在您希望捕获异常的地方。这就是我想要的。但似乎 else 正在 中的异常被捕获。

(注意,在这个例子中,我希望 AssertionError 导致整个函数失败,并且 BaseHTTPError 被捕获并优雅地处理。这对于实际用例来说没有意义,但这只是一个简单的MWE。)

实际行为

stdout 说:

<块引用>

启动 else 分支

foo() 优雅地返回 200

捕获JSONDecodeError内部的else异常,执行finally,代码返回200。也就是说,else 代码中的异常被捕获。所以看起来 else 分支中的代码try 保护的。

如果我想让 JSONDecodeError 被我的 .json() 行捕获,我会把它放在 try 中。 我如何让这个脚本捕捉到 JSONDecodeError 错误?

1 个答案:

答案 0 :(得分:2)

您所看到的内容已完美记录:

<块引用>

如果 finally 存在,它指定一个“清理”处理程序。执行 try 子句,包括任何 exceptelse 子句。如果在任何一个子句中发生异常并且没有被处理,则该异常被临时保存。 finally 子句被执行。如果有一个已保存的异常,它会在 finally 子句的末尾重新引发。如果 finally 子句引发另一个异常,则将保存的异常设置为新异常的上下文。如果 finally 子句执行 returnbreakcontinue 语句,则丢弃保存的异常:

>>> def f():
...     try:
...         1/0
...     finally:
...         return 42
...
>>> f()
42

https://docs.python.org/3/reference/compound_stmts.html#try

要在执行 else 后在 finally 内抛出异常,您不得在 return 内有 finally。取消缩进以将其移出 finally 分支。