Home | History | Annotate | Download | only in newmetaclasses
      1 """Enumeration metaclass."""
      2 
      3 class EnumMetaclass(type):
      4     """Metaclass for enumeration.
      5 
      6     To define your own enumeration, do something like
      7 
      8     class Color(Enum):
      9         red = 1
     10         green = 2
     11         blue = 3
     12 
     13     Now, Color.red, Color.green and Color.blue behave totally
     14     different: they are enumerated values, not integers.
     15 
     16     Enumerations cannot be instantiated; however they can be
     17     subclassed.
     18     """
     19 
     20     def __init__(cls, name, bases, dict):
     21         super(EnumMetaclass, cls).__init__(name, bases, dict)
     22         cls._members = []
     23         for attr in dict.keys():
     24             if not (attr.startswith('__') and attr.endswith('__')):
     25                 enumval = EnumInstance(name, attr, dict[attr])
     26                 setattr(cls, attr, enumval)
     27                 cls._members.append(attr)
     28 
     29     def __getattr__(cls, name):
     30         if name == "__members__":
     31             return cls._members
     32         raise AttributeError, name
     33 
     34     def __repr__(cls):
     35         s1 = s2 = ""
     36         enumbases = [base.__name__ for base in cls.__bases__
     37                      if isinstance(base, EnumMetaclass) and not base is Enum]
     38         if enumbases:
     39             s1 = "(%s)" % ", ".join(enumbases)
     40         enumvalues = ["%s: %d" % (val, getattr(cls, val))
     41                       for val in cls._members]
     42         if enumvalues:
     43             s2 = ": {%s}" % ", ".join(enumvalues)
     44         return "%s%s%s" % (cls.__name__, s1, s2)
     45 
     46 class FullEnumMetaclass(EnumMetaclass):
     47     """Metaclass for full enumerations.
     48 
     49     A full enumeration displays all the values defined in base classes.
     50     """
     51 
     52     def __init__(cls, name, bases, dict):
     53         super(FullEnumMetaclass, cls).__init__(name, bases, dict)
     54         for obj in cls.__mro__:
     55             if isinstance(obj, EnumMetaclass):
     56                 for attr in obj._members:
     57                     # XXX inefficient
     58                     if not attr in cls._members:
     59                         cls._members.append(attr)
     60 
     61 class EnumInstance(int):
     62     """Class to represent an enumeration value.
     63 
     64     EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
     65     like the integer 12 when compared, but doesn't support arithmetic.
     66 
     67     XXX Should it record the actual enumeration rather than just its
     68     name?
     69     """
     70 
     71     def __new__(cls, classname, enumname, value):
     72         return int.__new__(cls, value)
     73 
     74     def __init__(self, classname, enumname, value):
     75         self.__classname = classname
     76         self.__enumname = enumname
     77 
     78     def __repr__(self):
     79         return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname,
     80                                              self)
     81 
     82     def __str__(self):
     83         return "%s.%s" % (self.__classname, self.__enumname)
     84 
     85 class Enum:
     86     __metaclass__ = EnumMetaclass
     87 
     88 class FullEnum:
     89     __metaclass__ = FullEnumMetaclass
     90 
     91 def _test():
     92 
     93     class Color(Enum):
     94         red = 1
     95         green = 2
     96         blue = 3
     97 
     98     print Color.red
     99 
    100     print repr(Color.red)
    101     print Color.red == Color.red
    102     print Color.red == Color.blue
    103     print Color.red == 1
    104     print Color.red == 2
    105 
    106     class ExtendedColor(Color):
    107         white = 0
    108         orange = 4
    109         yellow = 5
    110         purple = 6
    111         black = 7
    112 
    113     print ExtendedColor.orange
    114     print ExtendedColor.red
    115 
    116     print Color.red == ExtendedColor.red
    117 
    118     class OtherColor(Enum):
    119         white = 4
    120         blue = 5
    121 
    122     class MergedColor(Color, OtherColor):
    123         pass
    124 
    125     print MergedColor.red
    126     print MergedColor.white
    127 
    128     print Color
    129     print ExtendedColor
    130     print OtherColor
    131     print MergedColor
    132 
    133 def _test2():
    134 
    135     class Color(FullEnum):
    136         red = 1
    137         green = 2
    138         blue = 3
    139 
    140     print Color.red
    141 
    142     print repr(Color.red)
    143     print Color.red == Color.red
    144     print Color.red == Color.blue
    145     print Color.red == 1
    146     print Color.red == 2
    147 
    148     class ExtendedColor(Color):
    149         white = 0
    150         orange = 4
    151         yellow = 5
    152         purple = 6
    153         black = 7
    154 
    155     print ExtendedColor.orange
    156     print ExtendedColor.red
    157 
    158     print Color.red == ExtendedColor.red
    159 
    160     class OtherColor(FullEnum):
    161         white = 4
    162         blue = 5
    163 
    164     class MergedColor(Color, OtherColor):
    165         pass
    166 
    167     print MergedColor.red
    168     print MergedColor.white
    169 
    170     print Color
    171     print ExtendedColor
    172     print OtherColor
    173     print MergedColor
    174 
    175 if __name__ == '__main__':
    176     _test()
    177     _test2()
    178