Python: how can I find out what exceptions can be thrown from a method call?

Is there any way to find out (during coding) what exceptions to expect when python code is executed? I will eventually catch the Exception base class 90% of the time, since I don’t know what type of exception can be thrown (and don’t tell me to read the documentation. Many times the exception can be propagated from the depth. Once the documentation is not updated or not adjusted ) Is there any tool to verify this? (e.g. by reading python and libs code)?

+60
python exception
Oct 19 '09 at 21:43
source share
7 answers

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 :)

+18
Oct 19 '09 at 10:15
source share

You must handle the exceptions that you will handle.

Capturing all exceptions for their specific types is nonsense. You must catch certain exceptions that you can and will handle. For other exceptions, you can write a generic catch that catches the “base exception”, logs it (use the str() function), and terminates your program (or does something else that works in an emergency).

If you are really going to handle all exceptions and are sure that none of them is fatal (for example, if you use the code in any isolated environment), then your approach to detecting a general BaseException is in line with your goals.

You may also be interested in a link to a language exception , not a link to the library you are using.

If the link to the library is really unsatisfactory and does not throw your own exceptions when catching system ones, the only useful approach is to run the tests (maybe add it to the test suite, because if something is undocumented, it can change!). Delete the file that is critical to your code and check which exception is thrown. Provide too much data and check what error it gives.

In any case, you will have to run the tests, because even if there is a method for getting exceptions from the source code, it will not give you an idea of ​​how you should handle any of these . Perhaps you should show the error message "file needful.txt not found!" when do you catch an IndexError ? Only a test can tell.

+23
Oct. 19 '09 at 21:51
source share

The right tool to solve this problem is unittests. If you have exceptions caused by real code that unittests does not raise, 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 the duck does not have a charlatan, TypeError if the duck has a charlatan, but it cannot be called. You have no idea what duck.quack() can raise, maybe even a DuckError or something

Now suppose you have code like this

 arr[i] = get_something_from_database() 

If it raises an IndexError , you don't know if it came from arr [i] or from the depth of the database function. usually it doesn’t really matter where the exception occurred, and something went wrong, and what you wanted did not happen.

A convenient technique is to catch and possibly re-raise exceptions like this.

 except Exception as e #inspect e, decide what to do raise 
+11
Oct 19 '09 at 21:56
source share

So far, no one has explained why you do not have a complete, 100% correct list of exceptions, so I thought it was worth commenting. One reason is the first-class feature. Say you have a function like this:

 def apl(f,arg): return f(arg) 

Now apl can raise any exception that f raises. Although there aren’t many such functions in the main library, everything that uses list comprehension using custom filters, display, reduction, etc. is not affected.

Documentation and source analyzers are the only "serious" sources of information here. Just keep in mind that they cannot do it.

+4
Dec 12 '09 at 0:02
source share

I came across this when using a socket, I wanted to find out all the errors that I could work into (instead of trying to create errors and find out which socket I need only for a short list). I ended up grep'ing "/usr/lib64/python2.4/test/test_socket.py" to "raise":

 $ grep raise test_socket.py Any exceptions raised by the clients during their tests raise TypeError, "test_func must be a callable function" raise NotImplementedError, "clientSetUp must be implemented." def raise_error(*args, **kwargs): raise socket.error def raise_herror(*args, **kwargs): raise socket.herror def raise_gaierror(*args, **kwargs): raise socket.gaierror self.failUnlessRaises(socket.error, raise_error, self.failUnlessRaises(socket.error, raise_herror, self.failUnlessRaises(socket.error, raise_gaierror, raise socket.error # Check that setting it to an invalid value raises ValueError # Check that setting it to an invalid type raises TypeError def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout, def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout, 

This is a fairly short list of errors. Now, of course, this only works in each case and depends on how accurate the tests are (usually they are). Otherwise, you need to pretty much catch all the exceptions, register them and analyze them and figure out how to handle them (which will not be difficult with a single test).

+3
Oct. 20 '09 at 0:26
source share

usually you only need to catch the exception around a few lines of code. You would not want to put all of your main function in a try except clause. for each row you should always (or you can easily check) which exception can be raised.

docs has an exhaustive list of built-in exceptions . do not try to exclude those exceptions that you do not expect, they can be handled / expected in the calling code.

edit : what can be selected depends obviously on what you do! access to a random element of a sequence: IndexError , random element dict: KeyError , etc.

Just try running these few lines in IDLE and throw an exception. But unittest would be a better solution, of course.

+1
Oct 19 '09 at 21:56
source share

There are two ways that I found informative. First, run the instructions in iPython, which will display the type of exception.

 n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str' 

In the second case, we agree to catch and improve it too much. Include a try statement in your code and catch an exception other than Exception like err . Print enough data to find out which exception was thrown. As exceptions are thrown, improve your code by adding a more specific exception. When you feel that you have cached all relevant exceptions, delete all inclusive. In any case, good, because it swallows programming errors.

 try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1) 
0
Mar 26 '14 at 2:56
source share