如何查找给定库函数在Python中引发的所有异常列表?

时间:2010-05-16 08:56:40

标签: python exception

对于长标题感到抱歉,但对我的问题似乎最具描述性。

基本上,我很难在官方python文档中找到异常信息。例如,在我正在编写的一个程序中,我正在使用shutil libary的移动函数:

from shutil import move
move('somefile.txt', '/tmp/somefile.txt')

只要我有对/ tmp /的写访问权限,有足够的磁盘空间,并且满足所有其他要求,那就可以了。

但是,在编写通用代码时,通常很难保证这些因素,因此通常会使用例外:

from shutil import move
try:
    move('somefile.txt', '/tmp/somefile.txt')
except:
    print 'Move failed for some reason.'

我想实际捕获相应的异常抛出而不只是捕获所有,但我根本找不到大多数python模块抛出的异常列表。有没有办法让我看看给定函数可以抛出哪些异常,为什么?这样我就可以为每个例外做出适当的案例,例如:

from shutil import move
try:
    move('somefile.txt', '/tmp/somefile.txt')
except PermissionDenied:
    print 'No permission.'
except DestinationDoesNotExist:
    print "/tmp/ doesn't exist"
except NoDiskSpace:
    print 'No diskspace available.'

任何可以将我链接到我在某些官方文档中忽略的相关文档,或者提供一种确定的方法来确定哪些函数抛出哪些异常以及原因。 / p>

谢谢!

更新:从给出的答案看来,似乎没有100%直接的方法来确定特定功能引发的错误。使用元编程,似乎我可以找出简单的情况并列出一些例外,但这不是一个特别有用或方便的方法。

我想最终会有一些标准来定义每个python函数引发的异常,并且这些信息将包含在官方文档中。在那之前,我想我会允许这些异常通过并为我的用户输出错误,因为它似乎是最理智的事情。

4 个答案:

答案 0 :(得分:12)

为了放大Messa,抓住你所期望的失败模式,你知道如何恢复。 Ian Bicking写了an article,它解决了一些总体原则,就像Eli Bendersky的note一样。

示例代码的问题在于处理错误,只是美化它们并丢弃它们。你的代码并不“知道”如何处理NameError,除了传递它之外没有什么应该做的,如果你认为必须添加细节,请查看Bicking的重新加注。

对于shutil.move,IOError和OSError是合理的“可预期的”,但不一定可以处理。你的函数的调用者希望它移动一个文件,如果Eli写的“契约”被破坏,它本身可能会破坏。

抓住你可以修复,装饰和重新提升你期望但无法解决的东西,并让调用者处理你没想到的东西,即使“处理”的代码是堆栈的七个级别在main

答案 1 :(得分:4)

Python目前没有一种机制来声明抛出哪些异常,这与(例如)Java不同。 (在Java中,你必须确切地定义哪些异常被抛出,如果你的一个实用程序方法需要抛出另一个异常,那么你需要将它添加到调用它的所有方法中,这很快就会枯燥!)

因此,如果您想要确切地发现任何给定的python位引发的异常,那么您需要检查文档和源代码。

然而,python有一个非常好的异常层次结构。

如果您研究下面的异常层次结构,您将看到要捕获的错误超类称为StandardError - 这应该捕获可能在正常操作中生成的所有错误。将错误转换为字符串将为用户提供关于出错的合理建议,因此我建议您上面的代码应该看起来像

from shutil import move
try:
    move('somefile.txt', '/tmp/somefile.txt')
except StandardError, e:
    print 'Move failed: %s' % e

异常层次结构

BaseException
|---Exception
|---|---StandardError
|---|---|---ArithmeticError
|---|---|---|---FloatingPointError
|---|---|---|---OverflowError
|---|---|---|---ZeroDivisionError
|---|---|---AssertionError
|---|---|---AttributeError
|---|---|---BufferError
|---|---|---EOFError
|---|---|---EnvironmentError
|---|---|---|---IOError
|---|---|---|---OSError
|---|---|---ImportError
|---|---|---LookupError
|---|---|---|---IndexError
|---|---|---|---KeyError
|---|---|---MemoryError
|---|---|---NameError
|---|---|---|---UnboundLocalError
|---|---|---ReferenceError
|---|---|---RuntimeError
|---|---|---|---NotImplementedError
|---|---|---SyntaxError
|---|---|---|---IndentationError
|---|---|---|---|---TabError
|---|---|---SystemError
|---|---|---TypeError
|---|---|---ValueError
|---|---|---|---UnicodeError
|---|---|---|---|---UnicodeDecodeError
|---|---|---|---|---UnicodeEncodeError
|---|---|---|---|---UnicodeTranslateError
|---|---StopIteration
|---|---Warning
|---|---|---BytesWarning
|---|---|---DeprecationWarning
|---|---|---FutureWarning
|---|---|---ImportWarning
|---|---|---PendingDeprecationWarning
|---|---|---RuntimeWarning
|---|---|---SyntaxWarning
|---|---|---UnicodeWarning
|---|---|---UserWarning
|---GeneratorExit
|---KeyboardInterrupt
|---SystemExit

这也意味着在定义自己的异常时,应该将它们基于StandardError而不是异常。

Base class for all standard Python exceptions that do not represent
interpreter exiting.

答案 2 :(得分:3)

是的,您可以(对于简单的情况),但您需要一些元编程。与其他答案一样,函数不会声明它会抛出特定的错误类型,因此您需要查看模块并查看它定义的异常类型或它引发的异常类型。您可以尝试浏览文档或利用Python API执行此操作。

要首先找到模块定义的异常类型,只需编写一个简单的脚本来遍历模块字典module.__dict__中的每个对象,看它是否以单词“Error”结尾,或者它是否为子类的子类例外:

def listexns(mod):
    """Saved as: http://gist.github.com/402861
    """
    module = __import__(mod)
    exns = []
    for name in module.__dict__:
        if (issubclass(module.__dict__[name], Exception) or
            name.endswith('Error')):
            exns.append(name)
    for name in exns:
        print '%s.%s is an exception type' % (str(mod), name)
    return

如果我按照shutils的例子运行此操作,我就明白了:

$ python listexn.py shutil
Looking for exception types in module: shutil
shutil.Error is an exception type
shutil.WindowsError is an exception type
$

它告诉您定义了哪些错误类型,但不会抛出哪些错误类型。为了实现后者,我们需要遍历Python解释器解析模块时生成的抽象语法树,并查找每个raise语句,然后保存引发的名称列表。这个代码有点长,所以首先我要说明输出:

$ python listexn-raised.py /usr/lib/python2.6/shutil.py
Looking for exception types in: /usr/lib/python2.6/shutil.py
/usr/lib/python2.6/shutil.py:OSError is an exception type
/usr/lib/python2.6/shutil.py:Error is an exception type
$ 

因此,现在我们知道shutil.py定义了错误类型ErrorWindowsError,并引发了异常类型OSErrorError。如果我们想要更完整,我们可以编写另一个方法来检查每个except子句,以查看shutil处理的异常。

以下是遍历AST的代码,它只是使用compiler.visitor接口来创建一个walker,它实现了Gang of Four书中的“访问者模式”:

class ExceptionFinder(visitor.ASTVisitor):
    """List all exceptions raised by a module. 
    Saved as: http://gist.github.com/402869
    """

    def __init__(self, filename):
        visitor.ASTVisitor.__init__(self)
        self.filename = filename
        self.exns = set()
        return

    def __visitName(self, node):
        """Should not be called by generic visit, otherwise every name
        will be reported as an exception type.
        """
        self.exns.add(node.name)
        return

    def __visitCallFunc(self, node):
        """Should not be called by generic visit, otherwise every name
        will be reported as an exception type.
        """
        self.__visitName(node.node)
        return

    def visitRaise(self, node):
        """Visit a raise statement.
        Cheat the default dispatcher.
        """
        if issubclass(node.expr1, compiler.ast.Name):
            self.__visitName(node.expr1)
        elif isinstance(node.expr1, compiler.ast.CallFunc):
            self.__visitCallFunc(node.expr1)
        return

答案 3 :(得分:2)

由于这些操作通常使用libc函数和操作系统调用,因此大多数情况下会得到带有 errno 编号的IOError或OSError;这些错误列在该libc / OS调用的手册页中。

我知道这可能不是一个完整的答案,最好在文档中列出所有例外......