blob: d6ffb3a40feb07779140f43f1de11ff80e7c13d7 [file] [log] [blame]
Kevin Chengbb27dba2019-05-02 14:29:30 -07001"""Python Enumerations"""
2
3import sys as _sys
4
5__all__ = ['Enum', 'IntEnum', 'unique']
6
7version = 1, 1, 6
8
9pyver = float('%s.%s' % _sys.version_info[:2])
10
11try:
12 any
13except NameError:
14 def any(iterable):
15 for element in iterable:
16 if element:
17 return True
18 return False
19
20try:
21 from collections import OrderedDict
22except ImportError:
23 OrderedDict = None
24
25try:
26 basestring
27except NameError:
28 # In Python 2 basestring is the ancestor of both str and unicode
29 # in Python 3 it's just str, but was missing in 3.1
30 basestring = str
31
32try:
33 unicode
34except NameError:
35 # In Python 3 unicode no longer exists (it's just str)
36 unicode = str
37
38class _RouteClassAttributeToGetattr(object):
39 """Route attribute access on a class to __getattr__.
40
41 This is a descriptor, used to define attributes that act differently when
42 accessed through an instance and through a class. Instance access remains
43 normal, but access to an attribute through a class will be routed to the
44 class's __getattr__ method; this is done by raising AttributeError.
45
46 """
47 def __init__(self, fget=None):
48 self.fget = fget
49
50 def __get__(self, instance, ownerclass=None):
51 if instance is None:
52 raise AttributeError()
53 return self.fget(instance)
54
55 def __set__(self, instance, value):
56 raise AttributeError("can't set attribute")
57
58 def __delete__(self, instance):
59 raise AttributeError("can't delete attribute")
60
61
62def _is_descriptor(obj):
63 """Returns True if obj is a descriptor, False otherwise."""
64 return (
65 hasattr(obj, '__get__') or
66 hasattr(obj, '__set__') or
67 hasattr(obj, '__delete__'))
68
69
70def _is_dunder(name):
71 """Returns True if a __dunder__ name, False otherwise."""
72 return (name[:2] == name[-2:] == '__' and
73 name[2:3] != '_' and
74 name[-3:-2] != '_' and
75 len(name) > 4)
76
77
78def _is_sunder(name):
79 """Returns True if a _sunder_ name, False otherwise."""
80 return (name[0] == name[-1] == '_' and
81 name[1:2] != '_' and
82 name[-2:-1] != '_' and
83 len(name) > 2)
84
85
86def _make_class_unpicklable(cls):
87 """Make the given class un-picklable."""
88 def _break_on_call_reduce(self, protocol=None):
89 raise TypeError('%r cannot be pickled' % self)
90 cls.__reduce_ex__ = _break_on_call_reduce
91 cls.__module__ = '<unknown>'
92
93
94class _EnumDict(dict):
95 """Track enum member order and ensure member names are not reused.
96
97 EnumMeta will use the names found in self._member_names as the
98 enumeration member names.
99
100 """
101 def __init__(self):
102 super(_EnumDict, self).__init__()
103 self._member_names = []
104
105 def __setitem__(self, key, value):
106 """Changes anything not dundered or not a descriptor.
107
108 If a descriptor is added with the same name as an enum member, the name
109 is removed from _member_names (this may leave a hole in the numerical
110 sequence of values).
111
112 If an enum member name is used twice, an error is raised; duplicate
113 values are not checked for.
114
115 Single underscore (sunder) names are reserved.
116
117 Note: in 3.x __order__ is simply discarded as a not necessary piece
118 leftover from 2.x
119
120 """
121 if pyver >= 3.0 and key in ('_order_', '__order__'):
122 return
123 elif key == '__order__':
124 key = '_order_'
125 if _is_sunder(key):
126 if key != '_order_':
127 raise ValueError('_names_ are reserved for future Enum use')
128 elif _is_dunder(key):
129 pass
130 elif key in self._member_names:
131 # descriptor overwriting an enum?
132 raise TypeError('Attempted to reuse key: %r' % key)
133 elif not _is_descriptor(value):
134 if key in self:
135 # enum overwriting a descriptor?
136 raise TypeError('Key already defined as: %r' % self[key])
137 self._member_names.append(key)
138 super(_EnumDict, self).__setitem__(key, value)
139
140
141# Dummy value for Enum as EnumMeta explicity checks for it, but of course until
142# EnumMeta finishes running the first time the Enum class doesn't exist. This
143# is also why there are checks in EnumMeta like `if Enum is not None`
144Enum = None
145
146
147class EnumMeta(type):
148 """Metaclass for Enum"""
149 @classmethod
150 def __prepare__(metacls, cls, bases):
151 return _EnumDict()
152
153 def __new__(metacls, cls, bases, classdict):
154 # an Enum class is final once enumeration items have been defined; it
155 # cannot be mixed with other types (int, float, etc.) if it has an
156 # inherited __new__ unless a new __new__ is defined (or the resulting
157 # class will fail).
158 if type(classdict) is dict:
159 original_dict = classdict
160 classdict = _EnumDict()
161 for k, v in original_dict.items():
162 classdict[k] = v
163
164 member_type, first_enum = metacls._get_mixins_(bases)
165 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
166 first_enum)
167 # save enum items into separate mapping so they don't get baked into
168 # the new class
169 members = dict((k, classdict[k]) for k in classdict._member_names)
170 for name in classdict._member_names:
171 del classdict[name]
172
173 # py2 support for definition order
174 _order_ = classdict.get('_order_')
175 if _order_ is None:
176 if pyver < 3.0:
177 try:
178 _order_ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])]
179 except TypeError:
180 _order_ = [name for name in sorted(members.keys())]
181 else:
182 _order_ = classdict._member_names
183 else:
184 del classdict['_order_']
185 if pyver < 3.0:
186 _order_ = _order_.replace(',', ' ').split()
187 aliases = [name for name in members if name not in _order_]
188 _order_ += aliases
189
190 # check for illegal enum names (any others?)
191 invalid_names = set(members) & set(['mro'])
192 if invalid_names:
193 raise ValueError('Invalid enum member name(s): %s' % (
194 ', '.join(invalid_names), ))
195
196 # save attributes from super classes so we know if we can take
197 # the shortcut of storing members in the class dict
198 base_attributes = set([a for b in bases for a in b.__dict__])
199 # create our new Enum type
200 enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict)
201 enum_class._member_names_ = [] # names in random order
202 if OrderedDict is not None:
203 enum_class._member_map_ = OrderedDict()
204 else:
205 enum_class._member_map_ = {} # name->value map
206 enum_class._member_type_ = member_type
207
208 # Reverse value->name map for hashable values.
209 enum_class._value2member_map_ = {}
210
211 # instantiate them, checking for duplicates as we go
212 # we instantiate first instead of checking for duplicates first in case
213 # a custom __new__ is doing something funky with the values -- such as
214 # auto-numbering ;)
215 if __new__ is None:
216 __new__ = enum_class.__new__
217 for member_name in _order_:
218 value = members[member_name]
219 if not isinstance(value, tuple):
220 args = (value, )
221 else:
222 args = value
223 if member_type is tuple: # special case for tuple enums
224 args = (args, ) # wrap it one more time
225 if not use_args or not args:
226 enum_member = __new__(enum_class)
227 if not hasattr(enum_member, '_value_'):
228 enum_member._value_ = value
229 else:
230 enum_member = __new__(enum_class, *args)
231 if not hasattr(enum_member, '_value_'):
232 enum_member._value_ = member_type(*args)
233 value = enum_member._value_
234 enum_member._name_ = member_name
235 enum_member.__objclass__ = enum_class
236 enum_member.__init__(*args)
237 # If another member with the same value was already defined, the
238 # new member becomes an alias to the existing one.
239 for name, canonical_member in enum_class._member_map_.items():
240 if canonical_member.value == enum_member._value_:
241 enum_member = canonical_member
242 break
243 else:
244 # Aliases don't appear in member names (only in __members__).
245 enum_class._member_names_.append(member_name)
246 # performance boost for any member that would not shadow
247 # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr)
248 if member_name not in base_attributes:
249 setattr(enum_class, member_name, enum_member)
250 # now add to _member_map_
251 enum_class._member_map_[member_name] = enum_member
252 try:
253 # This may fail if value is not hashable. We can't add the value
254 # to the map, and by-value lookups for this value will be
255 # linear.
256 enum_class._value2member_map_[value] = enum_member
257 except TypeError:
258 pass
259
260
261 # If a custom type is mixed into the Enum, and it does not know how
262 # to pickle itself, pickle.dumps will succeed but pickle.loads will
263 # fail. Rather than have the error show up later and possibly far
264 # from the source, sabotage the pickle protocol for this class so
265 # that pickle.dumps also fails.
266 #
267 # However, if the new class implements its own __reduce_ex__, do not
268 # sabotage -- it's on them to make sure it works correctly. We use
269 # __reduce_ex__ instead of any of the others as it is preferred by
270 # pickle over __reduce__, and it handles all pickle protocols.
271 unpicklable = False
272 if '__reduce_ex__' not in classdict:
273 if member_type is not object:
274 methods = ('__getnewargs_ex__', '__getnewargs__',
275 '__reduce_ex__', '__reduce__')
276 if not any(m in member_type.__dict__ for m in methods):
277 _make_class_unpicklable(enum_class)
278 unpicklable = True
279
280
281 # double check that repr and friends are not the mixin's or various
282 # things break (such as pickle)
283 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
284 class_method = getattr(enum_class, name)
285 obj_method = getattr(member_type, name, None)
286 enum_method = getattr(first_enum, name, None)
287 if name not in classdict and class_method is not enum_method:
288 if name == '__reduce_ex__' and unpicklable:
289 continue
290 setattr(enum_class, name, enum_method)
291
292 # method resolution and int's are not playing nice
293 # Python's less than 2.6 use __cmp__
294
295 if pyver < 2.6:
296
297 if issubclass(enum_class, int):
298 setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))
299
300 elif pyver < 3.0:
301
302 if issubclass(enum_class, int):
303 for method in (
304 '__le__',
305 '__lt__',
306 '__gt__',
307 '__ge__',
308 '__eq__',
309 '__ne__',
310 '__hash__',
311 ):
312 setattr(enum_class, method, getattr(int, method))
313
314 # replace any other __new__ with our own (as long as Enum is not None,
315 # anyway) -- again, this is to support pickle
316 if Enum is not None:
317 # if the user defined their own __new__, save it before it gets
318 # clobbered in case they subclass later
319 if save_new:
320 setattr(enum_class, '__member_new__', enum_class.__dict__['__new__'])
321 setattr(enum_class, '__new__', Enum.__dict__['__new__'])
322 return enum_class
323
324 def __bool__(cls):
325 """
326 classes/types should always be True.
327 """
328 return True
329
330 def __call__(cls, value, names=None, module=None, type=None, start=1):
331 """Either returns an existing member, or creates a new enum class.
332
333 This method is used both when an enum class is given a value to match
334 to an enumeration member (i.e. Color(3)) and for the functional API
335 (i.e. Color = Enum('Color', names='red green blue')).
336
337 When used for the functional API: `module`, if set, will be stored in
338 the new class' __module__ attribute; `type`, if set, will be mixed in
339 as the first base class.
340
341 Note: if `module` is not set this routine will attempt to discover the
342 calling module by walking the frame stack; if this is unsuccessful
343 the resulting class will not be pickleable.
344
345 """
346 if names is None: # simple value lookup
347 return cls.__new__(cls, value)
348 # otherwise, functional API: we're creating a new Enum type
349 return cls._create_(value, names, module=module, type=type, start=start)
350
351 def __contains__(cls, member):
352 return isinstance(member, cls) and member.name in cls._member_map_
353
354 def __delattr__(cls, attr):
355 # nicer error message when someone tries to delete an attribute
356 # (see issue19025).
357 if attr in cls._member_map_:
358 raise AttributeError(
359 "%s: cannot delete Enum member." % cls.__name__)
360 super(EnumMeta, cls).__delattr__(attr)
361
362 def __dir__(self):
363 return (['__class__', '__doc__', '__members__', '__module__'] +
364 self._member_names_)
365
366 @property
367 def __members__(cls):
368 """Returns a mapping of member name->value.
369
370 This mapping lists all enum members, including aliases. Note that this
371 is a copy of the internal mapping.
372
373 """
374 return cls._member_map_.copy()
375
376 def __getattr__(cls, name):
377 """Return the enum member matching `name`
378
379 We use __getattr__ instead of descriptors or inserting into the enum
380 class' __dict__ in order to support `name` and `value` being both
381 properties for enum members (which live in the class' __dict__) and
382 enum members themselves.
383
384 """
385 if _is_dunder(name):
386 raise AttributeError(name)
387 try:
388 return cls._member_map_[name]
389 except KeyError:
390 raise AttributeError(name)
391
392 def __getitem__(cls, name):
393 return cls._member_map_[name]
394
395 def __iter__(cls):
396 return (cls._member_map_[name] for name in cls._member_names_)
397
398 def __reversed__(cls):
399 return (cls._member_map_[name] for name in reversed(cls._member_names_))
400
401 def __len__(cls):
402 return len(cls._member_names_)
403
404 __nonzero__ = __bool__
405
406 def __repr__(cls):
407 return "<enum %r>" % cls.__name__
408
409 def __setattr__(cls, name, value):
410 """Block attempts to reassign Enum members.
411
412 A simple assignment to the class namespace only changes one of the
413 several possible ways to get an Enum member from the Enum class,
414 resulting in an inconsistent Enumeration.
415
416 """
417 member_map = cls.__dict__.get('_member_map_', {})
418 if name in member_map:
419 raise AttributeError('Cannot reassign members.')
420 super(EnumMeta, cls).__setattr__(name, value)
421
422 def _create_(cls, class_name, names=None, module=None, type=None, start=1):
423 """Convenience method to create a new Enum class.
424
425 `names` can be:
426
427 * A string containing member names, separated either with spaces or
428 commas. Values are auto-numbered from 1.
429 * An iterable of member names. Values are auto-numbered from 1.
430 * An iterable of (member name, value) pairs.
431 * A mapping of member name -> value.
432
433 """
434 if pyver < 3.0:
435 # if class_name is unicode, attempt a conversion to ASCII
436 if isinstance(class_name, unicode):
437 try:
438 class_name = class_name.encode('ascii')
439 except UnicodeEncodeError:
440 raise TypeError('%r is not representable in ASCII' % class_name)
441 metacls = cls.__class__
442 if type is None:
443 bases = (cls, )
444 else:
445 bases = (type, cls)
446 classdict = metacls.__prepare__(class_name, bases)
447 _order_ = []
448
449 # special processing needed for names?
450 if isinstance(names, basestring):
451 names = names.replace(',', ' ').split()
452 if isinstance(names, (tuple, list)) and isinstance(names[0], basestring):
453 names = [(e, i+start) for (i, e) in enumerate(names)]
454
455 # Here, names is either an iterable of (name, value) or a mapping.
456 item = None # in case names is empty
457 for item in names:
458 if isinstance(item, basestring):
459 member_name, member_value = item, names[item]
460 else:
461 member_name, member_value = item
462 classdict[member_name] = member_value
463 _order_.append(member_name)
464 # only set _order_ in classdict if name/value was not from a mapping
465 if not isinstance(item, basestring):
466 classdict['_order_'] = ' '.join(_order_)
467 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
468
469 # TODO: replace the frame hack if a blessed way to know the calling
470 # module is ever developed
471 if module is None:
472 try:
473 module = _sys._getframe(2).f_globals['__name__']
474 except (AttributeError, ValueError):
475 pass
476 if module is None:
477 _make_class_unpicklable(enum_class)
478 else:
479 enum_class.__module__ = module
480
481 return enum_class
482
483 @staticmethod
484 def _get_mixins_(bases):
485 """Returns the type for creating enum members, and the first inherited
486 enum class.
487
488 bases: the tuple of bases that was given to __new__
489
490 """
491 if not bases or Enum is None:
492 return object, Enum
493
494
495 # double check that we are not subclassing a class with existing
496 # enumeration members; while we're at it, see if any other data
497 # type has been mixed in so we can use the correct __new__
498 member_type = first_enum = None
499 for base in bases:
500 if (base is not Enum and
501 issubclass(base, Enum) and
502 base._member_names_):
503 raise TypeError("Cannot extend enumerations")
504 # base is now the last base in bases
505 if not issubclass(base, Enum):
506 raise TypeError("new enumerations must be created as "
507 "`ClassName([mixin_type,] enum_type)`")
508
509 # get correct mix-in type (either mix-in type of Enum subclass, or
510 # first base if last base is Enum)
511 if not issubclass(bases[0], Enum):
512 member_type = bases[0] # first data type
513 first_enum = bases[-1] # enum type
514 else:
515 for base in bases[0].__mro__:
516 # most common: (IntEnum, int, Enum, object)
517 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
518 # <class 'int'>, <Enum 'Enum'>,
519 # <class 'object'>)
520 if issubclass(base, Enum):
521 if first_enum is None:
522 first_enum = base
523 else:
524 if member_type is None:
525 member_type = base
526
527 return member_type, first_enum
528
529 if pyver < 3.0:
530 @staticmethod
531 def _find_new_(classdict, member_type, first_enum):
532 """Returns the __new__ to be used for creating the enum members.
533
534 classdict: the class dictionary given to __new__
535 member_type: the data type whose __new__ will be used by default
536 first_enum: enumeration to check for an overriding __new__
537
538 """
539 # now find the correct __new__, checking to see of one was defined
540 # by the user; also check earlier enum classes in case a __new__ was
541 # saved as __member_new__
542 __new__ = classdict.get('__new__', None)
543 if __new__:
544 return None, True, True # __new__, save_new, use_args
545
546 N__new__ = getattr(None, '__new__')
547 O__new__ = getattr(object, '__new__')
548 if Enum is None:
549 E__new__ = N__new__
550 else:
551 E__new__ = Enum.__dict__['__new__']
552 # check all possibles for __member_new__ before falling back to
553 # __new__
554 for method in ('__member_new__', '__new__'):
555 for possible in (member_type, first_enum):
556 try:
557 target = possible.__dict__[method]
558 except (AttributeError, KeyError):
559 target = getattr(possible, method, None)
560 if target not in [
561 None,
562 N__new__,
563 O__new__,
564 E__new__,
565 ]:
566 if method == '__member_new__':
567 classdict['__new__'] = target
568 return None, False, True
569 if isinstance(target, staticmethod):
570 target = target.__get__(member_type)
571 __new__ = target
572 break
573 if __new__ is not None:
574 break
575 else:
576 __new__ = object.__new__
577
578 # if a non-object.__new__ is used then whatever value/tuple was
579 # assigned to the enum member name will be passed to __new__ and to the
580 # new enum member's __init__
581 if __new__ is object.__new__:
582 use_args = False
583 else:
584 use_args = True
585
586 return __new__, False, use_args
587 else:
588 @staticmethod
589 def _find_new_(classdict, member_type, first_enum):
590 """Returns the __new__ to be used for creating the enum members.
591
592 classdict: the class dictionary given to __new__
593 member_type: the data type whose __new__ will be used by default
594 first_enum: enumeration to check for an overriding __new__
595
596 """
597 # now find the correct __new__, checking to see of one was defined
598 # by the user; also check earlier enum classes in case a __new__ was
599 # saved as __member_new__
600 __new__ = classdict.get('__new__', None)
601
602 # should __new__ be saved as __member_new__ later?
603 save_new = __new__ is not None
604
605 if __new__ is None:
606 # check all possibles for __member_new__ before falling back to
607 # __new__
608 for method in ('__member_new__', '__new__'):
609 for possible in (member_type, first_enum):
610 target = getattr(possible, method, None)
611 if target not in (
612 None,
613 None.__new__,
614 object.__new__,
615 Enum.__new__,
616 ):
617 __new__ = target
618 break
619 if __new__ is not None:
620 break
621 else:
622 __new__ = object.__new__
623
624 # if a non-object.__new__ is used then whatever value/tuple was
625 # assigned to the enum member name will be passed to __new__ and to the
626 # new enum member's __init__
627 if __new__ is object.__new__:
628 use_args = False
629 else:
630 use_args = True
631
632 return __new__, save_new, use_args
633
634
635########################################################
636# In order to support Python 2 and 3 with a single
637# codebase we have to create the Enum methods separately
638# and then use the `type(name, bases, dict)` method to
639# create the class.
640########################################################
641temp_enum_dict = {}
642temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n"
643
644def __new__(cls, value):
645 # all enum instances are actually created during class construction
646 # without calling this method; this method is called by the metaclass'
647 # __call__ (i.e. Color(3) ), and by pickle
648 if type(value) is cls:
649 # For lookups like Color(Color.red)
650 value = value.value
651 #return value
652 # by-value search for a matching enum member
653 # see if it's in the reverse mapping (for hashable values)
654 try:
655 if value in cls._value2member_map_:
656 return cls._value2member_map_[value]
657 except TypeError:
658 # not there, now do long search -- O(n) behavior
659 for member in cls._member_map_.values():
660 if member.value == value:
661 return member
662 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
663temp_enum_dict['__new__'] = __new__
664del __new__
665
666def __repr__(self):
667 return "<%s.%s: %r>" % (
668 self.__class__.__name__, self._name_, self._value_)
669temp_enum_dict['__repr__'] = __repr__
670del __repr__
671
672def __str__(self):
673 return "%s.%s" % (self.__class__.__name__, self._name_)
674temp_enum_dict['__str__'] = __str__
675del __str__
676
677if pyver >= 3.0:
678 def __dir__(self):
679 added_behavior = [
680 m
681 for cls in self.__class__.mro()
682 for m in cls.__dict__
683 if m[0] != '_' and m not in self._member_map_
684 ]
685 return (['__class__', '__doc__', '__module__', ] + added_behavior)
686 temp_enum_dict['__dir__'] = __dir__
687 del __dir__
688
689def __format__(self, format_spec):
690 # mixed-in Enums should use the mixed-in type's __format__, otherwise
691 # we can get strange results with the Enum name showing up instead of
692 # the value
693
694 # pure Enum branch
695 if self._member_type_ is object:
696 cls = str
697 val = str(self)
698 # mix-in branch
699 else:
700 cls = self._member_type_
701 val = self.value
702 return cls.__format__(val, format_spec)
703temp_enum_dict['__format__'] = __format__
704del __format__
705
706
707####################################
708# Python's less than 2.6 use __cmp__
709
710if pyver < 2.6:
711
712 def __cmp__(self, other):
713 if type(other) is self.__class__:
714 if self is other:
715 return 0
716 return -1
717 return NotImplemented
718 raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__))
719 temp_enum_dict['__cmp__'] = __cmp__
720 del __cmp__
721
722else:
723
724 def __le__(self, other):
725 raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))
726 temp_enum_dict['__le__'] = __le__
727 del __le__
728
729 def __lt__(self, other):
730 raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))
731 temp_enum_dict['__lt__'] = __lt__
732 del __lt__
733
734 def __ge__(self, other):
735 raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))
736 temp_enum_dict['__ge__'] = __ge__
737 del __ge__
738
739 def __gt__(self, other):
740 raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))
741 temp_enum_dict['__gt__'] = __gt__
742 del __gt__
743
744
745def __eq__(self, other):
746 if type(other) is self.__class__:
747 return self is other
748 return NotImplemented
749temp_enum_dict['__eq__'] = __eq__
750del __eq__
751
752def __ne__(self, other):
753 if type(other) is self.__class__:
754 return self is not other
755 return NotImplemented
756temp_enum_dict['__ne__'] = __ne__
757del __ne__
758
759def __hash__(self):
760 return hash(self._name_)
761temp_enum_dict['__hash__'] = __hash__
762del __hash__
763
764def __reduce_ex__(self, proto):
765 return self.__class__, (self._value_, )
766temp_enum_dict['__reduce_ex__'] = __reduce_ex__
767del __reduce_ex__
768
769# _RouteClassAttributeToGetattr is used to provide access to the `name`
770# and `value` properties of enum members while keeping some measure of
771# protection from modification, while still allowing for an enumeration
772# to have members named `name` and `value`. This works because enumeration
773# members are not set directly on the enum class -- __getattr__ is
774# used to look them up.
775
776@_RouteClassAttributeToGetattr
777def name(self):
778 return self._name_
779temp_enum_dict['name'] = name
780del name
781
782@_RouteClassAttributeToGetattr
783def value(self):
784 return self._value_
785temp_enum_dict['value'] = value
786del value
787
788@classmethod
789def _convert(cls, name, module, filter, source=None):
790 """
791 Create a new Enum subclass that replaces a collection of global constants
792 """
793 # convert all constants from source (or module) that pass filter() to
794 # a new Enum called name, and export the enum and its members back to
795 # module;
796 # also, replace the __reduce_ex__ method so unpickling works in
797 # previous Python versions
798 module_globals = vars(_sys.modules[module])
799 if source:
800 source = vars(source)
801 else:
802 source = module_globals
803 members = dict((name, value) for name, value in source.items() if filter(name))
804 cls = cls(name, members, module=module)
805 cls.__reduce_ex__ = _reduce_ex_by_name
806 module_globals.update(cls.__members__)
807 module_globals[name] = cls
808 return cls
809temp_enum_dict['_convert'] = _convert
810del _convert
811
812Enum = EnumMeta('Enum', (object, ), temp_enum_dict)
813del temp_enum_dict
814
815# Enum has now been created
816###########################
817
818class IntEnum(int, Enum):
819 """Enum where members are also (and must be) ints"""
820
821def _reduce_ex_by_name(self, proto):
822 return self.name
823
824def unique(enumeration):
825 """Class decorator that ensures only unique members exist in an enumeration."""
826 duplicates = []
827 for name, member in enumeration.__members__.items():
828 if name != member.name:
829 duplicates.append((name, member.name))
830 if duplicates:
831 duplicate_names = ', '.join(
832 ["%s -> %s" % (alias, name) for (alias, name) in duplicates]
833 )
834 raise ValueError('duplicate names found in %r: %s' %
835 (enumeration, duplicate_names)
836 )
837 return enumeration