[Python-ideas] Use `isinstance` check during exception handling

Andrew Barnert abarnert at yahoo.com
Thu Nov 5 15:06:40 EST 2015


On Nov 5, 2015, at 07:00, sjoerdjob at sjec.nl wrote:

>>> On Nov 4, 2015, at 14:02, sjoerdjob at sjec.nl wrote:
>>> 
>>> Hi all,
>>> 
>>> TL;DR: Change exception checking logic from
>>>   err.__class__ in exc.__mro__
>>> to
>>>   isinstance(err, exc)
>>> Because it is more powerful, more consistent and allows for cleaner
>>> code.
>> 
>> Correct me if I'm wrong, but doesn't Python allow you to raise a class and
>> handle it without ever creating an instance? If an instance is needed
>> (e.g., because the exception is caught by a handler with an as clause),
>> one is created by calling exc(), but if it's not needed, that doesn't
>> happen.
>> 
>> Assuming I'm right, your proposal would mean the instance is _always_
>> needed, so if you raise a type, exc() is always called. So, for example:
>> 
>>    class MyException(Exception):
>>        def __init__(self, thingy, *a, **kw):
>>            super().__init__(*a, **kw)
>>            self.thingy = thingy
>> 
>>    try:
>>        raise MyException
>>    except MyException:
>>        print('whoops')
>> 
>> Instead of printing "whoops", it would now (I think) abort with a
>> TypeError about trying to call MyException with the wrong number of
>> arguments.
> 
> Yes, the exception is instantiated as soon as it is raised (not when it's
> matched). This happens in Python/ceval.c. So you actually have to catch
> `TypeError` in this case.

Then what's the point of the code at Python/errors.c#186 that deals with the fact that the given exception (err) can be either an instance or a type? If it must always be an instance, surely those two lines are unnecessary and misleading.

In fact, looking into it a bit more: the normal way to check for an exception from C is with PyErr_ExceptionMatches, which calls PyErr_GivenExceptionMatches with the type, not the value, of the exception. So, even if an instance has been created, you don't usually have it inside PyErr_GivenExceptionMatches anyway, so the proposed change wouldn't work without further changes. At the very least you'd need to change PyErr_ExceptionMatches to pass the value instead of the type. But presumably there's a reason it passes the type in the first place (maybe in CPython it's not possible to raise a type without an instance from pure Python code, but it is from C API code?), which means there's probably other code that has to change as well. 

And that's assuming there's no other code than calls PyErr_GivenExceptionMatches with the value of PyErr_Occurred() (or tstate->curexc_type or any other way of getting an exception type) anywhere in CPython or in any extension module anyone's ever written. Since there's nothing about the docs that implies that this is invalid, and in fact the docs for PyErr_ExceptionMatches and the source code for PyErr_GivenExceptionMatches both strongly imply that passing a type is the normal way to check, I doubt that assumption is true.

>> Plus, there's almost certainly code where someone skips creating the
>> instance as an optimization—probably it's usually a useless premature
>> one, but maybe someone has tested and it makes a difference in a real
>> program.
> 
> It will get instantiated anyway.
> 
>> One obvious possibility is: if exc is itself a subclass of BaseException,
>> you use the existing subclass check; if not, instead of using the subclass
>> check on its type, you use the new instance check. But you'd have to think
>> that through to make sure it makes sense.
> 


More information about the Python-ideas mailing list