Search code examples
pythonpython-3.xabc

Abstract base classes and Exceptions


Question

Why do virtual subclasses of an abstract Exception created using the ABCMeta.register not match under the except clause?

Background

I'd like to ensure that exceptions that get thrown by a package that I'm using are converted to MyException, so that code which imports my module can catch any exception my module throws using except MyException: instead of except Exception so that they don't have to depend on an implementation detail (the fact that I'm using a third-party package).

Example

To do this, I've tried registering an OtherException as MyException using an abstract base class:

# Tested with python-3.6
from abc import ABC

class MyException(Exception, ABC):
    pass

class OtherException(Exception):
    """Other exception I can't change"""
    pass

MyException.register(OtherException)

assert issubclass(OtherException, MyException)  # passes

try:
    raise OtherException("Some OtherException")
except MyException:
    print("Caught MyException")
except Exception as e:
    print("Caught Exception: {}".format(e))

The assertion passes (as expected), but the exception falls to the second block:

Caught Exception: Some OtherException

Solution

  • Alright, I looked into this some more. The answer is that it's a long-outstanding open issue in Python3 (there since the very first release) and apparently was first reported in 2011. As Guido said in the comments, "I agree it's a bug and should be fixed." Unfortunately, this bug has lingered due to concerns about the performance of the fix and some corner cases that need to be handled.

    The core issue is that the exception matching routine PyErr_GivenExceptionMatches in errors.c uses PyType_IsSubtype and not PyObject_IsSubclass. Since types and objects are supposed to be the same in python3, this amounts to a bug.

    I've made a PR to python3 that seems to cover all of the issues discussed in the thread, but given the history I'm not super optimistic it's going to get merged soon. We'll see.