As I understand it, I can use the abc module in Python to create abstract classes that can't be instantiated (amongst other nice properties). I tried to use this to create a hierarchy of Exception
classes to represent various exit codes for my application, but I'm still able to instantiate my base class, even though I don't want that to happen. Here's some code that demonstrates the problem:
#!/usr/bin/env python3
import abc
class ExitCodeException(Exception):
__metaclass__ = abc.ABCMeta
def __init__(self, message):
super().__init__()
self._message = message
@abc.abstractmethod
def getExitCode(self):
"""Return the exit code for this exception"""
return
class FatalException(ExitCodeException):
def getExitCode(self):
return 1
raise ExitCodeException("Oh no!")
I was expecting my program to quit with an exception saying that ExitCodeException
couldn't be instantiated, but instead I just get the standard stack trace I'd expect if ExitCodeException
weren't abstract:
Traceback (most recent call last)
File "./email2pdf_classexception", line 21, in <module>
raise ExitCodeException("Oh no!")
__main__.ExitCodeException
How can I fix this?
As discussed in the comments by @BartoszKP and @Debanshu Kundu above, it appears the concrete superclass Exception
is what causes the issue here. As such, I've come up with a slightly different pattern which seems to work (as I understand it, this is an older-style of pattern from Python 2, but still seems valid):
#!/usr/bin/env python3
class ExitCodeException(Exception):
def __new__(cls, *args, **kwargs):
if cls is ExitCodeException:
raise NotImplementedError("Base class may not be instantiated")
return Exception.__new__(cls, *args, **kwargs)
def __init__(self, message):
super().__init__()
self._message = message
def getExitCode(self):
"""Return the exit code for this exception"""
return
class FatalException(ExitCodeException):
def getExitCode(self):
return 1
raise FatalException("Oh no!")
This works as intended; if I change the code to instantiate ExitCodeException
directly, it fails.