I try to construct a except clause that catches everything except [sic] a particular type of exception:
try:
try:
asdjaslk
except not NameError as ne: #I want this block to catch everything except NameError
print("!NameError- {0}: {1}".format(ne.__class__, ne))
except Exception as e: #NameError is the only one that should get here
print("Exception- {0}: {1}".format(e.__class__, e))
The language accepts the not in the except clause, but does nothing with it:
>>> Exception- <type 'exceptions.NameError'>: name 'asdjaslk' is not defined
Is it possible to do this or should I reraise them?
You'll have to re-raise. An except statement can only whitelist, not blacklist.
try:
asdjaslk
except Exception as ne:
if isinstance(ne, NameError):
raise
The not NameError expression returns False, so you are essentially trying to catch:
except False:
but no exception will ever match a boolean instance.
The syntax allows for any valid Python expression, and the thrown exception is matched against the outcome of that expression. except SomeException if debug else SomeOtherException: is perfectly valid, for example.
You can can try this:
try:
# your code raising exceptions
except NameError:
# catch the exception you don't want to catch
# but immediately raise it again:
print("re-raising NameError- {0}: {1}".format(ne.__class__, ne))
raise
except Exception as e:
# handle all other exceptions here
print("catching Exception- {0}: {1}".format(e.__class__, e))
pass
Related
I have a try/except block with multiple except blocks. I want to be able to execute a piece of code only if any of the exceptions are raised. Kind of like the finally statement, but finally execute regardless of if an exception was raised or not. Do I just have to add that code to each except block?
You can do your own type checking in the exception handler to deal with type-specific and general code.
def it_will_end_in_tears():
raise ValueError("bad value")
try:
val = it_will_end_in_tears()
except (TypeError, ValueError) as e:
if isinstance(e, TypeError):
print("type error stuff")
elif isinstance(e, ValueError):
print("value error stuff")
print("common stuff")
finally:
print("finally")
Instead of copy+pasting the code into each except block, i would declare a boolean execute_code = False and set it to True in each of the except blocks. After the whole try/except, add if execute_code:, then implement the code to execute if an exception occurred once inside the if block.
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'm trying to do a nested try/catch block in Python to print some extra debugging information:
try:
assert( False )
except:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise
I'd like to always re-raise the first error, but this code appears to raise the second error (the one caught with "that didn't work either"). Is there a way to re-raise the first exception?
raise, with no arguments, raises the last exception. To get the behavior you want, put the error in a variable so that you can raise with that exception instead:
try:
assert( False )
# Right here
except Exception as e:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise e
Note however that you should capture for a more specific exception rather than just Exception.
You should capture the first Exception in a variable.
try:
assert(False)
except Exception as e:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise e
raise by default will raise the last Exception.
raise raises the last exception caught unless you specify otherwise. If you want to reraise an early exception, you have to bind it to a name for later reference.
In Python 2.x:
try:
assert False
except Exception, e:
...
raise e
In Python 3.x:
try:
assert False
except Exception as e:
...
raise e
Unless you are writing general purpose code, you want to catch only the exceptions you are prepared to deal with... so in the above example you would write:
except AssertionError ... :
I would like to catch a specific exception and handle it accordingly - then I would like to continue and perform the generic handling that other exceptions would have to.
Coming from a C background, I could have previously utilised gotos to achieve the desired effect.
This is what I'm currently doing is and it works fine:
try:
output_var = some_magical_function()
except IntegrityError as ie:
integrity_error_handling()
shared_exception_handling_function(zde) # could be error reporting
except SomeOtherException as soe:
shared_exception_handling_function(soe) # the same function as above
Tldr:
Ie - is there "Pythonic" way of doing the following:
try:
output_var = some_magical_function()
except IntegrityError as ie:
integrity_error_handling()
except ALLExceptions as ae: # all exceptions INCLUDING the IntregityError
shared_exception_handling_function(ae) # could be error reporting
NB: I am aware of the finally clause - this isn't intended for tidy-up (ie- closing files)ยท
You could reraise the exception, and handle the generic case in the outer handler of a nested setup:
try:
try:
output_var = some_magical_function()
except IntegrityError as zde:
integrity_error_handling()
raise
except ALLExceptions as ae: # all exceptions INCLUDING the IntregityError
shared_exception_handling_function(ae) # could be error reporting
The unqualified raise statement re-raises the current exception, so the IntegrityError exception is thrown again to be handled by the AllExceptions handler.
The other path you could take is to test for the exception type:
try:
output_var = some_magical_function()
except ALLExceptions as ae: # all exceptions INCLUDING the IntregityError
if isinstance(ae, IntegrityError):
integrity_error_handling()
shared_exception_handling_function(ae) # could be error reporting
The Exception class will match all exceptions...
try:
output_var = some_magical_function()
except IntegrityError as zde:
integrity_error_handling()
except Exception as ae:
shared_exception_handling_function(ae) # could be error reporting
But it sounds like you want the final clause to run for both IntegrityError exceptions as well as everything else. So you'll need a different construct, possibly this:
try:
try:
output_var = some_magical_function()
except IntegrityError as zde:
integrity_error_handling()
raise
except Exception as ae:
shared_exception_handling_function(ae) # could be error reporting
The raise command on the inner try...except block causes the
caught exception to be passed up to the outer block.
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.