多个调用级别的异常处理

Kaw*_*awu 3 python exception try-except

如何最好地处理调用层次结构中引发异常的多个级别的方法,以便如果是致命错误,程序将退出(显示错误对话框后)?

我基本上来自 Java。在那里我简单地将任何方法声明为throws Exception,重新抛出它并在顶层的某个地方捕获它。

然而,Python 不同。我的 Python 代码基本上如下所示。

编辑:添加了更简单的代码......

主入口函数( plugin.py):

def main(catalog):

    print "Executing main(catalog)... "
    # instantiate generator
    gen = JpaAnnotatedClassGenerator(options)

    # run generator
    try:
        gen.generate_bar()  # doesn't bubble up
    except ValueError as error:
        Utilities.show_error("Error", error.message, "OK", "", "")
        return

    ... usually do the real work here if no error
Run Code Online (Sandbox Code Playgroud)

JpaAnnotatedClassGenerator班级 (engine.py):

class JpaAnnotatedClassGenerator:

    def generate_bar(self):
        self.generate_value_error()

    def generate_value_error(self):
        raise ValueError("generate_value_error() raised an error!")
Run Code Online (Sandbox Code Playgroud)

我想返回给调用者一个异常,该异常将被抛出回该调用,直到它到达最外层try-except以显示带有异常消息的错误对话框。

问题:如何在 Python 中最好地完成此操作?我真的必须try-except对每个被调用的方法重复吗?

顺便说一句:我使用的是 Python 2.6.x,由于绑定到提供解释器的 MySQL Workbench(Python 3 在其升级列表中),所以无法升级。

bru*_*ers 9

如果您没有捕获异常,它就会在调用堆栈中冒泡,直到有人捕获异常为止。如果没有人捕获它,运行时将获取它并终止,并显示异常错误消息和完整的回溯。IOW,您不必在任何地方显式捕获并重新引发异常 - 这实际上会破坏异常的全部意义。实际上,尽管异常主要用于错误/意外情况,但异常首先是一种控制流工具,允许打破正常执行流并将控制(和一些信息)传递到调用堆栈中的任意位置。

从这个 POV 来看,你的代码似乎是最正确的(警告:我没有费心阅读整个内容,只是快速浏览了一下),除了(没有双关语缩进)有几点:

首先,您应该定义自己的特定异常类,而不是使用内置的 ValueError (如果对您有意义,您可以继承它),这样您就可以确保只捕获您期望的确切异常(相当多的层)您自己的代码“之下”可能会引发您意想不到的 ValueError )。

然后,您可能(或不,取决于代码的使用方式)还想在函数中添加一个包罗万象的顶级处理程序,main()以便您可以正确记录(使用logger模块)所有错误并最终释放资源,做一些在进程终止之前进行清理等。

作为旁注,您可能还想学习和使用正确的字符串格式,并且 - 如果性能至少是一个问题 - 避免重复的常量调用,如下所示:

elif AnnotationUtil.is_embeddable_table(table) and AnnotationUtil.is_secondary_table(table):
    # ...
elif AnnotationUtil.is_embeddable_table(table):
    # ...
elif AnnotationUtil.is_secondary_table(table):
    # ...
Run Code Online (Sandbox Code Playgroud)

鉴于 Python 非常动态的特性,编译器和运行时都无法安全地优化这些重复的调用(该方法可能在调用之间动态地重新定义),因此您必须自己执行此操作。

编辑:

当尝试捕获 main() 函数中的错误时,异常不会冒泡,但是当我更深一层地使用此模式时,冒泡似乎起作用了。

您可以使用简单的 MCVE 轻松检查它是否正常工作:

def deeply_nested():
    raise ValueError("foo")

def nested():
    return deeply_nested()

def firstline():
    return nested()

def main():
    try:
        firstline()
    except ValueError as e:
        print("got {}".format(e))
    else:
        print("you will not see me")

if __name__ == "__main__":
    main()
Run Code Online (Sandbox Code Playgroud)

看来提供 Python 环境的软件以某种错误的方式处理主插件文件。看来我得检查一下 MySQL Workbench 的人了

呃...即使是嵌入的,机制期望仍然应该按预期工作 - 至少对于依赖于你的函数的调用堆栈部分main(无法告诉调用堆栈上部发生了什么)。但考虑到 MySQL 处理错误的方式(静默截断你的数据怎么样?),如果他们侵入运行时以静默传递插件代码中的任何错误,我不会感到特别惊讶 xD