Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
code directory
Project: CubicBraidGroup
Views: 5048"""Python Enumerations"""12import sys as _sys34__all__ = ['Enum', 'IntEnum', 'unique']56version = 1, 1, 278pyver = float('%s.%s' % _sys.version_info[:2])910try:11any12except NameError:13def any(iterable):14for element in iterable:15if element:16return True17return False1819try:20from collections import OrderedDict21except ImportError:22OrderedDict = None2324try:25basestring26except NameError:27# In Python 2 basestring is the ancestor of both str and unicode28# in Python 3 it's just str, but was missing in 3.129basestring = str3031try:32unicode33except NameError:34# In Python 3 unicode no longer exists (it's just str)35unicode = str3637class _RouteClassAttributeToGetattr(object):38"""Route attribute access on a class to __getattr__.3940This is a descriptor, used to define attributes that act differently when41accessed through an instance and through a class. Instance access remains42normal, but access to an attribute through a class will be routed to the43class's __getattr__ method; this is done by raising AttributeError.4445"""46def __init__(self, fget=None):47self.fget = fget4849def __get__(self, instance, ownerclass=None):50if instance is None:51raise AttributeError()52return self.fget(instance)5354def __set__(self, instance, value):55raise AttributeError("can't set attribute")5657def __delete__(self, instance):58raise AttributeError("can't delete attribute")596061def _is_descriptor(obj):62"""Returns True if obj is a descriptor, False otherwise."""63return (64hasattr(obj, '__get__') or65hasattr(obj, '__set__') or66hasattr(obj, '__delete__'))676869def _is_dunder(name):70"""Returns True if a __dunder__ name, False otherwise."""71return (name[:2] == name[-2:] == '__' and72name[2:3] != '_' and73name[-3:-2] != '_' and74len(name) > 4)757677def _is_sunder(name):78"""Returns True if a _sunder_ name, False otherwise."""79return (name[0] == name[-1] == '_' and80name[1:2] != '_' and81name[-2:-1] != '_' and82len(name) > 2)838485def _make_class_unpicklable(cls):86"""Make the given class un-picklable."""87def _break_on_call_reduce(self, protocol=None):88raise TypeError('%r cannot be pickled' % self)89cls.__reduce_ex__ = _break_on_call_reduce90cls.__module__ = '<unknown>'919293class _EnumDict(dict):94"""Track enum member order and ensure member names are not reused.9596EnumMeta will use the names found in self._member_names as the97enumeration member names.9899"""100def __init__(self):101super(_EnumDict, self).__init__()102self._member_names = []103104def __setitem__(self, key, value):105"""Changes anything not dundered or not a descriptor.106107If a descriptor is added with the same name as an enum member, the name108is removed from _member_names (this may leave a hole in the numerical109sequence of values).110111If an enum member name is used twice, an error is raised; duplicate112values are not checked for.113114Single underscore (sunder) names are reserved.115116Note: in 3.x __order__ is simply discarded as a not necessary piece117leftover from 2.x118119"""120if pyver >= 3.0 and key == '__order__':121return122if _is_sunder(key):123raise ValueError('_names_ are reserved for future Enum use')124elif _is_dunder(key):125pass126elif key in self._member_names:127# descriptor overwriting an enum?128raise TypeError('Attempted to reuse key: %r' % key)129elif not _is_descriptor(value):130if key in self:131# enum overwriting a descriptor?132raise TypeError('Key already defined as: %r' % self[key])133self._member_names.append(key)134super(_EnumDict, self).__setitem__(key, value)135136137# Dummy value for Enum as EnumMeta explicity checks for it, but of course until138# EnumMeta finishes running the first time the Enum class doesn't exist. This139# is also why there are checks in EnumMeta like `if Enum is not None`140Enum = None141142143class EnumMeta(type):144"""Metaclass for Enum"""145@classmethod146def __prepare__(metacls, cls, bases):147return _EnumDict()148149def __new__(metacls, cls, bases, classdict):150# an Enum class is final once enumeration items have been defined; it151# cannot be mixed with other types (int, float, etc.) if it has an152# inherited __new__ unless a new __new__ is defined (or the resulting153# class will fail).154if type(classdict) is dict:155original_dict = classdict156classdict = _EnumDict()157for k, v in original_dict.items():158classdict[k] = v159160member_type, first_enum = metacls._get_mixins_(bases)161__new__, save_new, use_args = metacls._find_new_(classdict, member_type,162first_enum)163# save enum items into separate mapping so they don't get baked into164# the new class165members = dict((k, classdict[k]) for k in classdict._member_names)166for name in classdict._member_names:167del classdict[name]168169# py2 support for definition order170__order__ = classdict.get('__order__')171if __order__ is None:172if pyver < 3.0:173try:174__order__ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])]175except TypeError:176__order__ = [name for name in sorted(members.keys())]177else:178__order__ = classdict._member_names179else:180del classdict['__order__']181if pyver < 3.0:182__order__ = __order__.replace(',', ' ').split()183aliases = [name for name in members if name not in __order__]184__order__ += aliases185186# check for illegal enum names (any others?)187invalid_names = set(members) & set(['mro'])188if invalid_names:189raise ValueError('Invalid enum member name(s): %s' % (190', '.join(invalid_names), ))191192# save attributes from super classes so we know if we can take193# the shortcut of storing members in the class dict194base_attributes = set([a for b in bases for a in b.__dict__])195# create our new Enum type196enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict)197enum_class._member_names_ = [] # names in random order198if OrderedDict is not None:199enum_class._member_map_ = OrderedDict()200else:201enum_class._member_map_ = {} # name->value map202enum_class._member_type_ = member_type203204# Reverse value->name map for hashable values.205enum_class._value2member_map_ = {}206207# instantiate them, checking for duplicates as we go208# we instantiate first instead of checking for duplicates first in case209# a custom __new__ is doing something funky with the values -- such as210# auto-numbering ;)211if __new__ is None:212__new__ = enum_class.__new__213for member_name in __order__:214value = members[member_name]215if not isinstance(value, tuple):216args = (value, )217else:218args = value219if member_type is tuple: # special case for tuple enums220args = (args, ) # wrap it one more time221if not use_args or not args:222enum_member = __new__(enum_class)223if not hasattr(enum_member, '_value_'):224enum_member._value_ = value225else:226enum_member = __new__(enum_class, *args)227if not hasattr(enum_member, '_value_'):228enum_member._value_ = member_type(*args)229value = enum_member._value_230enum_member._name_ = member_name231enum_member.__objclass__ = enum_class232enum_member.__init__(*args)233# If another member with the same value was already defined, the234# new member becomes an alias to the existing one.235for name, canonical_member in enum_class._member_map_.items():236if canonical_member.value == enum_member._value_:237enum_member = canonical_member238break239else:240# Aliases don't appear in member names (only in __members__).241enum_class._member_names_.append(member_name)242# performance boost for any member that would not shadow243# a DynamicClassAttribute (aka _RouteClassAttributeToGetattr)244if member_name not in base_attributes:245setattr(enum_class, member_name, enum_member)246# now add to _member_map_247enum_class._member_map_[member_name] = enum_member248try:249# This may fail if value is not hashable. We can't add the value250# to the map, and by-value lookups for this value will be251# linear.252enum_class._value2member_map_[value] = enum_member253except TypeError:254pass255256257# If a custom type is mixed into the Enum, and it does not know how258# to pickle itself, pickle.dumps will succeed but pickle.loads will259# fail. Rather than have the error show up later and possibly far260# from the source, sabotage the pickle protocol for this class so261# that pickle.dumps also fails.262#263# However, if the new class implements its own __reduce_ex__, do not264# sabotage -- it's on them to make sure it works correctly. We use265# __reduce_ex__ instead of any of the others as it is preferred by266# pickle over __reduce__, and it handles all pickle protocols.267unpicklable = False268if '__reduce_ex__' not in classdict:269if member_type is not object:270methods = ('__getnewargs_ex__', '__getnewargs__',271'__reduce_ex__', '__reduce__')272if not any(m in member_type.__dict__ for m in methods):273_make_class_unpicklable(enum_class)274unpicklable = True275276277# double check that repr and friends are not the mixin's or various278# things break (such as pickle)279for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):280class_method = getattr(enum_class, name)281obj_method = getattr(member_type, name, None)282enum_method = getattr(first_enum, name, None)283if name not in classdict and class_method is not enum_method:284if name == '__reduce_ex__' and unpicklable:285continue286setattr(enum_class, name, enum_method)287288# method resolution and int's are not playing nice289# Python's less than 2.6 use __cmp__290291if pyver < 2.6:292293if issubclass(enum_class, int):294setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))295296elif pyver < 3.0:297298if issubclass(enum_class, int):299for method in (300'__le__',301'__lt__',302'__gt__',303'__ge__',304'__eq__',305'__ne__',306'__hash__',307):308setattr(enum_class, method, getattr(int, method))309310# replace any other __new__ with our own (as long as Enum is not None,311# anyway) -- again, this is to support pickle312if Enum is not None:313# if the user defined their own __new__, save it before it gets314# clobbered in case they subclass later315if save_new:316setattr(enum_class, '__member_new__', enum_class.__dict__['__new__'])317setattr(enum_class, '__new__', Enum.__dict__['__new__'])318return enum_class319320def __call__(cls, value, names=None, module=None, type=None, start=1):321"""Either returns an existing member, or creates a new enum class.322323This method is used both when an enum class is given a value to match324to an enumeration member (i.e. Color(3)) and for the functional API325(i.e. Color = Enum('Color', names='red green blue')).326327When used for the functional API: `module`, if set, will be stored in328the new class' __module__ attribute; `type`, if set, will be mixed in329as the first base class.330331Note: if `module` is not set this routine will attempt to discover the332calling module by walking the frame stack; if this is unsuccessful333the resulting class will not be pickleable.334335"""336if names is None: # simple value lookup337return cls.__new__(cls, value)338# otherwise, functional API: we're creating a new Enum type339return cls._create_(value, names, module=module, type=type, start=start)340341def __contains__(cls, member):342return isinstance(member, cls) and member.name in cls._member_map_343344def __delattr__(cls, attr):345# nicer error message when someone tries to delete an attribute346# (see issue19025).347if attr in cls._member_map_:348raise AttributeError(349"%s: cannot delete Enum member." % cls.__name__)350super(EnumMeta, cls).__delattr__(attr)351352def __dir__(self):353return (['__class__', '__doc__', '__members__', '__module__'] +354self._member_names_)355356@property357def __members__(cls):358"""Returns a mapping of member name->value.359360This mapping lists all enum members, including aliases. Note that this361is a copy of the internal mapping.362363"""364return cls._member_map_.copy()365366def __getattr__(cls, name):367"""Return the enum member matching `name`368369We use __getattr__ instead of descriptors or inserting into the enum370class' __dict__ in order to support `name` and `value` being both371properties for enum members (which live in the class' __dict__) and372enum members themselves.373374"""375if _is_dunder(name):376raise AttributeError(name)377try:378return cls._member_map_[name]379except KeyError:380raise AttributeError(name)381382def __getitem__(cls, name):383return cls._member_map_[name]384385def __iter__(cls):386return (cls._member_map_[name] for name in cls._member_names_)387388def __reversed__(cls):389return (cls._member_map_[name] for name in reversed(cls._member_names_))390391def __len__(cls):392return len(cls._member_names_)393394def __repr__(cls):395return "<enum %r>" % cls.__name__396397def __setattr__(cls, name, value):398"""Block attempts to reassign Enum members.399400A simple assignment to the class namespace only changes one of the401several possible ways to get an Enum member from the Enum class,402resulting in an inconsistent Enumeration.403404"""405member_map = cls.__dict__.get('_member_map_', {})406if name in member_map:407raise AttributeError('Cannot reassign members.')408super(EnumMeta, cls).__setattr__(name, value)409410def _create_(cls, class_name, names=None, module=None, type=None, start=1):411"""Convenience method to create a new Enum class.412413`names` can be:414415* A string containing member names, separated either with spaces or416commas. Values are auto-numbered from 1.417* An iterable of member names. Values are auto-numbered from 1.418* An iterable of (member name, value) pairs.419* A mapping of member name -> value.420421"""422if pyver < 3.0:423# if class_name is unicode, attempt a conversion to ASCII424if isinstance(class_name, unicode):425try:426class_name = class_name.encode('ascii')427except UnicodeEncodeError:428raise TypeError('%r is not representable in ASCII' % class_name)429metacls = cls.__class__430if type is None:431bases = (cls, )432else:433bases = (type, cls)434classdict = metacls.__prepare__(class_name, bases)435__order__ = []436437# special processing needed for names?438if isinstance(names, basestring):439names = names.replace(',', ' ').split()440if isinstance(names, (tuple, list)) and isinstance(names[0], basestring):441names = [(e, i+start) for (i, e) in enumerate(names)]442443# Here, names is either an iterable of (name, value) or a mapping.444item = None # in case names is empty445for item in names:446if isinstance(item, basestring):447member_name, member_value = item, names[item]448else:449member_name, member_value = item450classdict[member_name] = member_value451__order__.append(member_name)452# only set __order__ in classdict if name/value was not from a mapping453if not isinstance(item, basestring):454classdict['__order__'] = ' '.join(__order__)455enum_class = metacls.__new__(metacls, class_name, bases, classdict)456457# TODO: replace the frame hack if a blessed way to know the calling458# module is ever developed459if module is None:460try:461module = _sys._getframe(2).f_globals['__name__']462except (AttributeError, ValueError):463pass464if module is None:465_make_class_unpicklable(enum_class)466else:467enum_class.__module__ = module468469return enum_class470471@staticmethod472def _get_mixins_(bases):473"""Returns the type for creating enum members, and the first inherited474enum class.475476bases: the tuple of bases that was given to __new__477478"""479if not bases or Enum is None:480return object, Enum481482483# double check that we are not subclassing a class with existing484# enumeration members; while we're at it, see if any other data485# type has been mixed in so we can use the correct __new__486member_type = first_enum = None487for base in bases:488if (base is not Enum and489issubclass(base, Enum) and490base._member_names_):491raise TypeError("Cannot extend enumerations")492# base is now the last base in bases493if not issubclass(base, Enum):494raise TypeError("new enumerations must be created as "495"`ClassName([mixin_type,] enum_type)`")496497# get correct mix-in type (either mix-in type of Enum subclass, or498# first base if last base is Enum)499if not issubclass(bases[0], Enum):500member_type = bases[0] # first data type501first_enum = bases[-1] # enum type502else:503for base in bases[0].__mro__:504# most common: (IntEnum, int, Enum, object)505# possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,506# <class 'int'>, <Enum 'Enum'>,507# <class 'object'>)508if issubclass(base, Enum):509if first_enum is None:510first_enum = base511else:512if member_type is None:513member_type = base514515return member_type, first_enum516517if pyver < 3.0:518@staticmethod519def _find_new_(classdict, member_type, first_enum):520"""Returns the __new__ to be used for creating the enum members.521522classdict: the class dictionary given to __new__523member_type: the data type whose __new__ will be used by default524first_enum: enumeration to check for an overriding __new__525526"""527# now find the correct __new__, checking to see of one was defined528# by the user; also check earlier enum classes in case a __new__ was529# saved as __member_new__530__new__ = classdict.get('__new__', None)531if __new__:532return None, True, True # __new__, save_new, use_args533534N__new__ = getattr(None, '__new__')535O__new__ = getattr(object, '__new__')536if Enum is None:537E__new__ = N__new__538else:539E__new__ = Enum.__dict__['__new__']540# check all possibles for __member_new__ before falling back to541# __new__542for method in ('__member_new__', '__new__'):543for possible in (member_type, first_enum):544try:545target = possible.__dict__[method]546except (AttributeError, KeyError):547target = getattr(possible, method, None)548if target not in [549None,550N__new__,551O__new__,552E__new__,553]:554if method == '__member_new__':555classdict['__new__'] = target556return None, False, True557if isinstance(target, staticmethod):558target = target.__get__(member_type)559__new__ = target560break561if __new__ is not None:562break563else:564__new__ = object.__new__565566# if a non-object.__new__ is used then whatever value/tuple was567# assigned to the enum member name will be passed to __new__ and to the568# new enum member's __init__569if __new__ is object.__new__:570use_args = False571else:572use_args = True573574return __new__, False, use_args575else:576@staticmethod577def _find_new_(classdict, member_type, first_enum):578"""Returns the __new__ to be used for creating the enum members.579580classdict: the class dictionary given to __new__581member_type: the data type whose __new__ will be used by default582first_enum: enumeration to check for an overriding __new__583584"""585# now find the correct __new__, checking to see of one was defined586# by the user; also check earlier enum classes in case a __new__ was587# saved as __member_new__588__new__ = classdict.get('__new__', None)589590# should __new__ be saved as __member_new__ later?591save_new = __new__ is not None592593if __new__ is None:594# check all possibles for __member_new__ before falling back to595# __new__596for method in ('__member_new__', '__new__'):597for possible in (member_type, first_enum):598target = getattr(possible, method, None)599if target not in (600None,601None.__new__,602object.__new__,603Enum.__new__,604):605__new__ = target606break607if __new__ is not None:608break609else:610__new__ = object.__new__611612# if a non-object.__new__ is used then whatever value/tuple was613# assigned to the enum member name will be passed to __new__ and to the614# new enum member's __init__615if __new__ is object.__new__:616use_args = False617else:618use_args = True619620return __new__, save_new, use_args621622623########################################################624# In order to support Python 2 and 3 with a single625# codebase we have to create the Enum methods separately626# and then use the `type(name, bases, dict)` method to627# create the class.628########################################################629temp_enum_dict = {}630temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n"631632def __new__(cls, value):633# all enum instances are actually created during class construction634# without calling this method; this method is called by the metaclass'635# __call__ (i.e. Color(3) ), and by pickle636if type(value) is cls:637# For lookups like Color(Color.red)638value = value.value639#return value640# by-value search for a matching enum member641# see if it's in the reverse mapping (for hashable values)642try:643if value in cls._value2member_map_:644return cls._value2member_map_[value]645except TypeError:646# not there, now do long search -- O(n) behavior647for member in cls._member_map_.values():648if member.value == value:649return member650raise ValueError("%s is not a valid %s" % (value, cls.__name__))651temp_enum_dict['__new__'] = __new__652del __new__653654def __repr__(self):655return "<%s.%s: %r>" % (656self.__class__.__name__, self._name_, self._value_)657temp_enum_dict['__repr__'] = __repr__658del __repr__659660def __str__(self):661return "%s.%s" % (self.__class__.__name__, self._name_)662temp_enum_dict['__str__'] = __str__663del __str__664665if pyver >= 3.0:666def __dir__(self):667added_behavior = [668m669for cls in self.__class__.mro()670for m in cls.__dict__671if m[0] != '_' and m not in self._member_map_672]673return (['__class__', '__doc__', '__module__', ] + added_behavior)674temp_enum_dict['__dir__'] = __dir__675del __dir__676677def __format__(self, format_spec):678# mixed-in Enums should use the mixed-in type's __format__, otherwise679# we can get strange results with the Enum name showing up instead of680# the value681682# pure Enum branch683if self._member_type_ is object:684cls = str685val = str(self)686# mix-in branch687else:688cls = self._member_type_689val = self.value690return cls.__format__(val, format_spec)691temp_enum_dict['__format__'] = __format__692del __format__693694695####################################696# Python's less than 2.6 use __cmp__697698if pyver < 2.6:699700def __cmp__(self, other):701if type(other) is self.__class__:702if self is other:703return 0704return -1705return NotImplemented706raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__))707temp_enum_dict['__cmp__'] = __cmp__708del __cmp__709710else:711712def __le__(self, other):713raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))714temp_enum_dict['__le__'] = __le__715del __le__716717def __lt__(self, other):718raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))719temp_enum_dict['__lt__'] = __lt__720del __lt__721722def __ge__(self, other):723raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))724temp_enum_dict['__ge__'] = __ge__725del __ge__726727def __gt__(self, other):728raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))729temp_enum_dict['__gt__'] = __gt__730del __gt__731732733def __eq__(self, other):734if type(other) is self.__class__:735return self is other736return NotImplemented737temp_enum_dict['__eq__'] = __eq__738del __eq__739740def __ne__(self, other):741if type(other) is self.__class__:742return self is not other743return NotImplemented744temp_enum_dict['__ne__'] = __ne__745del __ne__746747def __hash__(self):748return hash(self._name_)749temp_enum_dict['__hash__'] = __hash__750del __hash__751752# TODO: enable once Python 3.6 is released753# def __bool__(self):754# return bool(self._value_)755# if pyver < 3.0:756# temp_enum_dict['__nonzero__'] = __bool__757# else:758# temp_enum_dict['__bool__'] = __bool__759# del __bool__760761def __reduce_ex__(self, proto):762return self.__class__, (self._value_, )763temp_enum_dict['__reduce_ex__'] = __reduce_ex__764del __reduce_ex__765766# _RouteClassAttributeToGetattr is used to provide access to the `name`767# and `value` properties of enum members while keeping some measure of768# protection from modification, while still allowing for an enumeration769# to have members named `name` and `value`. This works because enumeration770# members are not set directly on the enum class -- __getattr__ is771# used to look them up.772773@_RouteClassAttributeToGetattr774def name(self):775return self._name_776temp_enum_dict['name'] = name777del name778779@_RouteClassAttributeToGetattr780def value(self):781return self._value_782temp_enum_dict['value'] = value783del value784785@classmethod786def _convert(cls, name, module, filter, source=None):787"""788Create a new Enum subclass that replaces a collection of global constants789"""790# convert all constants from source (or module) that pass filter() to791# a new Enum called name, and export the enum and its members back to792# module;793# also, replace the __reduce_ex__ method so unpickling works in794# previous Python versions795module_globals = vars(_sys.modules[module])796if source:797source = vars(source)798else:799source = module_globals800members = dict((name, value) for name, value in source.items() if filter(name))801cls = cls(name, members, module=module)802cls.__reduce_ex__ = _reduce_ex_by_name803module_globals.update(cls.__members__)804module_globals[name] = cls805return cls806temp_enum_dict['_convert'] = _convert807del _convert808809Enum = EnumMeta('Enum', (object, ), temp_enum_dict)810del temp_enum_dict811812# Enum has now been created813###########################814815class IntEnum(int, Enum):816"""Enum where members are also (and must be) ints"""817818def _reduce_ex_by_name(self, proto):819return self.name820821def unique(enumeration):822"""Class decorator that ensures only unique members exist in an enumeration."""823duplicates = []824for name, member in enumeration.__members__.items():825if name != member.name:826duplicates.append((name, member.name))827if duplicates:828duplicate_names = ', '.join(829["%s -> %s" % (alias, name) for (alias, name) in duplicates]830)831raise ValueError('duplicate names found in %r: %s' %832(enumeration, duplicate_names)833)834return enumeration835836837