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

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

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

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

Точки ответа получают те, кто может связать меня с какой-либо соответствующей документацией, которую я каким-то образом упустил из официальных документов, или предоставить надежный способ точно определить, какие исключения генерируются какими функциями и почему. 1016 *

Спасибо!

ОБНОВЛЕНИЕ : Из полученных ответов видно, что на самом деле не существует 100% прямого способа выяснить, какие ошибки вызываются определенными функциями. С метапрограммированием кажется, что я могу выяснить простые случаи и перечислить некоторые исключения, но это не особенно полезный или удобный метод.

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

Ответы [ 4 ]

12 голосов
/ 16 мая 2010

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

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

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

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

4 голосов
/ 16 мая 2010

В 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.
3 голосов
/ 16 мая 2010

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

Чтобы сначала найти, какие типы исключений определяет модуль, просто напишите простой скрипт, который просматривает каждый объект в словаре модуля 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
2 голосов
/ 16 мая 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...