1

I'm trying to create a custom base class for exceptions raised within my application. I want to make it an abstract class (using the abc module) that cannot be directly instantiated, to force myself to define more specific concrete subclasses for different types of error situations.

I can define an abstract class that inherits from a custom concrete class. However, to my surprise, if I make the abstract class inherit (directly or indirectly) from Exception, it can again be instantiated directly, defeating the purpose of making it abstract in the first place.

I want to understand what's happening here so that I can make my custom exception class abstract for real and not directly instantiable.

I've tried different variations of declaring my custom exception class as abstract, including using the metaclass=abc.ABCMeta syntax as well as inheriting from abc.ABC. Regardless of the way it is declared as abstract, it ceases to behave like an abstract class as soon as I make it inherit from Exception.

Relevant Python versions for me are 3.5, 3.6 and 3.7. I've tested the below code on Python 3.5.2 (Ubuntu 16.04) and 3.6.8 (Ubuntu 18.04).

The following seems to work as expected: instantiating AppException fails because of the abstract method (TypeError: Can't instantiate abstract class AppException with abstract methods abs_method).

Note that although the classes are called *Exception, they are not (yet) inheriting from the real built-in Exception class.

import abc

class BaseException():
    pass

class AppException(BaseException, abc.ABC):

    @abc.abstractmethod
    def abs_method(self):
        pass

class ConcreteException(AppException):

    def abs_method(self):
        return "concrete method"

# BaseException can be instantiated just fine
a = BaseException()

# ConcreteException can be instantiated just fine
c = ConcreteException()

# It shouldn't be possible to instantiate AppException directly,
# so this line should raise a TypeError
b = AppException()

When I change the definition of BaseException to inherit from the actual Exception class:

class BaseException(Exception):
    pass

then the TypeError goes away, so the instantiation of AppException did work this time. AppException is no longer behaving as an abstract class, even though in my understanding, it should.

Here is the actual code, currently stuck as a draft PR until I can figure out what's going on.

1 Answers1

1

This was covered earlier in this SO discussion.

There is no obvious solution, but one possible workaround (from the above SO discussion) is to add an __init__ method into the "abstract" extension class that prevents it from being instantiated directly.