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