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