Public feed
#376636
# -*- 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__`