Suppose I've got 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 appears that I can catch ProperSubclass
by MyExceptions
, but not 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 be able to catch built-in exceptions by their abstract base class? If so, how? And if not, what are the rules?
I guess another way of asking this is: do except clauses properly use isinstance()/issubclass() for matching, and if not (as appears to be the case) what do they use? Perhaps there are some shady shortcuts down in the C implementation.
Yes, it will catch exceptions derived from the base.
C++ In Java, catching a base class exception before derived is not allowed by the compiler itself. In C++, the compiler might give a warning about it but compiles the code.
NotImplemented is a special value which should be returned by the binary special methods to indicate that the operation is not implemented with respect to the other type. Raise NotImplementedError to indicate that a super-class method is not implemented and that child classes should implement it.
The Java specification requires that if an exception is thrown, it is either handled by a try/catch statement, or that the function is declared with "throws XYZException". This has the exception of RuntimeException, where it is OK if this is thrown without being caught.
The documentation says:
An object is compatible with an exception if it is the class or a base class of the exception object or a tuple containing an item compatible with the exception.
Unfortunately, this doesn't say whether virtual base classes should be considered, unlike the language for e.g. issubclass:
Return true if class is a subclass (direct, indirect or virtual) of classinfo. [...]
The language on overriding instance and subclass checks doesn't help much either:
The following methods are used to override the default behavior of the
isinstance()
andissubclass()
built-in functions. [...]
In fact, as you have suspected, the CPython implementation (for Python 3) bypasses subclass checks, calling PyType_IsSubtype
directly:
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 CPython implementation of issubclass, PyObject_IsSubclass, calls __subclasscheck__
before falling back to PyType_IsSubtype
.
So there is a good reason for this behavior; exception handling needs to be non-recursive, so it isn't safe for it to call back up into Python code. Note that the Python 2.7 version accepts the risk of overflow and does call PyObject_IsSubclass
. There is a proposal to relax this restriction in Python 3, but although a patch has been written it hasn't yet been accepted. Otherwise, it would be a good idea for the documentation to clarify that except
checks are non-virtual.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With