Python: как я могу узнать, какие исключения могут быть вызваны из вызова метода
есть ли способ узнать (во время кодирования), какие исключения следует ожидать при выполнении кода python?
Я в конечном итоге ловлю базовый класс исключений 90% времени, так как я не знаю, какой тип исключения может быть брошен(и не говорите мне читать документацию. много раз исключение может быть распространено из глубины. и много раз документация не обновляется или исправить). Есть ли какой-то инструмент, чтобы проверить это ? (например, читая код python и libs)?
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Если у утки нет шарлатана, aTypeErrorесли у утки есть шарлатан, но он не вызывается. Вы даже не представляете, чтоduck.quack()может поднять, хотя, может быть, даже aDuckErrorили что-тотеперь предположим, у вас есть такой код
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