I have a code sample below, where I want to catch some exception:
in the main function:
try:
...
do_something()
except some_exception,e:
do_some_other_thing
in the do_something function
try:
...
except some_exception,e:
do_some_other_thing
So when I was testing it, I noticed that the exception was handled twice (once in do_somthing(), and once in the main function), is my observation accurate?
Is there a way to catch the exception that are only not captured by its function? Because there are two scenarios that I want to catch and they are somewhat wrapped into the same exception handling class(i.e. some_exception)
Your observation is inaccurate; there must be 2 places that raise some_exeption, or you are explicitly re-raising it.
Once an exception has correctly been caught, it will not be caught by other handlers higher up in the stack.
This is easiest shown with a little demonstration:
>>> try:
... try:
... raise AttributeError('foo')
... except AttributeError as e:
... print 'caught', e
... except AttributeError as e:
... print 'caught the same exception again?', e
...
caught foo
Only the inner except handler was invoked. If, on the other hand, we re-raise the exception, you'll see both handlers print a message:
>>> try:
... try:
... raise AttributeError('foo')
... except AttributeError as e:
... print 'caught', e
... raise e
... except AttributeError as e:
... print 'caught the same exception again?', e
...
caught foo
caught the same exception again? foo
As such, there is no need to have to handle 'exceptions that are not caught by the function'; you will only need to handle exceptions that were not already caught earlier.
Why not try it out and see what happens with a simple test?
def main():
try:
raise ValueError("in main!")
except ValueError as e:
print "caught exception",str(e)
#for fun, re-run with the next line uncommented and see what happens!
#raise
try:
main()
except ValueError:
print "caught an exception above main"
If you actually try this code, you'll notice that it only prints one message (inside the main function) demonstrating that an exception will not propagate upward once caught (unless you re-raise it in your except clause).
Related
I have a function within a module, that does something like this:
def some_func():
try:
# do some error-prone thing
raise ValueError
return 'calculated foo'
except AttributeError as err:
# handle it
pass
except:
print('Some other error happened, let\'s reraise it....')
raise
else:
pass
finally:
return 'default foo'
Then within my main program,
try:
val = some_func()
print('val=', val)
except:
print('In main except')
raise
else:
print('In main else')
pass
finally:
print('And we\'re done')
My output is:
Some other error happened, let's reraise it....
val= default foo
In main else
And we're done
No exception is raised.
At the risk of missing something obvious, why isn't the ValueError being reraised within my main? It almost seems like the return within my finally in some_func() is causing the exception not to be reraised, but this seems odd to me and I can't find any documentation of it. Here's what I think should be happpening, would like to understand where I'm off.
I call some_func() within my main program
some_func() raises ValueError
The ValueError is caught within the some_func() except, prints "Some other error happened" and reraises it.
Back in main, I thought the reraised ValueError should get caught by the except, should print 'In main except', reraise, and then the exception itself should be uncaught causing the program to halt. But I get no exception raised and wind up in the else clause instead.
This is intended behavior and described in the documentation:
If finally is present, it specifies a ‘cleanup’ handler. (...) If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. (...) If the finally clause executes a return, break or continue statement, the saved exception is discarded
In Python 3, there's a useful raise ... from ... feature to re-raise an exception. That said, how do you find the original (/ re-raised) exception from the raised exception? Here's a (silly) example with comments to demonstrate what I mean--
def some_func():
try:
None() # TypeError: 'NoneType' object is not callable
except as err:
raise Exception("blah") from err
try:
some_func()
except as err:
# how can I access the original exception (TypeError)?
It's in the __cause__ attribute of the raised exception. Taken from the docs on the raise statement it says regarding raise ... from ...:
The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed.
So, in your given scenario, repring the __cause__ attribute:
def some_func():
try:
None() # TypeError: 'NoneType' object is not callable
except TypeError as err:
raise Exception("blah") from err
try:
some_func()
except Exception as er:
print(repr(er.__cause__))
Will print out:
TypeError("'NoneType' object is not callable",)
Whenever an exception is raised from an exception handler (the except clause), the original exception will bestored in new exception's __context__.
Whenever an exception is raised using from syntax, the exception specified in from will be saved in the __cause__ attribute of the new exception.
In the usual use case, that amounts to both __cause__ and __context__ containing the original exception:
def f():
try:
raise Exception('first exception')
except Exception as e:
raise Exception('second exception') from e
try:
f()
except Exception as e:
print('This exception', e)
print('Original exception', e.__context__)
print('Also original exception', e.__cause__)
Here is also an example of when __context__ is set:
try:
raise Exception('first exception')
except Exception as e:
raise Exception('second exception')
and an example of when __cause__ is set:
e = Exception('first exception')
raise Exception('second exception') from e
How can I ignore a certain exception to be raised to the caller in python 3?
Example:
def do_something():
try:
statement1
statement2
except Exception as e:
# ignore the exception
logging.warning("this is normal, exception is ignored")
try:
do_something()
except Exception as e:
# this is unexpected control flow, the first exception is already ignored !!
logging.error("unexpected error")
logging.error(e) # prints None
I found someone mentioned that "Because of the last thrown exception being remembered in Python, some of the objects involved in the exception-throwing statement are being kept live indefinitely" and then mentioned to use "sys.exc_clear()" in this case which is not available anymore in python 3. Any clue how can I completely ignore the exception in python3?
There's no need to do this in Python 3, sys.exc_clear() was removed because Python doesn't store the last raised exception internally as it did in Python 2:
For example, in Python 2, the exception is still kept alive when inside a function:
def foo():
try:
raise ValueError()
except ValueError as e:
print(e)
import sys; print(sys.exc_info())
Calling foo now shows the exception is kept:
foo()
(<type 'exceptions.ValueError'>, ValueError(), <traceback object at 0x7f45c57fc560>)
You need to call sys.exc_clear() in order to clear the Exception raised.
In Python 3, on the contrary:
def foo():
try:
raise ValueError()
except ValueError as e:
print(e)
import sys; print(sys.exc_info())
Calling the same function:
foo()
(None, None, None)
I am trying to figure out how I would print an exception if I don't know what the exception is in the first place. How would I do the following?
try:
some_command
except:
print *full_exception_trace*
Like the tutorial says.
try:
something()
except SomeException as e:
something_else(e)
You may find traceback useful.
def exception(self)
try:
Something.objects.all()
except Exception, err:
print err.message #(if you want)
#raise err
raise # The 'raise' statement with no arguments inside an error
# handler tells Python to re-raise the exception with the
# original traceback intact
err.message will give you the reason of the exception
The traceback module's print_exc() function seems to be what you want. Docs
my code is like below
class Something(models.Model)
def exception(self)
try:
Something.objects.all()
except Exception():
raise Exception()
called this method from testcases ,its working but i need to raise exception ,it does not catch the exception
and here is my test case
def test_exception(self):
instance = Something()
instance.exception()
its working fine but i need to raise exception from except block
This line:
except Exception():
should be:
except Exception:
def exception(self)
try:
Something.objects.all()
except Exception, err:
#print err.message (if you want)
raise err
This will catch the error and print the exact msg if required.
Why catch the Exception just to re-raise it?
If you are not doing anything in the except suite except re-raising the exception, then simply do not catch the exception in the first place:
#staticmethod
def exception():
Something.objects.all()
If you are doing something nontrivial inside the except suite, then:
def exception(self):
try:
Something.objects.all()
except Exception:
# do something (with self?)
raise
Then, to test that the exception method raises an Exception:
def test_exception(self):
instance = Something()
self.assertRaises(Exception, instance.exception)
This depends on Something.objects.all() raising Exception.
PS. If exception does not depend on self, then it is best to remove it from the argument list and make exception a staticmethod.
PPS. Exception is a very broad base exception class. A more specific exception would be more helpful for debugging, and allow other code to catch this specific exception instead of forcing it to handle any possible Exception.