Думаю, решение могло быть неточным только из-за отсутствия правил статической типизации.
Мне неизвестен какой-то инструмент, который проверяет исключения, но вы могли бы придумать свой собственный инструмент, соответствующий вашим потребностям (хороший шанс немного поиграть со статическим анализом).
В качестве первой попытки вы могли бы написать функцию, которая строит AST, находит все Raise
узлы, а затем пытается выяснить общие шаблоны создания исключений (например, вызов конструктора напрямую)
Пусть x
будет следующая программа:
x = '''\
if f(x):
raise IOError(errno.ENOENT, 'not found')
else:
e = g(x)
raise e
'''
Соберите AST с помощью compiler
пакета:
tree = compiler.parse(x)
Затем определите Raise
класс посетителя:
class RaiseVisitor(object):
def __init__(self):
self.nodes = []
def visitRaise(self, n):
self.nodes.append(n)
И пройдемся по собирающим Raise
узлам AST :
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
», что вполне нормально для быстрых практических результатов :)
raise
строки, а не толькоBaseException
подклассы. Поэтому, если вы вызываете библиотечный код, который находится вне вашего контроля, этого дажеexcept Exception
недостаточно, поскольку он не перехватывает исключения строк. Как отмечали другие, здесь вы лаете не на то дерево.