繁体   English   中英

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

[英]How Can I Find a List of All Exceptions That a Given Library Function Throws in Python?

很抱歉这个长标题,但它似乎对我的问题最具描述性。

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

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.'

答案要点是谁可以将我链接到一些我在官方文档中忽略的相关文档,或者提供一种确定的方法来确定哪些函数抛出了哪些异常,以及为什么。

谢谢!

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

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

为了放大Messa,抓住你所期望的失败模式,你知道如何恢复。 Ian Bicking写了一篇文章 ,与Eli Bendersky的笔记一样,解决了一些总体原则。

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

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

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

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而不是Exception。

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM