多个调用级别的异常处理

问题描述 投票:0回答:2

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

我基本上来自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

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!")

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

问题:如何在Python中做得最好?我是否真的必须为每个被调用的方法重复try-except

BTW:我使用的是Python 2.6.x,由于绑定到提供解释器的MySQL Workbench(Python 3在他们的升级列表中),我无法升级。

python exception-handling try-except
2个回答
2
投票

如果你没有捕获异常,它会使调用堆栈冒泡,直到有人这样做。如果没有人捕获它,运行时将获取它并死于异常错误消息和完整的回溯。 IOW,你不必在任何地方明确地捕捉并重新引用你的例外 - 这实际上会破坏了例外的全部意义。实际上,尽管主要用于错误/意外情况,但异常首先是一个控制流工具,它允许突破正常的执行流程并将控制(和一些信息)传递给调用堆栈中的任意位置。

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

首先,您应该定义自己的特定异常类,而不是使用内置的ValueError(如果它对您有意义,您可以继承它),因此您确定只能捕获您期望的确切异常(很多层) “在”你自己的代码下面会引发一个你没想到的ValueError。

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

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

elif AnnotationUtil.is_embeddable_table(table) and AnnotationUtil.is_secondary_table(table):
    # ...
elif AnnotationUtil.is_embeddable_table(table):
    # ...
elif AnnotationUtil.is_secondary_table(table):
    # ...

鉴于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()

似乎提供Python env的软件以某种方式以错误的方式处理主插件文件。看起来我将不得不检查MySQL Workbench的人

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


1
投票

It is fine for errors to bubble up

Python的异常未经检查,这意味着您没有义务声明或处理它们。即使您知道可能会引发某些事情,但只有在您打算对其进行操作时才会发现错误。有异常透明的图层是很好的,它可以优雅地作为异常气泡通过它们中止:

def logged_get(map: dict, key: str):
    result = map[key]  # this may raise, but there is no state to corrupt
    # the following is not meaningful if an exception occurred
    # it is fine for it to be skipped by the exception bubbling up
    print(map, '[%s]' % key, '=>', result)
    return result

在这种情况下,logged_get将简单地转发由查找引发的任何KeyError(和其他)。如果外部调用者知道如何处理错误,它可以这样做。

所以,就像你一样打电话给self.create_collection_embeddable_class_stub

It is fine for errors to kill the application

即使没有处理错误,解释器也会这样做。你得到一个堆栈跟踪,显示出错的地方和位置。这种致命错误“只有在有错误时才会发生”可以“安全地”冒泡以显示出错的地方。

实际上,退出解释器和断言也使用这种机制。

>>> assert 2 < 1, "This should never happen"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError: This should never happen

对于许多服务,您甚至可以在部署中使用它 - 例如,systemd会记录Linux系统服务。如果担心安全性,或者用户无法处理错误,则只尝试抑制外部错误。

It is fine to use precise errors

由于未检查异常,您可以使用任意多个,而无需过度训练API。这允许使用指示不同级别问题的自定义错误:

class DBProblem(Exception):
    """Something is wrong about our DB..."""

class DBEntryInconsistent(DBProblem):
    """A single entry is broken"""

class DBInconsistent(DBProblem):
    """The entire DB is foobar!"""

除非您的用例实际上符合其含义,否则通常最好不要重复使用内置错误。这允许在需要时精确处理错误:

try:
    gen.generate_classes(catalog)
except DBEntryInconsistent:
    logger.error("aborting due to corrupted entry")
    sys.exit(1)
except DBInconsistent as err:
    logger.error("aborting due to corrupted DB")
    Utility.inform_db_support(err)
    sys.exit(1)
# do not handle ValueError, KeyError, MemoryError, ...
# they will show up as a stack trace
© www.soinside.com 2019 - 2024. All rights reserved.