How to handle all but one exception?
try:
something
except <any Exception except for a NoChildException>:
# handling
Something like this, except without destroying the original traceback:
try:
something
except NoChildException:
raise NoChildException
except Exception:
# handling
The answer is to simply do a bare raise:
try:
...
except NoChildException:
# optionally, do some stuff here and then ...
raise
except Exception:
# handling
This will re-raise the last thrown exception, with original stack trace intact (even if it's been handled!).
New to Python ... but is not this a viable answer?
I use it and apparently works.... and is linear.
try:
something
except NoChildException:
assert True
except Exception:
# handling
E.g., I use this to get rid of (in certain situation useless) return exception FileExistsError from os.mkdir.
That is my code is:
try:
os.mkdir(dbFileDir, mode=0o700)
except FileExistsError:
assert True
and I simply accept as an abort to execution the fact that the dir is not somehow accessible.
I'd offer this as an improvement on the accepted answer.
try:
dosomestuff()
except MySpecialException:
ttype, value, traceback = sys.exc_info()
raise ttype, value, traceback
except Exception as e:
mse = convert_to_myspecialexception_with_local_context(e, context)
raise mse
This approach improves on the accepted answer by maintaining the original stacktrace when MySpecialException is caught, so when your top-level exception handler logs the exception you'll get a traceback that points to where the original exception was thrown.
You can do type checking on exceptions! Simply write
try:
...
except Exception as e:
if type(e) == NoChildException:
raise
It still includes the original stack trace.
I found a context in which catching all errors but one is not a bad thing, namely unit testing.
If I have a method:
def my_method():
try:
something()
except IOError, e:
handle_it()
Then it could plausibly have a unit test that looks like:
def test_my_method():
try:
my_module.my_method()
except IOError, e:
print "shouldn't see this error message"
assert False
except Exception, e:
print "some other error message"
assert False
assert True
Because you have now detected that my_method just threw an unexpected exception.
Related
I am curious if there is a way in python to continue on within try/catch block, after you catch an exception, look at its properties, and if not relevant, then continue down the stack.
try:
# Code
except AppleError as apple_ex:
# look at 'apple_ex.error_code' error body, and if not relevant,
# continue on to next down the catch block...
# In other words, proceed to except BananaError and so on down.
except BananaError as banana_ex:
# ...
except Exception as ex:
# ...
That is not how exceptions are handled in Python. When you raise an exception in a try block, if you handle catching it in the except, it will fall inside that block, but will not continue to the next except at that same level. Observe this functional example:
try:
raise AttributeError()
except AttributeError:
raise TypeError()
except TypeError:
print("it got caught") # will not catch the TypeError raised above
So, in your try, we raise an AttributeError, we catch it, and then raise a TypeError inside catching the AttributeError.
The except TypeError will not catch that TypeError.
Based on how you are explaining your problem, you need to rethink how you are handling your exceptions and see if you can determine the handling of errors somewhere else, and raise the error there.
For example:
def some_func():
try:
thing()
except SomeException:
# analyze the exception here and raise the error you *should* raise
if apple_error_thing:
raise AppleError
elif banana_error_thing:
raise BananaError
else:
raise UnknownException
def your_func():
try:
some_func()
except AppleError as e:
print('Apple')
except BananaError as e:
print('Banana')
except UnknownException as e:
print('Unknown')
An AppleError is still an AppleError and not a BananaError, even if error_code is not relevant, so it makes no sense to fall through to BananaError.
You could instead define specific errors for your different error codes:
GRANNY_SMITH_ERROR = 1
MACINTOSH_ERROR = 2
class AppleError(Exception):
def __init__(self, error_code, *args):
super(AppleError, self).__init__(*args)
self.error_code = error_code
class GrannySmithError(AppleError):
def __init__(self, *args):
super(GrannySmithError, self).__init__(GRANNY_SMITH_ERROR, *args)
class MacintoshError(AppleError):
def __init__(self, *args):
super(MacintoshError, self).__init__(MACINTOSH_ERROR, *args)
Then you can try to match the specific error:
try: raise MacintoshError()
except MacintoshError as exc: print("mac")
except GrannySmithError as exc: print("granny smith")
If you do not care to distinguish between different types of apple errors, you can still trap all apple errors:
try: raise MacintoshError()
except AppleError as exc: print("generic apple")
You can combine these, for example, only doing special processing for GrannySmith, not for Macintosh:
try: raise MacintoshError()
except GrannySmithError as exc: print("granny smith")
except AppleError as exc: print("generic apple")
The important thing is to list the errors from most specific to least specific. If you test for AppleError before GrannySmithError, then it will never enter the GrannySmith block.
No, that isn't possible. After the exception is handled by the inner except it doesn't have the ability to get handled by the outer except:
From the docs on the try statement:
When the end of this block is reached, execution continues normally after the entire try statement. (This means that if two nested handlers exist for the same exception, and the exception occurs in the try clause of the inner handler, the outer handler will not handle the exception.)
In short your only solution might be to have another handler at an outer level and re-raise the exception in the inner handler, that is:
try:
try:
raise ZeroDivisionError
except ZeroDivisionError as e:
print("caught")
raise ZeroDivisionError
except ZeroDivisionError as f:
print("caught")
Now the nested except raises an exception which is consequently caught by a similar handler.
I have:
MY_PATH_DIR = 'path/to/my/json/file.json'
try:
with open(MY_PATH_DIR, 'r') as f:
MY_PATH_DIR = json.load(f)
except IOError, RuntimeError, ValueError:
pass
except PermissionDenied:
pass
And I want to catch all possible errors. With
IOError - I am catching errors when the file doesn't exist or has a
syntax error (non valid JSON).
RuntimeError - couldn't test it but I think that makes sense from the
documentation in case of an unexpected error
ValueError - I got from here in case nothing got returned
PermissionDenied - is a specific Django error
Are there any other Exceptions that would make sense? I'm not sure if OSError makes sense here. I think that would be raised earlier, right?
The purpose of capturing exceptions is to control the program's behavior when something bad happened, but in an expected way. If you are not even sure what would cause that exception happen, capturing it would only swallow the underlying programming errors you might have.
I wouldn't add as many kinds of exception as possible to that single block of code, you should only add what you care about. To take it to extreme, each line of code would yield certain exceptions but for obvious reason you couldn't do try except for all of them.
Edit:
For the sake of correctness, since you mentioned I don't want my code to break in any case, you could simply do:
try:
# json.load
except Exception as e:
print "Let's just ignore all exceptions, like this one: %s" % str(e)
This is would give you what exception happens as output.
import random
import sys
def main():
"""Demonstrate the handling of various kinds of exceptions."""
# This is like what you are doing in your code.
exceptions = IOError, RuntimeError, ValueError
try:
raise random.choice(exceptions)()
except exceptions as error:
print('Currently handling:', repr(error))
# The following is not much different from Shang Wang's answer.
try:
raise random.choice(exceptions)()
except Exception as error:
print('Currently handling:', repr(error))
# However, the following code will sometimes not handle the exception.
exceptions += SystemExit, KeyboardInterrupt, GeneratorExit
try:
raise random.choice(exceptions)()
except Exception as error:
print('Currently handling:', repr(error))
# The code can be slightly altered to take the new errors into account.
try:
raise random.choice(exceptions)()
except BaseException as error:
print('Currently handling:', repr(error))
# This does not take into account classes not in the exception hierarchy.
class Death:
pass
try:
raise Death()
except BaseException as error:
print('Currently handling:', repr(error))
# If your version of Python does not consider raising an exception from an
# instance of a class not derived from the BaseException class, the way to
# get around this problem would be with the following code instead.
try:
raise Death()
except:
error = sys.exc_info()[1]
print('Currently handling:', repr(error))
if __name__ == '__main__':
main()
Lets say I have this:
try:
result = call_external_service()
if not result == expected:
raise MyException()
except MyException as ex:
# bubble up
raise ex
except Exception:
# unexpected exceptions from calling external service
do_some_logging()
Due to my limited python knowledge, I cannot think of an elegant way to bubble up the MyException exception, I was hoping I can do something like:
try:
result = call_external_service()
if not result == expected:
raise MyException()
except Exception, exclude(MyException):
# unexpected exceptions from calling external service
do_some_logging()
Your problem seems to be that you are wrapping too much code in your try block. What about this?:
try:
result = call_external_service()
except Exception:
# unexpected exceptions from calling external service
do_some_logging()
if result != expected:
raise MyException()
eg:
try:
myfruits = FruitFunction() #Raises some exception (unknown)
assert "orange" in myfruits #Raises AssertionError (known)
except:
# I don't know how to distinguish these two errors :(
I need to filter out one particular kind of exception (which is known) from all others that are unknown. I also need assertion to continue the same exception handling,
try:
myfruits = FruitFunction() #Raises some exception (unknown)
assert "orange" in myfruits #Raises AssertionError (known)
except AssertionError:
# Handle Assertion Errors here
# But I want the except below to happen too!
except:
# Handle everything here
I will add one real example to convey the idea more concisely:
try:
# This causes all sorts of errors
myurl = urllib.urlopen(parametes)
# But if everything went well
assert myurl.status == 202
# proceed normal stuff
except:
# print "An error occured" if any error occured
# but if it was an assertion error,
# add "it was something serious too!"
try:
try:
myfruits = FruitFunction() #Raises some exception (unknown)
assert "orange" in myfruits #Raises AssertionError (known)
except AssertionError:
# handle assertion
raise
except Exception:
# handle everything
I'm assuming you can't separate the two statements that throw the different exceptions (because they're off together in another function, for example). If you can then the following is more precise and straightforward:
try:
myfruits = FruitFunction() #Raises some exception (unknown)
try:
assert "orange" in myfruits #Raises AssertionError (known)
except AssertionError:
# handle assertion
raise
except Exception:
# handle everything
It's more precise because if the unknown exception raised by FruitFunction() happens to be AssertionError then it won't get caught in the inner try. Without separating the statements, there's no (sensible) way to distinguish two exceptions of the same type thrown from the two different places. So with the first code you'd better hope that FruitFunction() either doesn't raise AssertionError, or that if it does then it can be handled the same way as the other one.
try:
# Do something
myurl = urllib.urlopen(parametes)
assert myurl.status == 202
except Exception as e:
# Handle Exception
print('An error occured')
if isinstance(e, AssertionError):
# Handle AssertionError
print('it was something serious too!')
else:
# proceed normal stuff
In the case that it's both raised an unknown exception and an AssertionError, and you need to handle both, you should use two separate try statements.
try:
raise IndexError()
assert 'orange' in myfruits
except AssertionError:
print 'AssertionError'
except:
print 'another error'
The above will only catch the first error, while
try:
raise IndexError()
except:
print 'another error'
try:
assert 'orange' in myfruits
except AssertionError:
print 'AssertionError'
Will catch both errors.
I need to have a file deleted if not all the operations that must be done on it were successful (that is, if an exception is raised). It could have been as simple as using except:, deleting the file and then re-raising the exception, but in that case the original exception would be lost if the file cannot be deleted in the except clause for whatever arcane reason.
The best that I have been able to come up with is this:
try:
file_path = "whatever.jpg"
# do stuff with file
except:
exception_raised = True
raise
finally:
try:
if exception_raised:
os.unlink(file_path)
except:
pass
return file_path # everything OK
Does anybody know of a better, more Pythonic approach?
Another option is to simply store the exception if you don't want to lose it:
Python 3.x version:
try:
file_path = "whatever.jpg"
# do stuff with file
except BaseException as e:
try:
os.unlink(file_path)
except Exception:
traceback.print_exc()
raise e
The Python 2.x version is slightly more complex since you need to store the complete exception information manually (otherwise you'd lose the traceback):
try:
file_path = "whatever.jpg"
# do stuff with file
except:
e = sys.exc_info()
try:
os.unlink(file_path)
except Exception:
traceback.print_exc()
raise e[0], e[1], e[2]
Edit: Only catch subclasses of Exception in the inner try block, since you don't want do catch SystemExit or KeyboardInterrupt here. Also report any excpetion that occurred during unlinking instead of just dropping it.