Exception exceptions based on their abstract base class

Suppose I have an exception class with an abstract base class, something like this:

class MyExceptions(BaseExeption, metaclass=abc.ABCMeta): pass class ProperSubclass(MyExceptions): pass MyExceptions.register(ValueError) 

It looks like I can catch a ProperSubclass on MyExceptions , but not a ValueError :

 try: raise ProperSubclass() except MyExceptions: print('As expected, control comes here...') except: print('...and not here.') try: raise ValueError() except MyExceptions: print('Control does not come here...') except ValueError: print('...but unexpectedly comes here.') 

So my question is, should I handle the built-in exceptions with their abstract base class? If so, how? And if not, what are the rules?

I guess another way to ask the question is: do except clauses use isinstance () / issubclass () correctly for matching, and if not (as it happens), what do they use? Perhaps there are some shady shortcuts in the C implementation.

+7
python exception abstract-base-class
source share
1 answer

The documentation says:

An object is compatible with an exception if it is the class or base class of the exception object or a tuple containing an element compatible with the exception.

Unfortunately, this does not mean whether virtual base classes should be considered, unlike a language, for example. issubclass :

Returns true if the class is a subclass (direct, indirect or virtual) of the info class. [...]

When overriding instance and subclass validation, the language does not help either:

The following methods are used to override the default behavior of the built-in functions isinstance() and issubclass() . [...]

In fact, as you suspected, the CPython implementation (for Python 3) bypasses the subclass checks by directly calling PyType_IsSubtype :

http://hg.python.org/cpython/file/3.4/Python/errors.c#l167

 PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc) { ... /* PyObject_IsSubclass() can recurse and therefore is not safe (see test_bad_getattr in test.pickletester). */ res = PyType_IsSubtype((PyTypeObject *)err, (PyTypeObject *)exc); 

For reference, the implementation of CPython issubclass, PyObject_IsSubclass , calls __subclasscheck__ before returning to PyType_IsSubtype .

Thus, there is a good reason for this behavior; exception handling should be non-recursive, therefore unsafe for returning it to Python code. Note that Python 2.7 takes an overflow risk and calls PyObject_IsSubclass . There is a suggestion to mitigate this limitation in Python 3, but although the patch has been written, it has not yet been accepted. Otherwise, it would be a good idea for documentation to clarify that except checks are not virtual.

+7
source share

All Articles