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