I want to print all the exception statements from within the inner try to the catch of the outside try. Is there any way to do this without changing the inner try-catch block
def test_nested_exceptions():
try:
try:
raise AssertionError('inner error ')
except AssertionError as ae:
raise AssertionError("error in except")
finally:
raise AssertionError("error in finally")
except AssertionError as e:
print(e)
You can't access the error object in the finally block but you could get some details using the sys module as shown below.
import sys
def test_nested_exceptions():
try:
try:
raise AssertionError('inner error ')
except AssertionError as ae:
print(ae)
raise AssertionError("error in except")
finally:
print(sys.exc_info())
raise AssertionError("error in finally")
except AssertionError as e:
print(e)
test_nested_exceptions()
Related
I know I can store an exception name in a variable with this syntax:
try:
code
except TypeError as e:
logger.error(e)
except NameError as e:
logger.error(e)
How do I do the same for the generic except: message? I assume that this (which is the general idea) won't work:
try:
code
except as e:
logger.error(e)
You can you use type(e).__name__ to capture the name of any error you encounter, and access the message as you a normal variable, with e.message. All the built in errors (indexError, TypeError, etc.) are children of the class Exception, so they will be picked up. to save it as a variable named 'err':
try:
code
except Exception as e:
err = type(e).__name__
message = e.message
This will save the error type of any exception of the base python type Exception that you run into, using the built in __name__ variable
BaseException is the broadest type you can catch:
try:
# some code
except BaseException as e:
logger.error(e)
You are able to catch Exception:
import logging
try:
code
except TypeError as e:
logger.error(e)
except NameError as e:
logger.error(e)
except Exception as e:
logging.error(e)
I ran into this with Python 2, where old-style classes aren't caught by except Exception or except BaseException. I solved it by using sys.exc_info to access the current exception:
import sys
try:
code
except:
e = sys.exc_info()[1]
logging.error(e)
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.
In Python, we can use as to capture the exception instance in the except statement. However, there does not seem to have an easy way to do the same in an else statement after the try. To be more clear, see the code below.
try:
raise Exception('Foo')
except ValueError as valueError:
print(valueError)
print('I can capture the exception instance with "as" keyword')
else: # Cannot use "as" here
print('Some exception other than ValueError')
print('How can I capture the exception in order to, e.g. print its message?')
Any idea?
try:
raise Exception('Foo')
except ValueError as valueError:
print(valueError)
print('I can capture the exception instance with "as" keyword')
except Exception as e:
print(e)
print('Some exception other than ValueError')
else:
print('no exception raised')
Use multiple except clauses:
Either like this:
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
or clubbing exceptions together:
except (RuntimeError, TypeError, NameError):
pass
At below, I have a try-except block that I want to refactor it. As you see, it is not pythonic and not-maintainable.
try:
try:
foo()
except xError:
doSth()
raise
except:
exc_type = sys.exc_info()[0]
if exc_type == FooError:
doBlaBla()
else:
doFlaFla()
blablabla() # whatever the exceptions is, run this code block
foobar()
zoo()
...
I change that block as the below code;
try:
try:
foo()
except xError:
doSth()
raise
except FooError:
doBlaBla()
raise
except:
doFlaFla()
raise
blablabla() # This is where the problem happens for me.
foobar()
zoo()
...
As you can see, I need a except-finally like operation. It will not run when no exception raises but any exceptions. What do you advice ? How should I change this clode block ?
Why you've used innder try exempt block?
In my opinion, it is a good practice to put finally except statement, that will catch unexpected error.
So I suggest:
try:
fn()
except Exception1:
do stuff you want
except Exception 2:
do another stuff
except Exception as e:
# Here you will catch an unexpected error, and you can log it
raise UnknownError -> do stuff you want
Why not something like:
def others():
"""Other stuff to do when non-xError occurs."""
blablabla()
foobar()
zoo()
and then factor into a single try:
try:
foo()
except xError:
doSth()
raise
except FooError:
doBlaBla()
others()
except Exception:
doFlaFla()
others()
A bare except is usually a bad idea.
You can wrap your exception-code in a try-finally block, for example:
try:
try:
foo()
except xError:
doSth()
raise
# catch Exception to avoid catching system errors
except Exception as e:
try:
if isinstance(e, FooError):
doBlaBla()
else:
doFlaFla()
raise
finally:
# this will always run regardless of what you raise
Another approach could be something like this:
e = None
try:
try:
foo()
except xError:
doSth()
raise
except FooError as e:
doBlaBla()
except Exception as e:
doFlaFla()
raise
finally:
# this will always run regardless of what you raise
if e:
# exception took place...
Say I have some code like this:
try:
try:
raise Exception("in the try")
finally:
raise Exception("in the finally")
except Exception, e:
print "try block failed: %s" % (e,)
The output is:
try block failed: in the finally
From the point of that print statement, is there any way to access the exception raised in the try, or has it disappeared forever?
NOTE: I don't have a use case in mind; this is just curiosity.
I can't find any information about whether this has been backported and don't have a Py2 installation handy, but in Python 3, e has an attribute called e.__context__, so that:
try:
try:
raise Exception("in the try")
finally:
raise Exception("in the finally")
except Exception as e:
print(repr(e.__context__))
gives:
Exception('in the try',)
According to PEP 3314, before __context__ was added, information about the original exception was unavailable.
try:
try:
raise Exception("in the try")
except Exception, e:
print "try block failed"
finally:
raise Exception("in the finally")
except Exception, e:
print "finally block failed: %s" % (e,)
However, It would be a good idea to avoid having code that is likely to throw an exception in the finally block - usually you just use it to do cleanup etc. anyway.