Can one declare an abstract exception in Python?
Question:
I would like to declare a hierarchy of user-defined exceptions in Python. However, I would like my top-level user-defined class (TransactionException
) to be abstract. That is, I intend TransactionException
to specify methods that its subclasses are required to define. However, TransactionException
should never be instantiated or raised.
I have the following code:
from abc import ABCMeta, abstractmethod
class TransactionException(Exception):
__metaclass__ = ABCMeta
@abstractmethod
def displayErrorMessage(self):
pass
However, the above code allows me to instantiate TransactionException
…
a = TransactionException()
In this case a
is meaningless, and should instead draw an exception. The following code removes the fact that TransactionException
is a subclass of Exception
…
from abc import ABCMeta, abstractmethod
class TransactionException():
__metaclass__ = ABCMeta
@abstractmethod
def displayErrorMessage(self):
pass
This code properly prohibits instantiation but now I cannot raise a subclass of TransactionException
because it’s not an Exception
any longer.
Can one define an abstract exception in Python? If so, how? If not, why not?
NOTE: I’m using Python 2.7, but will happily accept an answer for Python 2.x or Python 3.x.
Answers:
class TransactionException(Exception):
def __init__(self, *args, **kwargs):
raise NotImplementedError('you should not be raising this')
class EverythingLostException(TransactionException):
def __init__(self, msg):
super(TransactionException, self).__init__(msg)
try:
raise EverythingLostException('we are doomed!')
except TransactionException:
print 'check'
try:
raise TransactionException('we are doomed!')
except TransactionException:
print 'oops'
There’s a great answer on this topic by Alex Martelli here. In essence, it comes down how the object initializers (__init__
) of the various base classes (object
, list
, and, I presume, Exception
) behave when abstract methods are present.
When an abstract class inherits from object
(which is the default, if no other base class is given), its __init__
method is set to that of object
‘s, which performs the heavy-lifting in checking if all abstract methods have been implemented.
If the abstract class inherits from a different base class, it will get that class’ __init__
method. Other classes, such as list
and Exception
, it seems, do not check for abstract method implementation, which is why instantiating them is allowed.
The other answer provides a suggested workaround for this. Of course, another option that you have is simply to accept that the abstract class will be instantiable, and try to discourage it.
My implementation for an abstract exception class, in which the children of the class work out of the box.
class TransactionException(Exception):
def __init__(self):
self._check_abstract_initialization(self)
@staticmethod
def _check_abstract_initialization(self):
if type(self) == TransactionException:
raise NotImplementedError("TransactionException should not be instantiated directly")
class AnotherException(TransactionException):
pass
TransactionException() # NotImplementedError: TransactionException should not be instantiated directly
AnotherException # passes
Here’s a helper function that can be used in such scenario:
def validate_abstract_methods(obj):
abstract_methods = []
for name in dir(obj):
value = getattr(obj, name, None)
if value is not None and getattr(value, '__isabstractmethod__', False):
abstract_methods.append(name)
if abstract_methods:
abstract_methods.sort()
raise TypeError(f"Can't instantiate abstract class {obj.__class__.__name__} with abstract methods {', '.join(abstract_methods)}")
This function roughly does the same thing as abc.ABC
class – you just need to call it from your class’ __init__
method.
I would like to declare a hierarchy of user-defined exceptions in Python. However, I would like my top-level user-defined class (TransactionException
) to be abstract. That is, I intend TransactionException
to specify methods that its subclasses are required to define. However, TransactionException
should never be instantiated or raised.
I have the following code:
from abc import ABCMeta, abstractmethod
class TransactionException(Exception):
__metaclass__ = ABCMeta
@abstractmethod
def displayErrorMessage(self):
pass
However, the above code allows me to instantiate TransactionException
…
a = TransactionException()
In this case a
is meaningless, and should instead draw an exception. The following code removes the fact that TransactionException
is a subclass of Exception
…
from abc import ABCMeta, abstractmethod
class TransactionException():
__metaclass__ = ABCMeta
@abstractmethod
def displayErrorMessage(self):
pass
This code properly prohibits instantiation but now I cannot raise a subclass of TransactionException
because it’s not an Exception
any longer.
Can one define an abstract exception in Python? If so, how? If not, why not?
NOTE: I’m using Python 2.7, but will happily accept an answer for Python 2.x or Python 3.x.
class TransactionException(Exception):
def __init__(self, *args, **kwargs):
raise NotImplementedError('you should not be raising this')
class EverythingLostException(TransactionException):
def __init__(self, msg):
super(TransactionException, self).__init__(msg)
try:
raise EverythingLostException('we are doomed!')
except TransactionException:
print 'check'
try:
raise TransactionException('we are doomed!')
except TransactionException:
print 'oops'
There’s a great answer on this topic by Alex Martelli here. In essence, it comes down how the object initializers (__init__
) of the various base classes (object
, list
, and, I presume, Exception
) behave when abstract methods are present.
When an abstract class inherits from object
(which is the default, if no other base class is given), its __init__
method is set to that of object
‘s, which performs the heavy-lifting in checking if all abstract methods have been implemented.
If the abstract class inherits from a different base class, it will get that class’ __init__
method. Other classes, such as list
and Exception
, it seems, do not check for abstract method implementation, which is why instantiating them is allowed.
The other answer provides a suggested workaround for this. Of course, another option that you have is simply to accept that the abstract class will be instantiable, and try to discourage it.
My implementation for an abstract exception class, in which the children of the class work out of the box.
class TransactionException(Exception):
def __init__(self):
self._check_abstract_initialization(self)
@staticmethod
def _check_abstract_initialization(self):
if type(self) == TransactionException:
raise NotImplementedError("TransactionException should not be instantiated directly")
class AnotherException(TransactionException):
pass
TransactionException() # NotImplementedError: TransactionException should not be instantiated directly
AnotherException # passes
Here’s a helper function that can be used in such scenario:
def validate_abstract_methods(obj):
abstract_methods = []
for name in dir(obj):
value = getattr(obj, name, None)
if value is not None and getattr(value, '__isabstractmethod__', False):
abstract_methods.append(name)
if abstract_methods:
abstract_methods.sort()
raise TypeError(f"Can't instantiate abstract class {obj.__class__.__name__} with abstract methods {', '.join(abstract_methods)}")
This function roughly does the same thing as abc.ABC
class – you just need to call it from your class’ __init__
method.