Python: How can I know which exceptions might be thrown from a method call

You should only catch exceptions that you will handle.

Catching all exceptions by their concrete types is nonsense. You should catch specific exceptions you can and will handle. For other exceptions, you may write a generic catch that catches "base Exception", logs it (use str() function) and terminates your program (or does something else that's appropriate in a crashy situation).

If you really gonna handle all exceptions and are sure none of them are fatal (for example, if you're running the code in some kind of a sandboxed environment), then your approach of catching generic BaseException fits your aims.

You might be also interested in language exception reference, not a reference for the library you're using.

If the library reference is really poor and it doesn't re-throw its own exceptions when catching system ones, the only useful approach is to run tests (maybe add it to test suite, because if something is undocumented, it may change!). Delete a file crucial for your code and check what exception is being thrown. Supply too much data and check what error it yields.

You will have to run tests anyway, since, even if the method of getting the exceptions by source code existed, it wouldn't give you any idea how you should handle any of those. Maybe you should be showing error message "File needful.txt is not found!" when you catch IndexError? Only test can tell.


I guess a solution could be only imprecise because of lack of static typing rules.

I'm not aware of some tool that checks exceptions, but you could come up with your own tool matching your needs (a good chance to play a little with static analysis).

As a first attempt, you could write a function that builds an AST, finds all Raise nodes, and then tries to figure out common patterns of raising exceptions (e. g. calling a constructor directly)

Let x be the following program:

x = '''\
if f(x):
    raise IOError(errno.ENOENT, 'not found')
else:
    e = g(x)
    raise e
'''

Build the AST using the compiler package:

tree = compiler.parse(x)

Then define a Raise visitor class:

class RaiseVisitor(object):
    def __init__(self):
        self.nodes = []
    def visitRaise(self, n):
        self.nodes.append(n)

And walk the AST collecting Raise 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 may continue by resolving symbols using compiler symbol tables, analyzing data dependencies, etc. Or you may just deduce, that CallFunc(Name('IOError'), ...) "should definitely mean raising IOError", which is quite OK for quick practical results :)


The correct tool to solve this problem is unittests. If you are having exceptions raised by real code that the unittests do not raise, then you need more unittests.

Consider this

def f(duck):
    try:
        duck.quack()
    except ??? could be anything

duck can be any object

Obviously you can have an AttributeError if duck has no quack, a TypeError if duck has a quack but it is not callable. You have no idea what duck.quack() might raise though, maybe even a DuckError or something

Now supposing you have code like this

arr[i] = get_something_from_database()

If it raises an IndexError you don't know whether it has come from arr[i] or from deep inside the database function. usually it doesn't matter so much where the exception occurred, rather that something went wrong and what you wanted to happen didn't happen.

A handy technique is to catch and maybe reraise the exception like this

except Exception as e
    #inspect e, decide what to do
    raise