0

Thats what i came from at first place:

class DismRestartType(DismEnum):
    DismRestartNo = 0, 'No Restart'
    DismRestartPossible = 1, 'Restart Possible'
    DismRestartRequired = 2, 'Restart Required'

and using it like:

class DismFeatureInfo(DismStructure):
    _pack_ = 4
    _fields_ = [
        ("FeatureName", c_wchar_p),
        ("RestartRequired", DismRestartType)
    ]

class DismEnum(Enum):

    def __new__(cls, value, description):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.description = description
        return obj

Notice the additional parameter for the description. The plan is to display later the description instead of the value, so i don't have to prepare every structure itself.
The problem: I get an error because the structure expect a c type and got an enum.
I did some research and founds this i.e.: Using an IntEnum as the type in a ctypes.Structure._fields_

So i tried:

class CEnumeration(c_int):

    def __new__(cls, value, description):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.description = description
        print("Will never be executed")
        return obj

    def __repr__(self):
        return self.description

Unfortunately i get *** AttributeError: 'DismRestartType' object has no attribute 'description', also the new method never gets executed. Can someone explain me why it's not executed and help me to reach the goal?

EDIT1:
I don't get it! Why does __new__ not executed in TestEnum but gets executed when i inherit from Enum? The metaclass new gets executed.

class PointlessMetaClass(type(c_int)):

    def __new__(meta, classname, bases, classDict):
        cls = type(c_int).__new__(meta, classname, bases, classDict)
        pdb.set_trace()  
        return cls

class TestEnum(metaclass=PointlessMetaClass):
    _type_ = "i"

    def __new__(cls, value):
        print("Why not executed")
        pdb.set_trace()
        return cls


class DismRestartType(TestEnum):
    DismRestartNo = 0, 'No Restart'
    DismRestartPossible = 1, 'Restart Possible'
    DismRestartRequired = 2, 'Restart Required'

Solution: I took long but now I got it:

from ctypes import c_int
from types import DynamicClassAttribute


class _EnumDict(dict):
    """Track enum member order and ensure member names are not reused.

    EnumMeta will use the names found in self._member_names as the
    enumeration member names.

    """
    def __init__(self):
        super().__init__()
        self._member_names = []

    def __setitem__(self, key, value):
        if not isinstance(value, DynamicClassAttribute) and not key.startswith("_"):
            self._member_names.append(key)
        super().__setitem__(key, value)


class EnumerationType(type(c_int)):
    """Metaclass for Enum."""
    @classmethod
    def __prepare__(metacls, cls, bases):
        edict = _EnumDict()
        return edict

    def __new__(metacls, classname, bases, classdict):
        # save enum items into separate mapping so they don't get baked into
        # the new class
        enum_members = {k: classdict[k] for k in classdict._member_names}
        for name in classdict._member_names:
            del classdict[name]

        # returns an instance of the new class, i.e. an instance of my enum
        enum_class = super().__new__(metacls, classname, bases, classdict)
        # Reverse value->name map for hashable values.
        enum_class._value2member_map_ = {}

        for member_name in classdict._member_names:
            value = enum_members[member_name][0]
            enum_member = c_int.__new__(enum_class)
            enum_member.value = value # overwrites the value attr of c_int class
            enum_member._name_ = member_name
            enum_member._description_ = enum_members[member_name][1]
            enum_member.__objclass__ = enum_class
            # i.e DismRestartType.DismRestartNo will return an object instead of the value
            setattr(enum_class, member_name, enum_member)
            # i.e. {'0': <class DismRestartType:DismRestartNo: 0>}
            enum_class._value2member_map_[value] = enum_member

        return enum_class

    def __repr__(self):
        return "<Enumeration %s>" % self.__name__

class CEnumeration(c_int, metaclass=EnumerationType):
    """Generic enumeration.

    Derive from this class to define new enumerations.

    """
    def __new__(cls, value):
        # all enum instances are actually created during class construction
        # without calling this method; this method is called by the metaclass'
        # __call__ (i.e. Color(3) ), and by pickle
        if type(value) is cls:
            # For lookups like Color(Color.RED)
            return value
        # by-value search for a matching enum member
        # see if it's in the reverse mapping (for hashable values)
        try:
            if value in cls._value2member_map_:
                return cls._value2member_map_[value]
        except TypeError:
            pass
        return cls._missing_(value)

    @classmethod
    def _missing_(cls, value):
        raise ValueError("%r is not a valid %s" % (value, cls.__name__))

    # return only description
    def __repr__(self):
        return "<%s.%s: %r>" % (
                self.__class__.__name__, self.name, self.value)

    def __str__(self):
        return "%s.%s" % (self.__class__.__name__, self.name)


    # DynamicClassAttribute is used to provide access to the `name` and
    # `value` properties of enum members while keeping some measure of
    # protection from modification, while still allowing for an enumeration
    # to have members named `name` and `value`.  This works because enumeration
    # members are not set directly on the enum class -- __getattr__ is
    # used to look them up.

    @DynamicClassAttribute
    def name(self):
        """The name of the Enum member."""
        try:
            # get name on instance
            return self._name_
        except AttributeError:
            # get name on class
            return self._value2member_map_[self.value]._name_

    @DynamicClassAttribute
    def description(self):
        """The description of the Enum member."""
        try:
            # get description on instance
            return self._description_
        except AttributeError:
            # get description on class
            return self._value2member_map_[self.value]._description_
Cœur
  • 37,241
  • 25
  • 195
  • 267
FalloutBoy
  • 964
  • 1
  • 9
  • 22
  • The `__new__` method is called by the class' meta class when the class is created, not when an object of the class is created. – thebjorn Sep 19 '17 at 21:58
  • but when i define a metaclass with `__metaclass__ = ` nothing changes? – FalloutBoy Sep 19 '17 at 22:34
  • Yes, metaclasses are complicated. I'd suggest working through some of the documentation on metaclasses so you know where you need to define `__new__` and what it does. – thebjorn Sep 19 '17 at 22:41
  • It doesn't matter where i define it, it gets never executed. I think is has to do with the inheritance `c_int`? – FalloutBoy Sep 19 '17 at 23:28
  • That's simply not true, and you've almost certainly misunderstood something. Check e.g. http://www.voidspace.org.uk/python/articles/metaclasses.shtml and then update your question with what you've tried. – thebjorn Sep 19 '17 at 23:35
  • I updated my post. I know what a metaclass does but there is still a lot of hidden magic that i don't understand – FalloutBoy Sep 20 '17 at 15:47
  • Please move your solution to an answer of its own, thank you. – Cœur Jun 10 '18 at 17:18

0 Answers0