Description
Bug report
Bug description:
Consider the following:
class ConstructsNone(BaseException):
@classmethod
def __new__(*args, **kwargs): return None
raise Exception("Printing this exception raises an exception. Mwa-ha-nyaa~ >:3") from ConstructsNone
TypeError: print_exception(): Exception expected for value, NoneType found
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: Printing this exception raises an exception. Mwa-ha-nyaa~ >:3
In Python/ceval.c
, in do_raise()
, when you raise an object, cpython checks if it's an exception type, and if it is, constructs it by calling it with no arguments. Then it checks to make sure that what was constructed is in fact an exception.
Then it does the same thing for the exception's cause. If it's a type, it constructs the cause by calling it with no arguments. But, for the cause, it actually doesn't check to make sure that the result of the call is in fact an exception, it just stores the result without checking. This seems like a bug. Not a catastrophic one by any means, but probably unintentional considering that the very same condition is checked a few lines above.
That doesn't necessarily explain the result above though. We've created an exception object where the cause is None
(or any other sort of object that we want). Then, when the interpreter (interactive mode) goes to print the exception, it expects the cause to be an exception. This leads to yet another exception being raised, telling you that the cause is the wrong type.
The solution of course is just to add the check when the cause is called. I've submitted the pull request, here: #112216
CPython versions tested on:
3.10, 3.11, 3.12, CPython main branch
Operating systems tested on:
Linux, Windows