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 Raise
węzły, a następnie próbuje znaleźć typowe wzorce zgłaszania wyjątków (np. Bezpośrednie wywołanie konstruktora)
Niech x
bę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ą compiler
pakietu:
tree = compiler.parse(x)
Następnie zdefiniuj Raise
klasę gości:
class RaiseVisitor(object):
def __init__(self):
self.nodes = []
def visitRaise(self, n):
self.nodes.append(n)
I przejdź przez Raise
wę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 :)
raise
używać łańcuchów, a nie tylkoBaseException
podklas. Więc jeśli wywołujesz kod biblioteki, który jest poza twoją kontrolą, nawetexcept Exception
nie jest wystarczający, ponieważ nie przechwytuje wyjątków ciągów. Jak zauważyli inni, szczekasz tutaj na niewłaściwe drzewo.