Как я могу найти список всех исключений, которые данная функция библиотеки выбрасывает в Python?

Извините за длинный заголовок, но он кажется наиболее наглядным для моего вопроса.

В принципе, мне сложно найти информацию об исключениях в официальной документации на python. Например, в одной программе, которую я сейчас пишу, я использую функцию перемещения libyl:

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, и что эта информация будет включена в официальную документацию. До тех пор я думаю, что я просто позволю этим исключениям пройти и пропустить ошибки для моих пользователей, поскольку кажется, что это самая разумная вещь.

Ответ 1

Чтобы усилить Messa, поймите, что вы ожидаете, это режимы отказа, из которых вы знаете, как восстановить. Ian Bicking написал статью статью, в которой рассматриваются некоторые общие принципы, как это делает Эли Бендерски отметить.

Проблема с образцом кода заключается в том, что он обрабатывает ошибки не, просто убирая их и отбрасывая. Ваш код не "знает", что делать с NameError, и не так много нужно делать, кроме как пропустить его, посмотрите на Bicking re-raise, если вы чувствуете, что должны добавить детали.

IOError и OSError являются разумно "ожидаемыми" для shutil.move, но не обязательно обрабатываются. И вызывающая функция вашей функции хотела, чтобы она перемещала файл и сама могла сломаться, если этот "контракт", о котором пишет Эли, сломан.

Поймайте то, что вы можете исправить, украсить и повторно поднять то, что вы ожидаете, но не можете исправить, и позвольте собеседнику разобраться с тем, чего вы не ожидали, даже если код, который "имеет дело", находится на семи уровнях вверх по стеку в main.

Ответ 2

У 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

Это также означает, что при определении своих собственных исключений вы должны основывать их на стандартном стандарте, а не на исключении.

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

Ответ 3

Да, вы можете (для простых случаев), но вам нужно немного метапрограммирования. Как и в других ответах, функция не заявляет, что выбрасывает конкретный тип ошибки, поэтому вам нужно посмотреть на модуль и посмотреть, какие типы исключений он определяет, или какие типы исключений он вызывает. Вы можете попытаться просмотреть документацию или использовать API Python для этого.

Чтобы найти типы исключений, которые определяет модуль, просто напишите простой script, чтобы пройти через каждый объект в словаре модулей module.__dict__ и посмотреть, заканчивается ли оно словом "Ошибка" или если он является подклассом Исключение:

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 определяет типы ошибок Error и WindowsError и вызывает типы исключений OSError и Error. Если мы хотим быть немного более полными, мы могли бы написать другой метод, чтобы проверить каждое предложение except, чтобы также видеть, какие дескрипторы shutil обрабатываются.

Здесь код для прохождения по AST, он просто использует интерфейс compiler.visitor для создания ходунки, который реализует "шаблон посетителя" из книги "Банда четырех":

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

Ответ 4

Поскольку эти операции обычно используют функции libc и вызовы операционной системы, в основном вы получаете IOError или OSError с номером errno; эти ошибки перечислены в man-страницах этих вызовов libc/OS.

Я знаю, что это, возможно, не полный ответ, было бы хорошо иметь все исключения, перечисленные в документации...