Python:我怎么知道方法调用可能抛出哪些异常

Gab*_*iMe 77 python exception

有没有办法知道(在编码时)执行python代码时会遇到哪些异常?我最终会在90%的时间内捕获基本Exception类,因为我不知道可能抛出哪种异常类型(并且不要告诉我阅读文档.很多时候,异常可以从深层传播.文档未更新或更正的时间).有什么工具可以检查吗?(比如通过阅读python代码和库)?

P S*_*ved 24

您应该只捕获您将处理的异常.

通过具体类型捕获所有异常是无稽之谈.您应该捕获可以并且将要处理的特定异常.对于其他异常,您可以编写一个捕获"基本异常"的通用catch,记录它(使用str()函数)并终止您的程序(或者在崩溃的情况下执行其他适当的操作).

如果您真的要处理所有异常,并且确定它们都不是致命的(例如,如果您在某种沙盒环境中运行代码),那么捕获泛型BaseException的方法符合您的目标.

您可能也对语言异常参考感兴趣,而不是您正在使用的库的参考.

如果库引用真的很差并且在捕获系统时它没有重新抛出自己的异常,唯一有用的方法是运行测试(可能将它添加到测试套件中,因为如果某些内容没有记录,它可能会改变!) .删除对您的代码至关重要的文件,并检查抛出的异常.提供过多数据并检查它产生的错误.

无论如何你都必须运行测试,因为即使存在通过源代码获取异常的方法,它也不会让你知道如何处理任何这些.也许您应该显示错误消息"找不到文件needful.txt!" 什么时候赶上IndexError?只有测试可以告诉.

  • 当然,但如果他不知道可能抛出什么,怎么能决定他应该处理哪些例外? (19认同)

And*_*ikh 22

我想解决方案可能只是不精确,因为缺乏静态类型规则.

我不知道有一些检查异常的工具,但你可以提出自己的工具来满足你的需求(很有可能在静态分析中玩一点).

作为第一次尝试,您可以编写一个构建AST的函数,查找所有Raise节点,然后尝试找出引发异常的常见模式(例如直接调用构造函数)

让我们x成为以下计划:

x = '''\
if f(x):
    raise IOError(errno.ENOENT, 'not found')
else:
    e = g(x)
    raise e
'''
Run Code Online (Sandbox Code Playgroud)

使用compiler包构建AST :

tree = compiler.parse(x)
Run Code Online (Sandbox Code Playgroud)

然后定义一个Raise访客类:

class RaiseVisitor(object):
    def __init__(self):
        self.nodes = []
    def visitRaise(self, n):
        self.nodes.append(n)
Run Code Online (Sandbox Code Playgroud)

并走AST收集Raise节点:

v = RaiseVisitor()
compiler.walk(tree, v)

>>> print v.nodes
[
    Raise(
        CallFunc(
            Name('IOError'),
            [Getattr(Name('errno'), 'ENOENT'), Const('not found')],
            None, None),
        None, None),
    Raise(Name('e'), None, None),
]
Run Code Online (Sandbox Code Playgroud)

您可以继续使用编译器符号表解析符号,分析数据依赖性等.或者您可以推断,CallFunc(Name('IOError'), ...)"应该绝对意味着提升IOError",这对于快速实际结果来说是相当不错的:)

  • 顺便说一句,你可以只需`find/path/to/library -name'*.py'| grep'raise'`得到类似的结果:) (7认同)

Joh*_*ooy 12

解决这个问题的正确工具是单元测试.如果你有真实代码引发的异常,单元测试不会引发,那么你需要更多的单元测试.

考虑一下

def f(duck):
    try:
        duck.quack()
    except ??? could be anything
Run Code Online (Sandbox Code Playgroud)

鸭子可以是任何物体

显然你可以有一个AttributeError如果鸭子没有嘎嘎,TypeError如果鸭子有一个庸医但它不可驯服.你不知道会发生什么duck.quack(),甚至可能是DuckError什么

现在假设你有这样的代码

arr[i] = get_something_from_database()
Run Code Online (Sandbox Code Playgroud)

如果它引发了IndexError你不知道它是来自arr [i]还是来自数据库函数的深处.通常情况下,异常发生的地方并不重要,而是出现了问题而你想要发生的事情并没有发生.

一个方便的技术是捕获并可能像这样重新加入异常

except Exception as e
    #inspect e, decide what to do
    raise
Run Code Online (Sandbox Code Playgroud)

  • 我不认为编写单元测试是答案。问题是“我如何找出预期的异常”,编写单元测试无助于您找到这一点。事实上,为了编写单元测试,您已经必须知道会出现哪些异常,因此为了编写正确的单元测试,您还必须回答原始问题。 (4认同)
  • 您也可以选择在某处记录异常然后重新加载 (2认同)

Kur*_*urt 5

我在使用 socket 时遇到了这个问题,我想找出我会遇到的所有错误情况(因此,与其试图创建错误并找出是什么 socket,我只想要一个简洁的列表)。最终,我最终将“/usr/lib64/python2.4/test/test_socket.py”用于“raise”:

$ grep raise test_socket.py
Any exceptions raised by the clients during their tests
        raise TypeError, "test_func must be a callable function"
    raise NotImplementedError, "clientSetUp must be implemented."
    def raise_error(*args, **kwargs):
        raise socket.error
    def raise_herror(*args, **kwargs):
        raise socket.herror
    def raise_gaierror(*args, **kwargs):
        raise socket.gaierror
    self.failUnlessRaises(socket.error, raise_error,
    self.failUnlessRaises(socket.error, raise_herror,
    self.failUnlessRaises(socket.error, raise_gaierror,
        raise socket.error
    # Check that setting it to an invalid value raises ValueError
    # Check that setting it to an invalid type raises TypeError
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,
Run Code Online (Sandbox Code Playgroud)

这是一个非常简洁的错误列表。当然,这仅适用于个案,并取决于测试是否准确(通常是这样)。否则,您几乎需要捕获所有异常,记录它们并剖析它们并弄清楚如何处理它们(通过单元测试不会很难)。

  • 这加强了我的论点,即 Python 中的异常处理是非常有问题的,如果我们需要使用 grep 或源分析器来处理如此基本的事情(例如在 Java 中从第一天就存在。有时冗长是一件好事。Java 是冗长的但至少没有令人讨厌的惊喜) (4认同)

vir*_*tor 5

到目前为止,没有人解释,为什么您不能拥有完整的100%正确的例外列表,所以我认为值得一提。原因之一是一流的功能。假设您有一个像这样的函数:

def apl(f,arg):
   return f(arg)
Run Code Online (Sandbox Code Playgroud)

现在apl可以引发任何f引发的异常。尽管在核心库中没有很多类似的功能,但是使用自定义过滤器,映射,归约等使用列表理解的任何内容都会受到影响。

文档和源代码分析器是这里唯一的“大量”信息源。只要记住他们不能做什么。