Python: как я могу узнать, какие исключения могут быть вызваны из вызова метода



есть ли способ узнать (во время кодирования), какие исключения следует ожидать при выполнении кода python?
Я в конечном итоге ловлю базовый класс исключений 90% времени, так как я не знаю, какой тип исключения может быть брошен(и не говорите мне читать документацию. много раз исключение может быть распространено из глубины. и много раз документация не обновляется или исправить). Есть ли какой-то инструмент, чтобы проверить это ? (например, читая код python и libs)?

484   7  

7 ответов:

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

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

в качестве первой попытки вы можете написать функцию, которая строит AST, находит все Raise узлы, а затем пытается выяснить общие шаблоны создания исключений (например, вызов конструктора непосредственно)

пусть x быть следующая программа:

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

построить АСТ с помощью compiler пакет:

tree = compiler.parse(x)

затем определите a Raise класс посетителя:

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

и ходить по АСТ коллекционирования 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", который вполне нормально для быстрого практического результата:)

вы должны только ловить исключения, которые вы будете обрабатывать.

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

Если вы действительно собираетесь обрабатывать все исключения и уверены, что ни один из них не является фатальным (например, если вы запускаете код в какой-то изолированной среде), то ваш подход к ловле generic BaseException соответствует вашим целям.

вы также можете быть заинтересованы в ссылка на исключение языка, не Ссылка на библиотеку, которую вы используете.

Если ссылка на библиотеку действительно плохая, и она не повторяет свои собственные исключения при ловле системных, единственный полезный подход-запустить тесты (может быть, добавить его в тестовый набор, потому что если что-то недокументировано, это может измениться!). Удалите файл, имеющий решающее значение для вашего кода, и проверьте, какое исключение создается. Поставьте слишком много данных и проверьте, какую ошибку он дает.

вам все равно придется запускать тесты, так как, даже если бы метод получения исключений по исходному коду существовал, это не дало бы вам ни малейшего представления о том, как вы должны обрабатывать любой из этих. Может быть, вы должно отображаться сообщение об ошибке "файл необходим.txt не найден!- когда поймаешь IndexError? Только тест может сказать.

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

считайте это

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

утка может быть любым объектом

очевидно, вы можете иметь AttributeError Если у утки нет шарлатана, a TypeError если у утки есть шарлатан, но он не вызывается. Вы даже не представляете, что duck.quack() может поднять, хотя, может быть, даже a DuckError или что-то

теперь предположим, у вас есть такой код

arr[i] = get_something_from_database()

если он выдает IndexError вы не знаете, пришел ли он из arr[i] или из глубины функции базы данных. обычно не так важно, где произошло исключение, скорее, что что-то пошло не так, и то, что вы хотели, не произошло.

удобный метод, чтобы поймать и, возможно, reraise исключение, как это

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

никто не объяснил до сих пор, почему вы не можете иметь полное, 100% правильный список исключений, поэтому я подумал, что стоит прокомментировать. Одна из причин-это первоклассная функция. Предположим, что у вас есть такая функция:

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

Теперь apl может вызвать любое исключение, что f поднимает. Хотя в основной библиотеке не так много таких функций, все, что использует понимание списка с пользовательскими фильтрами, картой, уменьшением и т. д. есть пострадавшие.

В документация и анализаторы источников являются здесь единственными "серьезными" источниками информации. Просто имейте в виду, что они не могут сделать.

я столкнулся с этим при использовании сокета, я хотел узнать все условия ошибок, в которых я буду работать (поэтому вместо того, чтобы пытаться создавать ошибки и выяснять, какой сокет мне просто нужен краткий список). В конечном итоге я закончил grep'ING "/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,

Это довольно краткий список ошибок. Теперь, конечно, это работает только в каждом конкретном случае и зависит от точности тестов (которые они обычно являются). Иначе вам нужно в значительной степени поймать все исключения, зарегистрировать их и рассечь их и выяснить, как их обрабатывать (что с модульным тестированием не будет сложно).

как правило, вам нужно будет поймать исключение только вокруг нескольких строк кода. Вы не хотели бы поставить всю свою на try except предложения. для каждой строки вы всегда должны теперь (или можете легко проверить), какое исключение может быть вызвано.

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

edit: то, что может быть брошено, зависит от того, что вы делаете! доступ к случайному элементу последовательности:IndexError, случайный элемент диктатора:KeyError и т. д.

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

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

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

во втором случае мы соглашаемся на то, чтобы поймать слишком много и улучшить его. Включите выражение try в свой код и поймайте кроме исключения как err. Выведите достаточное количество данных, чтобы узнать, какое исключение было вызвано. По мере появления исключений улучшите свой код, добавив более точное предложение except. Когда вы чувствуете, что у вас есть кэшированные все соответствующие исключения удалить все включено один. Хорошая вещь, чтобы сделать в любом случае, потому что он глотает ошибки программирования.

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

Comments

    Ничего не найдено.