This should finally fix #6896. Let's watch the buildbots.
[python.git] / Demo / metaclasses / Enum.py
blobdf1d8143e6077cfea8726bcca0067032c180cd36
1 """Enumeration metaclass.
3 XXX This is very much a work in progress.
5 """
7 import string
9 class EnumMetaClass:
10 """Metaclass for enumeration.
12 To define your own enumeration, do something like
14 class Color(Enum):
15 red = 1
16 green = 2
17 blue = 3
19 Now, Color.red, Color.green and Color.blue behave totally
20 different: they are enumerated values, not integers.
22 Enumerations cannot be instantiated; however they can be
23 subclassed.
25 """
27 def __init__(self, name, bases, dict):
28 """Constructor -- create an enumeration.
30 Called at the end of the class statement. The arguments are
31 the name of the new class, a tuple containing the base
32 classes, and a dictionary containing everything that was
33 entered in the class' namespace during execution of the class
34 statement. In the above example, it would be {'red': 1,
35 'green': 2, 'blue': 3}.
37 """
38 for base in bases:
39 if base.__class__ is not EnumMetaClass:
40 raise TypeError, "Enumeration base class must be enumeration"
41 bases = filter(lambda x: x is not Enum, bases)
42 self.__name__ = name
43 self.__bases__ = bases
44 self.__dict = {}
45 for key, value in dict.items():
46 self.__dict[key] = EnumInstance(name, key, value)
48 def __getattr__(self, name):
49 """Return an enumeration value.
51 For example, Color.red returns the value corresponding to red.
53 XXX Perhaps the values should be created in the constructor?
55 This looks in the class dictionary and if it is not found
56 there asks the base classes.
58 The special attribute __members__ returns the list of names
59 defined in this class (it does not merge in the names defined
60 in base classes).
62 """
63 if name == '__members__':
64 return self.__dict.keys()
66 try:
67 return self.__dict[name]
68 except KeyError:
69 for base in self.__bases__:
70 try:
71 return getattr(base, name)
72 except AttributeError:
73 continue
75 raise AttributeError, name
77 def __repr__(self):
78 s = self.__name__
79 if self.__bases__:
80 s = s + '(' + string.join(map(lambda x: x.__name__,
81 self.__bases__), ", ") + ')'
82 if self.__dict:
83 list = []
84 for key, value in self.__dict.items():
85 list.append("%s: %s" % (key, int(value)))
86 s = "%s: {%s}" % (s, string.join(list, ", "))
87 return s
90 class EnumInstance:
91 """Class to represent an enumeration value.
93 EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
94 like the integer 12 when compared, but doesn't support arithmetic.
96 XXX Should it record the actual enumeration rather than just its
97 name?
99 """
101 def __init__(self, classname, enumname, value):
102 self.__classname = classname
103 self.__enumname = enumname
104 self.__value = value
106 def __int__(self):
107 return self.__value
109 def __repr__(self):
110 return "EnumInstance(%r, %r, %r)" % (self.__classname,
111 self.__enumname,
112 self.__value)
114 def __str__(self):
115 return "%s.%s" % (self.__classname, self.__enumname)
117 def __cmp__(self, other):
118 return cmp(self.__value, int(other))
121 # Create the base class for enumerations.
122 # It is an empty enumeration.
123 Enum = EnumMetaClass("Enum", (), {})
126 def _test():
128 class Color(Enum):
129 red = 1
130 green = 2
131 blue = 3
133 print Color.red
134 print dir(Color)
136 print Color.red == Color.red
137 print Color.red == Color.blue
138 print Color.red == 1
139 print Color.red == 2
141 class ExtendedColor(Color):
142 white = 0
143 orange = 4
144 yellow = 5
145 purple = 6
146 black = 7
148 print ExtendedColor.orange
149 print ExtendedColor.red
151 print Color.red == ExtendedColor.red
153 class OtherColor(Enum):
154 white = 4
155 blue = 5
157 class MergedColor(Color, OtherColor):
158 pass
160 print MergedColor.red
161 print MergedColor.white
163 print Color
164 print ExtendedColor
165 print OtherColor
166 print MergedColor
168 if __name__ == '__main__':
169 _test()