Search code examples
pythonenumsoverridingtypeerrornew-operator

Override __new__ of a class which extends Enum


A class Message extends Enum to add some logic. The two important parameters are verbose level and message string, with other optional messages (*args). Another class MessageError is a special form of the Message class in which verbose level is always zero, everything else is the same.

The following code screams TypeError:

TypeError: Enum.new() takes 2 positional arguments but 3 were given

from enum import Enum


class Message(Enum):
    verbose: int
    messages: list[str]

    def __new__(cls, verbose: int, message: str, *args):
        self = object.__new__(cls)
        self._value_ = message
        return self
    
    def __init__(self, verbose: int, message: str, *args):
        self.verbose = verbose
        self.messages = [self.value] + list(args)


class MessageError(Message):

    def __new__(cls, message: str):
        return super().__new__(cls, 0, message) # <- problem is here!

    def __init__(self, message: str):
        return super().__init__(0, message)


class Info(Message):
    I_001 = 2, 'This is an info'


class Error(MessageError):
    E_001 = 'This is an error'

I was expecting that super().__new__(cls, 0, message) would call __new__ from the Message class, but it seems that is not the case. What am I doing wrong here?


Solution

  • Enums are unusual (aka weird) in several regards, with creation being the biggest area. During enum class creation, after the members themselves have been created but before the class is returned, any existing __new__ is renamed to __new_member__1 and the __new__ from Enum itself is inserted into the class -- this is so calls like Info('This is an error') will return the existing member (or raise), and not create a new member.

    Your MessageError.__new__ should look like:

        def __new__(cls, message: str):
            return super().__new_member__(cls, 0, message)
    

    1 In 3.11+ it's also saved as _new_member_ to follow the pattern of sunder names for enum-specific methods and attributes.


    Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.