During handling of the above exception, another exception occurred
Python is alerting you that you threw an exception while another one was in the process of being handled. The warning is there to alert you in case this was unexpected so that you know about the original exception. Consider a case like the following:
class Resource:
def close(self):
if cannot_close:
raise Error("Cannot close")
def write_data(self, data):
...
some_function():
try:
res = Resource()
res.write_data("some data")
except Error as e:
res.close()
Let's say that write_data
raises an exception, but then the close
does so also, unexpectedly. When that happens, it's nice to know about both exceptions. In most cases, you want to know about the original exception raised by write_data
but knowing about the one from close
helps you know that something strange happened with close
.
But for your case, you are simply restating the original error in a new way. Doing so lets you provide more context, for example:
with open(json_file) as j:
try:
json_config = json.load(j)
except ValueError as e:
raise Exception('Invalid json from file {}: {}'.format(json_file, e))
This would provide you with the path of the file that failed to be parsed as JSON, which is helpful context information that won't be in the original exception message.
So to tell Python that you are essentially re-raising the original exception, but with more context, you add from e
.
with open(json_file) as j:
try:
json_config = json.load(j)
except ValueError as e:
raise Exception('Invalid json from file {}: {}'.format(json_file, e)) from e
This is equivalent to "exception chaining" in Java where you supply the original exception to the constructor of the new exception.
Since you're raising another exception from inside your except
statement, python is just telling you that.
In other words, usually you use except
to handle an exception and not make the program fail, but in this case you're raising another exception while already handling one, which is what python is telling you.
There is really nothing to be worried about, if that's the behavior you want. If you want to "get rid" of that message, you can perhaps write something to the output without raising another exception, or just make the first halt the program without using a try/except
statement.
As Steven suggests, you can do:
raise Exception('Invalid json: {}'.format(e)) from e
to get both exceptions printed, like this:
Traceback (most recent call last):
File "tmp.py", line 5, in <module>
raise Exception('Invalid json: {}'.format(e)) from e
Exception
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
<...>
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 103 column 9 (char 1093)
Or you can do this:
raise Exception('Invalid json: {}'.format(e)) from None
To suppress the first one and only log the Invalid json...
exception.
By the way, doing something like raise Exception('Invalid json: {}'.format(e))
doesn't really make much sense, at that point you can just leave the original exception alone, since you're not adding much information to it.
Currently, you having an issue with raising the ValueError
exception inside another caught exception. The reasoning for this solution doesn't make much sense to me but if you change
raise Exception('Invalid json: {}'.format(e))
To
raise Exception('Invalid json: {}'.format(e)) from None
Making your end code.
with open(json_file) as j:
try:
json_config = json.load(j)
except ValueError as e:
raise Exception('Invalid json: {}'.format(e)) from None
You should get the desired result of catching an exception.
e.g.
>>> foo = {}
>>> try:
... var = foo['bar']
... except KeyError:
... raise KeyError('No key bar in dict foo') from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
KeyError: 'No key bar in dict foo'
Sorry I can't give you an explanation why this works specifically but it seems to do the trick.
UPDATE: Looks like there's a PEP doc explaining how to suppress these exception inside exception warnings.