I think that the solution can only be inaccurate due to the lack of static typing rules.
I am not aware of any tool that checks exceptions, but you can come up with your own tool that suits your needs (a good chance to play around with static analysis a bit).
As a first attempt, you can write a function that builds the AST, finds all the Raise nodes, and then tries to figure out common patterns for throwing exceptions (for example, calling the constructor directly)
Let x be the following program:
x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
Create an AST using the compiler package:
tree = compiler.parse(x)
Then define the Raise class:
class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
And go through the Raise build nodes:
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), ]
You can continue by resolving characters using compiler character tables, analyzing data dependencies, etc. Or you can simply deduce that CallFunc(Name('IOError'), ...) "necessarily means raising IOError ", which is quite normal for quick practical results :)