What is the difference between __cause__ and __context__? - python

These are attributes for Python exceptions, but I am having trouble wrapping my head around them. Python's documentation seems rather quiet about this. I took a look at the documentation but am rather confused. So, what is the difference between the two and how are they used?
EDIT: On that note, how are they related to __traceback__, if at all?
EDIT 3: I guess I just don't understand __cause__. I finally understand __traceback__ and __context__. Why does attribute_error.__cause__ not refer to AttributeError()?
try:
raise NameError() from OSError
except NameError as name_error:
print('name_error.__cause__: %s' % repr(name_error.__cause__))
print('name_error.__context__: %s' % repr(name_error.__context__))
print('name_error.__traceback__: %s' % repr(name_error.__traceback__))
try:
raise AttributeError()
except AttributeError as attribute_error:
print('attribute_error.__cause__: %s' % repr(attribute_error.__cause__))
print('attribute_error.__context__: %s' % repr(attribute_error.__context__))
print('attribute_error.__traceback__: %s' % repr(attribute_error.__traceback__))
raise attribute_error from IndexError
This outputs
name_error.__cause__: OSError()
name_error.__context__: None
name_error.__traceback__: <traceback object at 0x000000000346CAC8>
attribute_error.__cause__: None
attribute_error.__context__: NameError()
attribute_error.__traceback__: <traceback object at 0x000000000346CA88>
Traceback (most recent call last):
File "C:\test\test.py", line 13, in <module>
raise attribute_error from IndexError
File "C:\test\test.py", line 8, in <module>
raise AttributeError()
AttributeError

__cause__ is the cause of the exception - due to the given exception, the current exception was raised. This is a direct link - X threw this exception, therefore Y has to throw this exception.
__context__ on the other hand means that the current exception was raised while trying to handle another exception, and defines the exception that was being handled at the time this one was raised. This is so that you don't lose the fact that the other exceptions happened (and hence were at this code to throw the exception) - the context. X threw this exception, while handling it, Y was also thrown.
__traceback__ shows you the stack - the various levels of functions that have been followed to get to the current line of code. This allows you to pinpoint what caused the exception. It is likely to be used (potentially in tandem with __context__) to find what caused a given bug.

Related

What happens when an exception occurs in an `except` or `finally` clause? [duplicate]

What happens to my first exception (A) when the second (B) is raised in the following code?
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
finally:
raise B('second')
except X as c:
print(c)
If run with X = A I get:
Traceback (most recent call last):
File "raising_more_exceptions.py", line 6, in
raise A('first')
__main__.A: first
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
But if X = B I get:
second
Questions
Where did my first exception go?
Why is only the outermost exception catchable?
How do I peel off the outermost exception and reraise the earlier exceptions?
Update0
This question specifically addresses Python 3, as its exception handling is quite different to Python 2.
Answering to question 3, you can use:
raise B('second') from None
Which will remove the exception A traceback.
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
The 'causing' exception is available as c.__context__ in your last exception handler. Consider your second example where X = B (replaced X with B in the example below):
try:
try:
raise A('first')
finally:
raise B('second')
except B as c:
print(repr(c))
print(repr(c.__context__))
This would output:
B('second')
A('first')
Note that c.__context__ points to A('first'), which is where the first exception went.
Python is using this information to render a more useful traceback. Under Python 2.x the original exception would have been lost, this is for Python 3 only.
Typically you would use this to throw a consistent exception while still keeping the original exception accessible (although it's pretty cool that it happens automatically from an exception handler, I didn't know that!):
try:
do_something_involving_http()
except (URLError, socket.timeout) as ex:
raise MyError('Network error') from ex
More info (and some other pretty useful things you can do) here: http://docs.python.org/3.3/library/exceptions.html
Pythons exception handling will only deal with one exception at a time. However, exception objects are subject to the same variable rules and garbage collection as everything else. Hence, if you save the exception object in a variable somewhere you can deal with it later, even if another exception is raised.
In your case, when an exception is raised during the "finally" statement, Python 3 will print out the traceback of the first exception before the one of the second exception, to be more helpful.
A more common case is that you want to raise an exception during an explicit exception handling. Then you can "save" the exception in the next exception. Just pass it in as a parameter:
>>> class A(Exception):
... pass
...
>>> class B(Exception):
... pass
...
>>> try:
... try:
... raise A('first')
... except A as e:
... raise B('second', e)
... except Exception as c:
... print(c.args[1])
...
first
As you see you can now access the original exception.
I believe all the ingredients to answer your question(s) are already in the existing answers. Let me combine and elaborate.
Let me repeat your question's code to provide line number references:
1 class A(Exception): pass
2 class B(Exception): pass
3
4 try:
5 try:
6 raise A('first')
7 finally:
8 raise B('second')
9 except X as c:
10 print(c)
So to answer your questions:
Where did my first exception go?
Your first exception A is raised in line 6. The finally clause in line 7 is always executed as soon as the try block (lines 5-6) is left, regardless if it is left because of successful completion or because of a raised exception.
While the finally clause is being executed, line 8 raises another exception B. As Lennart and Ignazio have pointed out, only one exception, the one that is most recently being raised, can be kept track of. So as soon as B is raised, the overall try block (lines 4-8) is quit and the exception B is being caught by the except statement in line 9 if it matches (if X is B).
Why is only the outermost exception catchable?
Hopefully this is clear now from my explanation of 1. You could catch the inner/lower/first exception, though. To merge in Lennart's answer, slightly modified, here's how to catch both:
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
except A as e:
raise B('second', e)
except Exception as c:
print(c)
The output is:
('second', A('first',))
How do I peel off the outermost exception and reraise the earlier exceptions?
In Lennart's example the solution to this question is the line except A as e where the inner/lower/first exception is being caught and stored in variable e.
As a general gut-feeling of when to catch exceptions, when to ignore them, and when to re-raise, maybe this question and Alex Martelli's answer help.
It got thrown out.
Only one exception can be "active" at a time per thread.
You can't, unless you encapsulate the earlier exception in the later exception somehow.

How to re-raise the same exception without the traceback?

In an except block I want to raise the same exception but without the stack trace and without the information that this exception has been raised as direct cause of another exception. (and without modifying sys.tracebacklimit globally)
Additionally I have a very clumsy exception class which parses and modifies the message text so I can't just reproduce it.
My current approach is
try:
deeply_nested_function_that_raises_exception()
except ClumsyExceptionBaseClass as exc:
cls, code, msg = exc.__class__, exc.code, exc.msg
raise cls("Error: %d %s" % (code, msg))
What I'm doing here is de-composing the exception information, re-assemble a new exception with a message which will be parsed and split into error code and message in the constructor and raise it from outside the except block in order to forget all trace information.
Is there a more pythonic way to do this? All I want is get rid of the noisy (and useless in my case) trace back while keeping the information contained in the exception object..
In Python 3, you can use with_traceback to remove the traceback entries accumulated so far:
try: ...
except Exception as e:
raise e.with_traceback(None)
In Python 2, it’s just
try: ...
except Exception as e:
raise e # not just "raise"
It will of course still show the trace to this line, since that’s added as the exception propagates (again).

How to use Python Mock to raise an exception - but with Errno set to a given value

Given this Python code:
elif request.method == 'DELETE':
try:
os.remove(full_file)
return jsonify({'results':'purged %s' % full_file})
except OSError as e:
if e.errno != errno.ENOENT:
raise
return jsonify({'results':'file not present: %s' % full_file})
I want to test all possible paths, including the exception handling. Using Mock, it's easy enough to raise an exception, which I do with this code:
with patch('os.remove', new=Mock(side_effect=OSError(errno.ENOENT))):
self.assertRaises(OSError, self.app.delete, file_URL) # broken
Mock raises an exception, which has a printed value of 2 (ENOENT) - but e.errno is set to NONE. And so far, I have not found a way to set it. The result is, the exception always gets re-raised, and I never reach the last line of code, in my unit test.
I've also tried creating a dummy class with errno set, and returning that. But unless it has *side_effect* set to be called, it doesn't raise an exception, and when I set side_effect, I don't get the object.errno as a return value.
Is there a way to have Mock raise an Exception, where that Exception object has the errno attribute set?
Pass two arguments to OSError constructor. (First one should be errno).
For example:
>>> OSError(2).errno
>>> OSError(2, 'message').errno
2
>>> OSError(2, 'message').strerror
'message'

Raise two errors at the same time

Is there any way to raise two errors at the same time by using try and except?
For example, ValueError and KeyError.
How do I do that?
The question asks how to RAISE multiple errors not catch multiple errors.
Strictly speaking you can't raise multiple exceptions but you could raise an object that contains multiple exceptions.
raise Exception(
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
Question: Why would you ever want to do this?
Answer: In a loop when you want to raise an error but process the loop to completion.
For example when unit-testing with unittest2 you might want to raise an exception and keep processing then raise all of the errors at the end. This way you can see all of the errors at once.
def test_me(self):
errors = []
for modulation in self.modulations:
logging.info('Testing modulation = {modulation}'.format(**locals()))
self.digitalModulation().set('value', modulation)
reply = self.getReply()
try:
self._test_nodeValue(reply, self.digitalModulation())
except Exception as e:
errors.append(e)
if errors:
raise Exception(errors)
Python 3.11
Starting with 3.11 you can use ExceptionGroup to raise multiple exceptions.
raise ExceptionGroup("this was bad",
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
You could raise an error which inherits from both ValueError and KeyError. It would get caught by a catch block for either.
class MyError(ValueError, KeyError):
...
Yes, you can handle more than one error, either using
try:
# your code here
except (ValueError, KeyError) as e:
# catch it, the exception is accessable via the variable e
Or, directly add two "ways" of handling different errors:
try:
# your code here
except ValueError as e:
# catch it, the exception is accessable via the variable e
except KeyError as e:
# catch it, the exception is accessable via the variable e
You may also leave out the "e" variable.
Checkout the documentation: http://docs.python.org/tutorial/errors.html#handling-exceptions
The solution from #shrewmouse still requires to choose an exception class to wrap the caught exceptions.
Following solution uses Exception Chaining via finally to execute code after one exception occurs
We don't need to know beforehand, what exceptions occur
Note that only the first exception that occurs can be detected from the caller via except
if this is a problem, use #Collin's solution above to inherit from all collected exceptions
You'll see the exceptions separated by:
"During handling of the above exception, another exception occurred:"
def raise_multiple(errors):
if not errors: # list emptied, recursion ends
return
try:
raise errors.pop() # pop removes list entries
finally:
raise_multiple(errors) # recursion
If you have a task that needs to be done for each element of a list, you don't need to collect the Exceptions beforehand. Here's an example for multiple file deletion with multiple error reporting:
def delete_multiple(files):
if not files:
return
try:
os.remove(files.pop())
finally:
delete_multiple(files)
PS:
Tested with Python 3.8.5
To print full traceback per exception have a look at traceback.print_exc
The original question is answered since years. But as this page is the top search result for "python raise multiple" I share my approach to fill an (IMHO relevant) gap in the solution spectrum.
try :
pass
except (ValueError,KeyError):
pass
read more about Handling exceptions
You can Raise more than one exception like this:
try:
i = 0
j = 1 / i
except ZeroDivisionError:
try:
i = 'j'
j = 4 + i
except TypeError:
raise ValueError
NOTE: it may be that only the ValueError is raised but this error message seems right:
Traceback (most recent call last):
File "<pyshell#9>", line 3, in <module>
j = 1 / i
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 7, in <module>
j = 4 + i
TypeError: unsupported operand type(s) for +: 'int' and 'str'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 9, in <module>
raise ValueError
ValueError

Raising exceptions when an exception is already present in Python 3

What happens to my first exception (A) when the second (B) is raised in the following code?
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
finally:
raise B('second')
except X as c:
print(c)
If run with X = A I get:
Traceback (most recent call last):
File "raising_more_exceptions.py", line 6, in
raise A('first')
__main__.A: first
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
But if X = B I get:
second
Questions
Where did my first exception go?
Why is only the outermost exception catchable?
How do I peel off the outermost exception and reraise the earlier exceptions?
Update0
This question specifically addresses Python 3, as its exception handling is quite different to Python 2.
Answering to question 3, you can use:
raise B('second') from None
Which will remove the exception A traceback.
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
The 'causing' exception is available as c.__context__ in your last exception handler. Consider your second example where X = B (replaced X with B in the example below):
try:
try:
raise A('first')
finally:
raise B('second')
except B as c:
print(repr(c))
print(repr(c.__context__))
This would output:
B('second')
A('first')
Note that c.__context__ points to A('first'), which is where the first exception went.
Python is using this information to render a more useful traceback. Under Python 2.x the original exception would have been lost, this is for Python 3 only.
Typically you would use this to throw a consistent exception while still keeping the original exception accessible (although it's pretty cool that it happens automatically from an exception handler, I didn't know that!):
try:
do_something_involving_http()
except (URLError, socket.timeout) as ex:
raise MyError('Network error') from ex
More info (and some other pretty useful things you can do) here: http://docs.python.org/3.3/library/exceptions.html
Pythons exception handling will only deal with one exception at a time. However, exception objects are subject to the same variable rules and garbage collection as everything else. Hence, if you save the exception object in a variable somewhere you can deal with it later, even if another exception is raised.
In your case, when an exception is raised during the "finally" statement, Python 3 will print out the traceback of the first exception before the one of the second exception, to be more helpful.
A more common case is that you want to raise an exception during an explicit exception handling. Then you can "save" the exception in the next exception. Just pass it in as a parameter:
>>> class A(Exception):
... pass
...
>>> class B(Exception):
... pass
...
>>> try:
... try:
... raise A('first')
... except A as e:
... raise B('second', e)
... except Exception as c:
... print(c.args[1])
...
first
As you see you can now access the original exception.
I believe all the ingredients to answer your question(s) are already in the existing answers. Let me combine and elaborate.
Let me repeat your question's code to provide line number references:
1 class A(Exception): pass
2 class B(Exception): pass
3
4 try:
5 try:
6 raise A('first')
7 finally:
8 raise B('second')
9 except X as c:
10 print(c)
So to answer your questions:
Where did my first exception go?
Your first exception A is raised in line 6. The finally clause in line 7 is always executed as soon as the try block (lines 5-6) is left, regardless if it is left because of successful completion or because of a raised exception.
While the finally clause is being executed, line 8 raises another exception B. As Lennart and Ignazio have pointed out, only one exception, the one that is most recently being raised, can be kept track of. So as soon as B is raised, the overall try block (lines 4-8) is quit and the exception B is being caught by the except statement in line 9 if it matches (if X is B).
Why is only the outermost exception catchable?
Hopefully this is clear now from my explanation of 1. You could catch the inner/lower/first exception, though. To merge in Lennart's answer, slightly modified, here's how to catch both:
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
except A as e:
raise B('second', e)
except Exception as c:
print(c)
The output is:
('second', A('first',))
How do I peel off the outermost exception and reraise the earlier exceptions?
In Lennart's example the solution to this question is the line except A as e where the inner/lower/first exception is being caught and stored in variable e.
As a general gut-feeling of when to catch exceptions, when to ignore them, and when to re-raise, maybe this question and Alex Martelli's answer help.
It got thrown out.
Only one exception can be "active" at a time per thread.
You can't, unless you encapsulate the earlier exception in the later exception somehow.

Categories