Python: Exception raised even when caught in try/except clause [duplicate] - python

This question already has answers here:
How can I more easily suppress previous exceptions when I raise my own exception in response?
(3 answers)
Closed 8 years ago.
In my code I want to catch an exception when it occurs, print some information abut the exception to the screen, and then end the script once I have done so. I tried to use something equivalent to the following code, but I don't understand why I get the traceback I do.
When executing:
try:
1 / 0
except ZeroDivisionError:
print("Exception: ZeroDivisionError")
raise Exception
Console reads:
Exception: ZeroDivisionError
Traceback (most recent call last):
File "<pyshell#19>", line 2, in <module>
1 / 0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#19>", line 5, in <module>
raise Exception
Exception
I thought that if I catch the ZeroDivisionError, it would no longer be raised, and the only thing that would show is the raise Exception I do at the end, but both show in the console.
Why do they both show, and how do I alter the code so only the second shows, or is there a better way to achieve what I want?

The console shows the context here; when an exception is raised from an exception handler, Python attaches the active exception as the __context__ attribute and Python shows that context later on if the new exception is not being handled. If you don't want the context to be shown, you need to supply a cause instead; you can supply an empty cause with with raise ... from None:
try:
1 / 0
except ZeroDivisionError:
print("Exception: ZeroDivisionError")
raise Exception from None
Quoting the raise statement documentation:
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[...]
A similar mechanism works implicitly if an exception is raised inside an exception handler: the previous exception is then attached as the new exception’s __context__ attribute[...]
And from the Exceptions documentation:
When raising (or re-raising) an exception in an except clause __context__ is automatically set to the last exception caught; if the new exception is not handled the traceback that is eventually displayed will include the originating exception(s) and the final exception.
When raising a new exception (rather than using a bare raise to re-raise the exception currently being handled), the implicit exception context can be supplemented with an explicit cause by using from with raise:
raise new_exc from original_exc
The expression following from must be an exception or None. It will be set as __cause__ on the raised exception. Setting __cause__ also implicitly sets the __suppress_context__ attribute to True, so that using raise new_exc from None effectively replaces the old exception with the new one for display purposes (e.g. converting KeyError to AttributeError), while leaving the old exception available in __context__ for introspection when debugging.
The default traceback display code shows these chained exceptions in addition to the traceback for the exception itself. An explicitly chained exception in __cause__ is always shown when present. An implicitly chained exception in __context__ is shown only if __cause__ is None and __suppress_context__ is false.

Related

What are the scoping rules for exception handling? [duplicate]

This question already has an answer here:
Why do I get a `NameError` (or `UnboundLocalError`) from using a named exception after the `except` block?
(1 answer)
Closed 1 year ago.
I stumbled on an interesting scenario following this question. Consider the following simple example:
try:
1/0
error = "error"
except Exception as error:
print(error)
print(error)
The output is:
division by zero
Traceback (most recent call last):
File "main.py", line 7, in <module>
print(error)
NameError: name 'error' is not defined
What happens is of course the first assignment to error is skipped because of the exception, and the except is executed. It prints the exception and - to my understanding - assigns, in a way, the exception object to error. But then, surprisingly, the following print complains that error is not defined.
I tried to understand the scoping rule for exceptions but all I could find in the Exceptions docs was this:
The variable is bound to an exception instance
Which doesn't really explain what is the scope or life-span of this exception instance.
The most similar construct I can think of is a with statement, which doesn't behave the same way:
with open("test.txt") as file:
pass
file.read()
This code will output a maybe expected output of:
Traceback (most recent call last):
File "main.py", line 4, in <module>
file.read()
ValueError: I/O operation on closed file.
But still it is not a NameError which means that file is well-defined.
So can anyone explain what are the scoping rules for exception handling and what is happening here?
This is documented under, The try statement
When an exception has been assigned using as target, it is cleared
at the end of the except clause. This is as if
except E as N:
foo
was translated to
except E as N:
try:
foo
finally:
del N
This means the exception must be assigned to a different name to be
able to refer to it after the except clause

Raising exception with two arguments

Greetings again StackOverflow Community,
I was reading through a library a colleague wrote and found something that I don't quite grasp what they are trying to do. But maybe this is something I am missing with respect to Python syntax.
class SampleClass:
def some_function(self) -> None:
try:
self.do_something()
except CustomException as e:
raise DifferentExceptionClass("Could not do something", e)
# The previous line is the cause of bewilderment.
def do_something(self) -> None:
raise CustomException("Tried to do something and failed.")
I've read that raise can accept arguments but this seems to raise DifferentExceptionClass exception with a tuple as the value. What is the difference between what my colleague has done here and doing something like raise DifferentExeptionClass("Could not do something. {}".format(e)) Is there a benefit to raising an exception his way?
The output to the function call to some_function() is:
test = SampleClass()
test.some_function()
Traceback (most recent call last):
File "<input>", line 4, in some_function
File "<input>", line 10, in do_something
CustomException: Tried to do something and failed.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 6, in some_function
DifferentExceptionClass: ('Could not do something', CustomException('Tried to do something and failed.',))
EDIT: The colleague is unreachable for comment. Also they wrote this library a long time ago and might not remember the "mood" they were in when they wrote this. I thought it would also make a good conversation on SO in case anyone else had seen similar implementation.
I've read that raise can accept arguments but this seems to raise DifferentExceptionClass exception with a tuple as the value.
Exceptions are in fact classes as well. Indeed somewhere you will find something like:
class DifferentExceptionClass(Exception):
def __init__(self,message,innerException):
# ...
pass
So you call a constructor. How the arguments are handled is up to the exception. It is possible that the message is formatted with the inner exception, but it is also possible that it does something totally different.
The advantage is for instance that the innerException (or other parameters) can be stored, inspected and handled accordingly. If you format the exception, the real exception data is lost: you only have a textual representation of it.
There's definitely benefit to doing this. You chain exceptions and provide that chain as information to the user rather than providing the most recent exception created.
Of course, your colleague could of done it in a better way by using syntax Python provides for this. The raise exc from raised_exc syntax is used to raise an exception after another exception has been raised:
except CustomException as e:
raise DifferentExceptionClass("Could not do something") from e
and causes e (the raised exception, CustomException here) to be stored as the __cause__ attribute of the most recent exception (DifferentExceptionClass here) if you need to peek at it.
In the case where a raise is used inside an except handler (as happens in your code snippet), the previous exception (e) is already implicitly stored as a __context__ attribute for it. So, passing it as an argument too, doesn't do anything else but also store the exception in the args tuple.

Exception handling in Python

http://docs.python.org/library/imaplib.html states that:
exception IMAP4.error
Exception raised on any errors. The reason for the exception is passed to the constructor as a string.
What does "exception is passed to the constructor as a string" mean? What would the code look like that can print the reason.
Just use print str(exception).
You can specify the reason when constructing the exception yourself, and put it into a variable when catching the exception.
try:
raise imaplib.IMAP4.error('Some exception')
except imaplib.IMAP4.error, error:
print error

What does this code from types.py do?

try:
raise TypeError
except TypeError:
try:
tb = sys.exc_info()[2]
TracebackType = type(tb)
FrameType = type(tb.tb_frame)
except AttributeError:
# In the restricted environment, exc_info returns (None, None,
# None) Then, tb.tb_frame gives an attribute error
pass
tb = None; del tb
I can't understand this code at all. What is it's purpose?
It's a trick to get a traceback object and a frame object so that TracebackType and FrameType can be assigned their types. It simply raises an exception so it can catch the exception, then get the traceback and frame from sys.exc_info.
The code tries to find out the types used for the tracebacks returned by sys.exc_info() and assigned these types to the variables TracebackType and FrameType.
To do so it first needs to raise an exception and catch it (the TypeError), so that sys.exc_info() can return a traceback for this exception. Then this traceback gets inspected to determine the types. In the end the local tb variable is deleted to not keep unnecessary circular references around (see the warning in the documentation of sys.exc_info()).
It appears as though this code is used to get the call stack. If you research the exc_info function from http://pyref.infogami.com/sys.exc_info you'll find that the function returns a tuple of 3 values where the third is a Traceback object. This object contains the call stack information which is then displayed.

Configuring Python's default exception handling

For an uncaught exception, Python by default prints a stack trace, the exception itself, and terminates. Is anybody aware of a way to tailor this behaviour on the program level (other than establishing my own global, catch-all exception handler), so that the stack trace is omitted? I would like to toggle in my app whether the stack trace is printed or not.
You are looking for sys.excepthook:
sys.excepthook(type, value, traceback)
This function prints out a given traceback and exception to sys.stderr.
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.

Categories