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

时间:2009-10-19 21:43:37

标签: python exception

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

7 个答案:

答案 0 :(得分:23)

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

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

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

您可能也对language exception reference感兴趣,而不是您正在使用的库的参考。

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

无论如何你都必须运行测试,因为即使如果存在通过源代码获取异常的方法,它也不会让你知道如何处理任何。也许您应该显示错误消息“找不到文件needful.txt!”当你抓住IndexError时?只有测试可以说明。

答案 1 :(得分:20)

我认为由于缺乏静态类型规则,解决方案可能只是不精确。

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

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

x成为以下程序:

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

使用compiler包构建AST:

tree = compiler.parse(x)

然后定义Raise访客类:

class RaiseVisitor(object):
    def __init__(self):
        self.nodes = []
    def visitRaise(self, n):
        self.nodes.append(n)

走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),
]

您可以继续使用编译器符号表解析符号,分析数据依赖关系等。或者您可以推断,CallFunc(Name('IOError'), ...)“应该绝对意味着提升IOError”,这对于快速来说是非常好的实际结果:))

答案 2 :(得分:12)

解决此问题的正确工具是单元测试。如果您通过实际代码提出异常,单元测试不会引发,那么您需要更多的单元测试。

考虑一下

def f(duck):
    try:
        duck.quack()
    except ??? could be anything

duck可以是任何对象

显然,如果鸭子没有嘎嘎,你可以拥有AttributeError,如果鸭子有庸医但是不可驯服,你可以TypeError。您不知道duck.quack()可能会提出什么,甚至可能是DuckError或其他什么

现在假设你有这样的代码

arr[i] = get_something_from_database()

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

一种方便的技术是捕捉并可能像这样重新加载异常

except Exception as e
    #inspect e, decide what to do
    raise

答案 3 :(得分:5)

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

def apl(f,arg):
   return f(arg)

现在apl可以引发f引发的任何异常。虽然核心库中的功能不多,但使用自定义过滤器,map,reduce等列表理解的任何功能都会受到影响。

文档和源分析器是这里唯一的“严肃”信息来源。请记住他们不能做的事情。

答案 4 :(得分:3)

我在使用套接字时碰到了这个,我想找出我要运行的所有错误条件(所以不要试图创建错误并找出我想要简洁列表的套接字)。最后,我最终选择“/usr/lib64/python2.4/test/test_socket.py”作为“加注”:

$ 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,

这是一个非常简洁的错误列表。当然,这只能根据具体情况而定,并取决于测试的准确性(通常是这些测试)。否则你需要几乎捕获所有异常,记录它们并解剖它们并弄清楚如何处理它们(单元测试不会很困难)。

答案 5 :(得分:1)

通常,您只需要在几行代码中捕获异常。您不希望将整个main函数放入try except子句中。对于每一行,你现在应该(或者能够容易地检查)可能会引发什么样的异常。

文档详尽无遗list of built-in exceptions。不要试图除了那些你没想到的异常,它们可能会在调用代码中被处理/预期。

编辑:可能会抛出的内容显然取决于您正在做什么!访问序列的随机元素:IndexError,字典的随机元素:KeyError等。

尝试在IDLE中运行这几行并导致异常。但是,单元测试自然会是一个更好的解决方案。

答案 6 :(得分:0)

我发现有两种方法可以提供信息。第一个,运行iPython中的指令,它将显示异常类型。

n = 2
str = 'me '
str + 2
TypeError: unsupported operand type(s) for +: 'int' and 'str'

在第二种方式中,我们决心抓住太多并改进它。在代码中包含try表达式并捕获除之外的 。打印足够的数据以了解抛出的异常。抛出异常会通过添加更精确的except子句来改进代码。当您认为已缓存所有相关异常时,请删除全包异常。无论如何都要做好事,因为它会吞下编程错误。

try:
   so something
except Exception as err:
   print "Some message"
   print err.__class__
   print err
   exit(1)