Myślę, że rozwiązanie może być nieprecyzyjne tylko z powodu braku statycznych reguł pisania.
Nie znam jakiegoś narzędzia, które sprawdza wyjątki, ale możesz wymyślić własne narzędzie pasujące do twoich potrzeb (dobra okazja, aby pobawić się trochę analizą statyczną).
Jako pierwszą próbę możesz napisać funkcję, która buduje AST, wyszukuje wszystkie Raisewęzły, a następnie próbuje znaleźć typowe wzorce zgłaszania wyjątków (np. Bezpośrednie wywołanie konstruktora)
Niech xbędzie następujący program:
x = '''\
if f(x):
raise IOError(errno.ENOENT, 'not found')
else:
e = g(x)
raise e
'''
Zbuduj AST za pomocą compilerpakietu:
tree = compiler.parse(x)
Następnie zdefiniuj Raiseklasę gości:
class RaiseVisitor(object):
def __init__(self):
self.nodes = []
def visitRaise(self, n):
self.nodes.append(n)
I przejdź przez Raisewęzły zbierające 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),
]
Możesz kontynuować, rozwiązując symbole za pomocą tabel symboli kompilatora, analizując zależności danych itp. Możesz też po prostu wywnioskować, że CallFunc(Name('IOError'), ...)„zdecydowanie powinno oznaczać podniesienie IOError”, co jest całkiem OK dla szybkich praktycznych rezultatów :)
raiseużywać łańcuchów, a nie tylkoBaseExceptionpodklas. Więc jeśli wywołujesz kod biblioteki, który jest poza twoją kontrolą, nawetexcept Exceptionnie jest wystarczający, ponieważ nie przechwytuje wyjątków ciągów. Jak zauważyli inni, szczekasz tutaj na niewłaściwe drzewo.