| 
									
										
										
										
											1997-08-25 15:37:59 +00:00
										 |  |  | """Enumeration metaclass.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | XXX This is very much a work in progress. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EnumMetaClass: | 
					
						
							|  |  |  |     """Metaclass for enumeration.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     To define your own enumeration, do something like | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class Color(Enum): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         red = 1 | 
					
						
							|  |  |  |         green = 2 | 
					
						
							|  |  |  |         blue = 3 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Now, Color.red, Color.green and Color.blue behave totally | 
					
						
							|  |  |  |     different: they are enumerated values, not integers. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Enumerations cannot be instantiated; however they can be | 
					
						
							|  |  |  |     subclassed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, name, bases, dict): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """Constructor -- create an enumeration.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Called at the end of the class statement.  The arguments are | 
					
						
							|  |  |  |         the name of the new class, a tuple containing the base | 
					
						
							|  |  |  |         classes, and a dictionary containing everything that was | 
					
						
							|  |  |  |         entered in the class' namespace during execution of the class | 
					
						
							|  |  |  |         statement.  In the above example, it would be {'red': 1, | 
					
						
							|  |  |  |         'green': 2, 'blue': 3}. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         for base in bases: | 
					
						
							|  |  |  |             if base.__class__ is not EnumMetaClass: | 
					
						
							|  |  |  |                 raise TypeError, "Enumeration base class must be enumeration" | 
					
						
							|  |  |  |         bases = filter(lambda x: x is not Enum, bases) | 
					
						
							|  |  |  |         self.__name__ = name | 
					
						
							|  |  |  |         self.__bases__ = bases | 
					
						
							|  |  |  |         self.__dict = {} | 
					
						
							|  |  |  |         for key, value in dict.items(): | 
					
						
							|  |  |  |             self.__dict[key] = EnumInstance(name, key, value) | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __getattr__(self, name): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """Return an enumeration value.
 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         For example, Color.red returns the value corresponding to red. | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         XXX Perhaps the values should be created in the constructor? | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         This looks in the class dictionary and if it is not found | 
					
						
							|  |  |  |         there asks the base classes. | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         The special attribute __members__ returns the list of names | 
					
						
							|  |  |  |         defined in this class (it does not merge in the names defined | 
					
						
							|  |  |  |         in base classes). | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if name == '__members__': | 
					
						
							|  |  |  |             return self.__dict.keys() | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             return self.__dict[name] | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             for base in self.__bases__: | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     return getattr(base, name) | 
					
						
							|  |  |  |                 except AttributeError: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         raise AttributeError, name | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         s = self.__name__ | 
					
						
							|  |  |  |         if self.__bases__: | 
					
						
							|  |  |  |             s = s + '(' + string.join(map(lambda x: x.__name__, | 
					
						
							|  |  |  |                                           self.__bases__), ", ") + ')' | 
					
						
							|  |  |  |         if self.__dict: | 
					
						
							|  |  |  |             list = [] | 
					
						
							|  |  |  |             for key, value in self.__dict.items(): | 
					
						
							|  |  |  |                 list.append("%s: %s" % (key, int(value))) | 
					
						
							|  |  |  |             s = "%s: {%s}" % (s, string.join(list, ", ")) | 
					
						
							|  |  |  |         return s | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EnumInstance: | 
					
						
							|  |  |  |     """Class to represent an enumeration value.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves | 
					
						
							|  |  |  |     like the integer 12 when compared, but doesn't support arithmetic. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XXX Should it record the actual enumeration rather than just its | 
					
						
							|  |  |  |     name? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, classname, enumname, value): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         self.__classname = classname | 
					
						
							|  |  |  |         self.__enumname = enumname | 
					
						
							|  |  |  |         self.__value = value | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __int__(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         return self.__value | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         return "EnumInstance(%s, %s, %s)" % (`self.__classname`, | 
					
						
							|  |  |  |                                              `self.__enumname`, | 
					
						
							|  |  |  |                                              `self.__value`) | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         return "%s.%s" % (self.__classname, self.__enumname) | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __cmp__(self, other): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         return cmp(self.__value, int(other)) | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Create the base class for enumerations. | 
					
						
							|  |  |  | # It is an empty enumeration. | 
					
						
							|  |  |  | Enum = EnumMetaClass("Enum", (), {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _test(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class Color(Enum): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         red = 1 | 
					
						
							|  |  |  |         green = 2 | 
					
						
							|  |  |  |         blue = 3 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print Color.red | 
					
						
							|  |  |  |     print dir(Color) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print Color.red == Color.red | 
					
						
							|  |  |  |     print Color.red == Color.blue | 
					
						
							|  |  |  |     print Color.red == 1 | 
					
						
							|  |  |  |     print Color.red == 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class ExtendedColor(Color): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         white = 0 | 
					
						
							|  |  |  |         orange = 4 | 
					
						
							|  |  |  |         yellow = 5 | 
					
						
							|  |  |  |         purple = 6 | 
					
						
							|  |  |  |         black = 7 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print ExtendedColor.orange | 
					
						
							|  |  |  |     print ExtendedColor.red | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print Color.red == ExtendedColor.red | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class OtherColor(Enum): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         white = 4 | 
					
						
							|  |  |  |         blue = 5 | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     class MergedColor(Color, OtherColor): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         pass | 
					
						
							| 
									
										
										
										
											1997-08-23 21:14:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print MergedColor.red | 
					
						
							|  |  |  |     print MergedColor.white | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print Color | 
					
						
							|  |  |  |     print ExtendedColor | 
					
						
							|  |  |  |     print OtherColor | 
					
						
							|  |  |  |     print MergedColor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     _test() |