Can I get the exception from the finally block in python? - python

I have a try/finally clause in my script. Is it possible to get the exact error message from within the finally clause?

No, at finally time sys.exc_info is all-None, whether there has been an exception
or not. Use:
try:
whatever
except:
here sys.exc_info is valid
to re-raise the exception, use a bare `raise`
else:
here you know there was no exception
finally:
and here you can do exception-independent finalization

The finally block will be executed regardless of whether an exception was thrown or not, so as Josh points out, you very likely don't want to be handling it there.
If you really do need the value of an exception that was raised, then you should catch the exception in an except block, and either handle it appropriately or re-raise it, and then use that value in the finally block -- with the expectation that it may never have been set, if there was no exception raised during execution.
import sys
exception_name = exception_value = None
try:
# do stuff
except Exception as e:
exception_name, exception_value, _ = sys.exc_info()
raise # or don't -- it's up to you
finally:
# do something with exception_name and exception_value
# but remember that they might still be none

Actually, other answers are bit vague. So, let me clarify it. You can always invoke sys.exc_info() from finally block. However, its output will vary depending whether exception has been actually raised.
import sys
def f(i):
try:
if i == 1:
raise Exception
except Exception as e:
print "except -> " + str(sys.exc_info())
finally:
print "finally -> " + str(sys.exc_info())
f(0)
f(1)
>>>
finally -> (None, None, None)
except -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x029438F0>)
finally -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x029438F0>)
Thus, you can always know in finally block, whether exception was raised, if it's first level function. But sys.exc_info() will behave differently when length of call stack exceeds 1, as shown in below example. For more information, refer to How sys.exc_info() works?
import sys
def f(i):
try:
if i == 1:
raise Exception
except Exception as e:
print "except -> " + str(sys.exc_info())
finally:
print "finally -> " + str(sys.exc_info())
def f1(i):
if i == 0:
try:
raise Exception('abc')
except Exception as e:
pass
f(i)
f1(0)
f1(1)
>>>
finally -> (<type 'exceptions.Exception'>, Exception('abc',), <traceback object at 0x02A33940>)
except -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x02A33990>)
finally -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x02A33990>)
I hope, it makes things bit clearer.

No, at finally time sys.exc_info is all-None, whether there has been an exception or not. Use [this instead]: …
The other answerer is correct in that you should be handling this inside the except clause.
However, for posterity / the record, here is an answer to the original question as stated:
import sys
try:
int("not an integer LOL")
except:
e = sys.exc_info()[1]
# isinstance(e, ValueError) == True
raise # this line is optional; I have it commented for the example output
else:
e = None # you should do this to avoid a NameError
finally:
print("I really wanted to access %s inside of a finally clause. And I'm doing so now."
% repr(e))
This will print something like:
I really wanted to access ValueError("invalid literal for int() with base 10: 'not an integer LOL'") inside of a finally clause. And I'm doing so now.

You'll want to do that in the except clause, not the finally.
Refer to: http://www.doughellmann.com/articles/Python-Exception-Handling/

Related

How does it help to subclass an exception?

Instead of calling the exception directly, I have seen it is subclassed with nothing in it or a pass statement. How does it help Python internally to subclass a base class, in this manner? Does it change namespace or signature? How?
class ACustomException(Exception):
pass
class BCustomException(Exception):
pass
Raising Exception is like telling the doctor "Something's wrong" and then refusing to answer any questions. Compare:
try:
with open("foo.json", "rt") as r:
new_count = json.load(r)["count"] + 1
except Exception:
# Is the file missing?
# Is the file there, but not readable?
# Is the file readable, but does not contain valid JSON?
# Is the file format okay, but the data's not a dict with `count`?
# Is the entry `count` there, but is not a number?
print("Something's wrong")
# I don't care. You figure it out.
and
try:
with open("data.json", "rt") as r:
new_count = json.load(r)["count"] + 1
except FileNotFoundError:
print("File is missing.")
except PermissionError:
print("File not readable.")
except json.decoder.JSONDecoderError:
print("File is not valid JSON.")
except KeyError:
print("Cannot find count.")
except TypeError:
print("Count is not a number.")
If you are making a library, you can use the predefined exception classes where appropriate — but sometimes you need to communicate errors that Python creators never thought about, or you need to make a finer distinction than the existing exceptions do. This is when you'd create a custom exception.
For example, Django will define django.contrib.auth.models.User.DoesNotExist exception to communicate that the code tried to look for a User in the database, but no User matching the given criteria could be found. Being able to catch django.contrib.auth.models.User.DoesNotExist is like being a doctor, and getting a patient that not only tells you what hurts, but brings X-rays and a printed family history with them.
When you're handling exceptions with try-except, you're catching them by name, so having specific names helps you handle them.
For example, if a function raises Exception for any error, the catching logic gets complicated:
def foobar():
if FOO:
raise Exception('FOO happened')
elif BAR:
raise Exception('BAR happened')
try:
foobar()
except Exception as e:
if e.args == ('FOO happened',):
print('Handling FOO')
elif e.args == ('BAR happened',):
print('Handling BAR')
else:
raise
On the other hand if you have subclassed exceptions, the catching logic is simple:
class FooError(Exception):
pass
class BarError(Exception):
pass
def function():
if FOO:
raise FooError('FOO happened')
elif BAR:
raise BarError('BAR happened')
try:
function()
except FooError:
print('Handling FOO')
except BarError:
print('Handling BAR')
It helps with determining 'what' the traceback issue is referring to in case of maybe a web service that you maybe running, so it's not low-level or the generic errors you normally get back rather the exception class that would be used.
To be more specific with an example:
val = int(input('Enter a number:'))
try:
val *= val
except ValueError as e:
raise e
print(val)
### ValueError will be raised if user inputs something other than a number
### this raise e will return the actual error message saying
### ValueError: invalid literal for int() with base 10: 'ok'
In your case, you could still raise your exception keeping ValueError as the except to be handled for, like this:
val = int(input('Enter a number:'))
try:
val *= val
except ValueError as e:
raise ACustomException('some debug statement referring to the cause of the error')
print(val)
### now this will raise your exception class besides the ValueError exception, with a debug statement if you choose to have one in it.

Why isn't my Python exception being re-raised?

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

Re-raising the exception doesn't work with "finally" clause

When I put in a finally clause, the raise statement in except doesn't work.
So the except block doesn't produce an Exception.
What am I missing? What do I need to do if I want to re-raise the Exception after the finally clause returns the value?
def test():
res = 1
try:
raise Exception
res = 2
except:
print('ha fallado')
raise
finally:
return res
test()
Solution:
def test():
res = 1
try:
raise Exception
res = 2
except:
print('ha fallado')
raise
finally:
# ... finally code that need to exec
pass
return res
print(test())
In this way if a Exception happened, the except block handle the exception and then raise it.
If not exception happened return the value.
Thanks for all the answers! So quick :)
Here is an answer that quotes the relevant part of the documentation:
The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause. If the finally clause raises another exception, the saved exception is set as the context of the new exception. If the finally clause executes a return or break statement, the saved exception is discarded:
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
P.S. I don't quite understand what you actually want to achieve; as noted in the top answer linked by zmbq, you can't really have both.
That's because you put a return statement in the finally block. It means you really want to return a value, even if an exception is thrown.
See here for more information.

Ignore raising exception to the caller in python 3

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)

Print blank exception in python

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

Categories