Public feed #376636

astronouth7303

public python v1 · immutable
Raw Edit
·published 2007-03-01 04:30 UTC
rendered paste body
# -*- coding: utf-8 -*-"""Defines the Enum class used by wxlcars."""__all__ = 'Enum',class Enum(object):	"""	Superclass for enums. Behaviors:	* Not mutually exlusive (bitmaskable)	* Singletons (can't make new values after construction)	* Aliasable (two enum elements may have same value)	* Immutable, both Enum and items	* Inheritable & overridable items, including iter() and len()		If you want to change how the values behave, just define the values in 	your class.		At this time, Enum inherits from int. All operations return integers, 	not combined flag objects or anything. This may change in the future in	that combined flags will be represented as iterables and Enum objects	will be opaque.	"""	class __metaclass__(type):		## Note that this class doesn't call super() anywhere. Why? It's a pain to get at the class object!				## Holds the value instances		## Indirectly accessable by dict(Enum)		__values = None		def __new__(mcls, name, bases, dic):			items = {}			docs = {}			if '__values__' in dic and hasattr(dic['__values__'], '__iter__'):				__values__ = dic['__values__']				if isinstance(__values__, dict):					__values__ = __values__.items()				keys = []				values = []				## Inherit values				if name != 'Enum': ## Shouldn't be necessary					for sub in bases:						if issubclass(sub, Enum):							#print "inheriting", dict(sub)							keys += dict(sub).keys()							values += dict(sub).values()				#print "keys, values (1)", name, keys, values				for k in __values__:					v = None					d = None					if hasattr(k, '__iter__'):						k = list(k)						if len(k) == 2: 							k,v = k							if hasattr(v, '__iter__'):								v,d = v							else:								raise ValueError						elif len(k) == 3:							k,v,d = k						else:							raise ValueError					keys += [k]					values += [v]					if d is not None: docs[k] = d				#print "keys, values (2)", name, keys, values				val = 0				for i,(k,v) in enumerate(zip(keys, values)):					#assert v is not None and values.count(v) == 1					#print "value1", v					if v is None:						v = val						values[i] = v						## Skip values in the enum						while v in values:							v += 1						## And increment val (skipping used values)						val = v + 1					#print "value2", v					items[k] = v					dic[k] = None ## Placeholder				#print "items", name, items			elif name != 'Enum': ## If not the parent class				raise Exception, """Enum classes must have iterable __value__."""			## No need to handle slots because the new attributes are in the class, not the instance			cls = type.__new__(mcls, name, bases, dic)			if name != 'Enum':				cls.__inited = False				fvals = {}				for k, v in items.iteritems():					if not isinstance(v, Enum):						fv = cls(k, v)						if k in docs:							fv.__doc__ = docs[k]						#print "Created:",name,					else:						fv = v						#print "Inherited:",type(v).__name__,					#print "k,v,fv", k, `v`, `fv`					setattr(cls, k, fv)					fvals[k] = fv				cls.__values = fvals				cls.__inited = True				#print "before/after values", name, items, fvals			else:				cls.__values = {}				cls.__inited = True			#print "values", name, cls.__values			return cls		def __iter__(cls):			return cls.__values.iteritems()		def __len__(cls):			return len(cls.__values)		def __contains__(cls, other):			"""			True if other is a value of this enum or if other is a string			of the name of a value.			"""			return other in cls.__values.values() or \				(isinstance(other, basestring) and other in cls.__values)		def __getitem__(cls, i):			return cls.__values[i]#		def __getattr__(cls, name):#			if name in cls.__values:#				return cls.__values[name]#			else:#				return type.__getattr__(name)		def __repr__(cls):			global Enum ## To reference the 			if cls is not Enum:				return "<enum %r values %s>" % (cls.__module__+'.'+cls.__name__, ', '.join(("%s=%s" % i for i in cls.__values.iteritems())))			else:				return type.__repr__(cls)		def __call__(cls, name, val=None):			if not cls.__inited: ## Initializing the values				return type.__call__(cls, name, val)			else:				if val in cls.__values:					return cls.__values[val]				else:					raise Exception, """Can't add new values to an enum"""						__slots__ = ("__name", "__value")	name = property((lambda self: self.__name), doc="""The name this value is accessable by.""")	value = property((lambda self: self.__value), doc="""The alternate value used for conversion and comparision.""")	def __new__(cls, name, value=None):		self = super(Enum, cls).__new__(cls)		self.__name = name		self.__value = value		return self	def __repr__(self):		return "%s(%r, %r)" % (self.__class__.__name__, self.__name, self.__value)	def __str__(self):		return str(self.__value)	def __unicode__(self):		return unicode(self.__value)	def __hash__(self):		return hash(self.__name) ^ hash(self.__value)	def __cmp__(self, other):		"""		Performs comparision. May compare to any sub- or super-class.		Compares by value by default, but falls back to ID otherwise.		"""		t1, t2 = type(self), type(other)		if not (issubclass(t1, t2) or issubclass(t1, t2)): 			return NotImplemented		if (self.__value is None) or (other.__value is None):			## unsortable, but unequal			return cmp(id(self), id(other))		else:			return cmp(self.__value, other.__value)	if __name__ == '__main__':	## Test the enum class	class MyEnum(Enum):		__values__ = 'foo', 'bar', 'baz'	class oenum(MyEnum):		__values__ = ('quux', 42, "A random flag"),	help(oenum.quux)	print "Docstring:", `oenum.quux.__doc__`