Catching a (specific) generic Exception raised by an external library - python

I am using an external library which raises a generic Exception in one of it's modules under certain circumstances:
if some_condition:
raise Exception('DSSP failed to produce an output')
This only occurs in a few edge-cases, but is still something I want to catch and handle.
So, other than cursing the original coders for raising a generic Exception, what are my options here? I only want to catch and handle this particular line in the original code. What is the most Pythonic way of dealing with this?

Catch Exception and reraise if it's not the exception you're looking for:
try:
do_thing()
except Exception as e:
if type(e) is not Exception:
# Not what we're looking for. Let it pass.
raise
handle(e)
You can add some other checks if you want to really make sure it's the exception you're looking for, such as examining the exception's message.

Related

In python 3, re-raise an error with a shorter traceback

I'm trying to create a try-except block that re-raises an arbitrary exception, but only includes the final block in the traceback stack.
Something like this:
import traceback
def my_func(shorten_tracebacks=True):
try:
# Do stuff here
except Exception as e:
if shorten_tracebacks:
raise TheSameTypeOfError, e, traceback.print_exc(limit=1)
else:
raise(e)
Is there a preferred way to do this?
In case it matters, I'm doing this for convenience in debugging certain APIs that are often used in jupyter notebooks---they tend to generate really long stack traces where only the last block is informative. This forces the user to scroll a lot. If you don't want to shorten the traceback, you can always set shorten_tracebacks=False
My preference is to create a new exception without a context (if an exception has a context python will print both the exception and the exception which caused that exception, and the exception which caused that exception, and so on...)
try:
...
except:
raise Exception("Can't ramistat the foo, is foo installed?") from None
Some best practices:
Include relevant debugging information in the exception message.
Use a custom exception type, so callers can catch the new exception.
Catch only the specific error types you're expecting, to let unexpected errors fall through with the extended traceback.
The downside to this approach is that if your exception catching is overly broad, you can end up suppressing useful context which is important to debugging the exception. An alternate pattern might look like this:
try:
...
except Exception as e:
if "ramistat not found" in e.message:
# The ramistat is missing. This is a common kind of error.
# Create a more helpful and shorter message
raise Exception("Can't ramistat the foo, is foo installed?") from None
else:
# Some other kind of problem
raise e
Essentially, you check the exception, and replace it with a custom message if it's a kind of error you know how to deal with. Otherwise, you re-raise the original exception, and let the user figure out what to do.

Raising an exception, Handling it. Only to raise it again; Python

I'm reading the Python Tutorial.
I came across a part that says:
'''
If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:
'''
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
I don't understand why would one raise an exception, handle it and then re-raise it??
If the programmer doesn't want to handle the exception they shouldn't use a try..except statement in the first place??
It helps if you don't think of exceptions as "mistakes" that have to be caught/corrected, and instead think of them as a way of communicating information.
Sometimes when you catch an exception it's expected, or you can find a way around it. Other times, the exception means you can't do the thing you were trying to do, and so you want to raise an exception to your own caller to let them know why it's not going to work out.
When you're in that situation, sometimes you'll catch the lower-level exception that thwarted your plans, and then raise your own exception (maybe with a more specific type) to communicate your own failure to the caller. Sometimes it might work just as well to simply raise the same exception back to the caller. In the situation where you just want the lower level exception to go all the way up to the caller, you might opt to just not catch it, but what if you want to log a message or clean up some piece of internal state before you go down in flames? That's where the except/raise pattern is useful.
begin_complicated_process()
try:
do_risky_thing()
except ShenanigansError:
log("Did someone say shenanigans?!")
unwind_complicated_process()
raise
complete_complicated_process()

How to raise exception within a try/except block properly

right now I have a problem where I want to raise a specific TypeError if there is one. However, what ends up happening is the interpreter sees the first error, and then in the middle of handling it it raises the other one as well saying "During handling of the above exception, another exception occurred:"
this is what I have
def function(dictionary)
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error")
I plug in the following into the shell:
function({1:'a', 2:3})
How can I approach this?
If you want to discard the exception context, you can explicitly discard it using from None, e.g.:
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error") from None
That said, it's usually best to leave the context in place; the only time you'll see it is if the exception is uncaught and the default logging occurs, or you try to log the exception (e.g. with logger.exception). That additional information is often useful, especially for extremely broad exception types like TypeError and ValueError (where you intend to catch specific known subtypes, and unexpectedly catch one caused in a completely different way).
To be clear, this only works on Python 3, but then, exception context chaining only exists on Python 3; on Python 2, the context is lost automatically.
Since you are raising the exception while handling it, the exception is sent back to the caller function.
If you just want to handle it and print the error and move on with rest of the execution, you can do sth like this
except TypeError as t:
print ("Error", t)

In Python, how do I inspect and then re-raise an exception while maintaining the original call stack?

I've got a situation where I'm catching a specific exception type, inspecting the exception's message to check if it's actually an exception I want to catch, and then re-raising the exception if not:
try:
# do something exception-prone
except FooException as e:
if e.message == 'Something I want to handle':
# handle the exception
else:
raise e
This works fine, with one problem. In the case I re-raise the exception, that exception now occurs at the line I re-raised it (i.e. at raise e), rather than at the location the exception originally occurred. This isn't ideal for debugging, where you want to know where the original exception happened.
Thus my question: is there any way to re-raise or otherwise "pass on" an exception after catching it while maintaining the original exception location?
NOTE: In case you are wondering what the actual situation is: I'm dynamically importing some modules using __import__. I'm catching ImportError to gracefully deal with the case that any of these modules do not exist. However, in the case that any of these modules themselves contain an import statement that raises ImportError, I want those "real" (from the point of view of my application) exceptions to be raised -- and at the original location as far as debugging tools are concerned.
Just do:
raise
instead of raise e. See the tutorial section on raising exceptions, and also the language reference on raise statements:
If no expressions are present, raise re-raises the last exception that was active in the current scope. If no exception is active in the current scope, a TypeError exception is raised indicating that this is an error (if running under IDLE, a Queue.Empty exception is raised instead).

Deciding which exceptions to catch in Python

Suppose that I am using a library X that specifies for example that exception.BaseError is the base class for all exceptions of X.
Now, there is another exception, say X.FooError, which of course inherits from exception.BaseError but is more generalized, let's say that it handles invalid input. Let's suppose there are many other such classes, inherting from BaseError but all for generalized cases.
X
|
BaseError
|
FooError
So I want to then check for invalid input. So which exception should I catch? Of course, catching each individual exception is not possible, so I catch the X.BaseError and then print an error message. Or I can catch the X.FooError specifically but then I miss out all the other error cases.
Is this the standard way of doing it -- catch the base exception? If yes, then why do the other exceptions exist? For the generalized case when we want to catch a specific exception?
Catch only the exceptions you can handle. If you can handle both the base exception and the derived exception then catch both. But make sure to put the derived exception first, since the first exception handler found that matches is the one used.
try:
X.foo()
except X.FooError:
pass
except X.BaseError:
pass
As usual, there is good advice in PEP-8, Style Guide for Python Code:
When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause.
There's lots more in there, but it's pointless me reproducing it here.
In this case, I would catch the specifics, were I to handle them differently to a BaseError and the BaseError for those that require more general handling. I would stop well short of catching the built-in Exception, however.
You catch a specfic exception by defining it in the except clause, thus:
try:
#do stuff
except X.FooError:
# handle the error
except (X.AnotherError, x.YetAnotherError), exc:
print 'I'm handling %s' % exc
Notice you can handle multiple exception classes by listing them in a tuple.

Categories