blob: 5be13d5c71ab1e7e22aea75c42dc0dade0d0e27d [file] [log] [blame]
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001import sys
2from collections import OrderedDict
Ethan Furmane03ea372013-09-25 07:14:41 -07003from types import MappingProxyType, DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -07004
Ethan Furmanf24bb352013-07-18 17:05:39 -07005__all__ = ['Enum', 'IntEnum', 'unique']
Ethan Furman6b3d64a2013-06-14 16:55:46 -07006
7
Ethan Furman101e0742013-09-15 12:34:36 -07008def _is_descriptor(obj):
9 """Returns True if obj is a descriptor, False otherwise."""
10 return (
11 hasattr(obj, '__get__') or
12 hasattr(obj, '__set__') or
13 hasattr(obj, '__delete__'))
14
15
Ethan Furman6b3d64a2013-06-14 16:55:46 -070016def _is_dunder(name):
17 """Returns True if a __dunder__ name, False otherwise."""
18 return (name[:2] == name[-2:] == '__' and
19 name[2:3] != '_' and
Ethan Furman648f8602013-10-06 17:19:54 -070020 name[-3:-2] != '_' and
21 len(name) > 4)
Ethan Furman6b3d64a2013-06-14 16:55:46 -070022
23
24def _is_sunder(name):
25 """Returns True if a _sunder_ name, False otherwise."""
26 return (name[0] == name[-1] == '_' and
27 name[1:2] != '_' and
Ethan Furman648f8602013-10-06 17:19:54 -070028 name[-2:-1] != '_' and
29 len(name) > 2)
Ethan Furman6b3d64a2013-06-14 16:55:46 -070030
31
32def _make_class_unpicklable(cls):
33 """Make the given class un-picklable."""
Ethan Furmanca1b7942014-02-08 11:36:27 -080034 def _break_on_call_reduce(self, proto):
Ethan Furman6b3d64a2013-06-14 16:55:46 -070035 raise TypeError('%r cannot be pickled' % self)
Ethan Furmanca1b7942014-02-08 11:36:27 -080036 cls.__reduce_ex__ = _break_on_call_reduce
Ethan Furman6b3d64a2013-06-14 16:55:46 -070037 cls.__module__ = '<unknown>'
38
Ethan Furman101e0742013-09-15 12:34:36 -070039
Ethan Furman6b3d64a2013-06-14 16:55:46 -070040class _EnumDict(dict):
Ethan Furman101e0742013-09-15 12:34:36 -070041 """Track enum member order and ensure member names are not reused.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070042
43 EnumMeta will use the names found in self._member_names as the
44 enumeration member names.
45
46 """
47 def __init__(self):
48 super().__init__()
49 self._member_names = []
50
51 def __setitem__(self, key, value):
Ethan Furman101e0742013-09-15 12:34:36 -070052 """Changes anything not dundered or not a descriptor.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070053
54 If an enum member name is used twice, an error is raised; duplicate
55 values are not checked for.
56
57 Single underscore (sunder) names are reserved.
58
59 """
60 if _is_sunder(key):
61 raise ValueError('_names_ are reserved for future Enum use')
Ethan Furman101e0742013-09-15 12:34:36 -070062 elif _is_dunder(key):
63 pass
64 elif key in self._member_names:
65 # descriptor overwriting an enum?
66 raise TypeError('Attempted to reuse key: %r' % key)
67 elif not _is_descriptor(value):
68 if key in self:
69 # enum overwriting a descriptor?
70 raise TypeError('Key already defined as: %r' % self[key])
Ethan Furman6b3d64a2013-06-14 16:55:46 -070071 self._member_names.append(key)
72 super().__setitem__(key, value)
73
74
Ethan Furman101e0742013-09-15 12:34:36 -070075
Ezio Melotti9a3777e2013-08-17 15:53:55 +030076# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
77# until EnumMeta finishes running the first time the Enum class doesn't exist.
78# This is also why there are checks in EnumMeta like `if Enum is not None`
Ethan Furman6b3d64a2013-06-14 16:55:46 -070079Enum = None
80
81
82class EnumMeta(type):
83 """Metaclass for Enum"""
84 @classmethod
85 def __prepare__(metacls, cls, bases):
86 return _EnumDict()
87
88 def __new__(metacls, cls, bases, classdict):
89 # an Enum class is final once enumeration items have been defined; it
90 # cannot be mixed with other types (int, float, etc.) if it has an
91 # inherited __new__ unless a new __new__ is defined (or the resulting
92 # class will fail).
93 member_type, first_enum = metacls._get_mixins_(bases)
94 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
95 first_enum)
96
97 # save enum items into separate mapping so they don't get baked into
98 # the new class
99 members = {k: classdict[k] for k in classdict._member_names}
100 for name in classdict._member_names:
101 del classdict[name]
102
103 # check for illegal enum names (any others?)
104 invalid_names = set(members) & {'mro', }
105 if invalid_names:
106 raise ValueError('Invalid enum member name: {0}'.format(
107 ','.join(invalid_names)))
108
109 # create our new Enum type
110 enum_class = super().__new__(metacls, cls, bases, classdict)
Ethan Furman520ad572013-07-19 19:47:21 -0700111 enum_class._member_names_ = [] # names in definition order
112 enum_class._member_map_ = OrderedDict() # name->value map
Ethan Furman5e5a8232013-08-04 08:42:23 -0700113 enum_class._member_type_ = member_type
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700114
Ethan Furman354ecf12015-03-11 08:43:12 -0700115 # save attributes from super classes so we know if we can take
116 # the shortcut of storing members in the class dict
117 base_attributes = {a for b in bases for a in b.__dict__}
118
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700119 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700120 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700121
Ethan Furman2da95042014-03-03 12:42:52 -0800122 # If a custom type is mixed into the Enum, and it does not know how
123 # to pickle itself, pickle.dumps will succeed but pickle.loads will
124 # fail. Rather than have the error show up later and possibly far
125 # from the source, sabotage the pickle protocol for this class so
126 # that pickle.dumps also fails.
127 #
128 # However, if the new class implements its own __reduce_ex__, do not
129 # sabotage -- it's on them to make sure it works correctly. We use
130 # __reduce_ex__ instead of any of the others as it is preferred by
131 # pickle over __reduce__, and it handles all pickle protocols.
132 if '__reduce_ex__' not in classdict:
Ethan Furmandc870522014-02-18 12:37:12 -0800133 if member_type is not object:
134 methods = ('__getnewargs_ex__', '__getnewargs__',
135 '__reduce_ex__', '__reduce__')
Ethan Furman2da95042014-03-03 12:42:52 -0800136 if not any(m in member_type.__dict__ for m in methods):
Ethan Furmandc870522014-02-18 12:37:12 -0800137 _make_class_unpicklable(enum_class)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700138
139 # instantiate them, checking for duplicates as we go
140 # we instantiate first instead of checking for duplicates first in case
141 # a custom __new__ is doing something funky with the values -- such as
142 # auto-numbering ;)
143 for member_name in classdict._member_names:
144 value = members[member_name]
145 if not isinstance(value, tuple):
146 args = (value, )
147 else:
148 args = value
149 if member_type is tuple: # special case for tuple enums
150 args = (args, ) # wrap it one more time
151 if not use_args:
152 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700153 if not hasattr(enum_member, '_value_'):
154 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700155 else:
156 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700157 if not hasattr(enum_member, '_value_'):
158 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700159 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700160 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700161 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700162 enum_member.__init__(*args)
163 # If another member with the same value was already defined, the
164 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700165 for name, canonical_member in enum_class._member_map_.items():
Ethan Furman0081f232014-09-16 17:31:23 -0700166 if canonical_member._value_ == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700167 enum_member = canonical_member
168 break
169 else:
170 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700171 enum_class._member_names_.append(member_name)
Ethan Furman354ecf12015-03-11 08:43:12 -0700172 # performance boost for any member that would not shadow
173 # a DynamicClassAttribute
174 if member_name not in base_attributes:
175 setattr(enum_class, member_name, enum_member)
176 # now add to _member_map_
Ethan Furman520ad572013-07-19 19:47:21 -0700177 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700178 try:
179 # This may fail if value is not hashable. We can't add the value
180 # to the map, and by-value lookups for this value will be
181 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700182 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700183 except TypeError:
184 pass
185
186 # double check that repr and friends are not the mixin's or various
187 # things break (such as pickle)
Ethan Furmandc870522014-02-18 12:37:12 -0800188 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700189 class_method = getattr(enum_class, name)
190 obj_method = getattr(member_type, name, None)
191 enum_method = getattr(first_enum, name, None)
192 if obj_method is not None and obj_method is class_method:
193 setattr(enum_class, name, enum_method)
194
195 # replace any other __new__ with our own (as long as Enum is not None,
196 # anyway) -- again, this is to support pickle
197 if Enum is not None:
198 # if the user defined their own __new__, save it before it gets
199 # clobbered in case they subclass later
200 if save_new:
201 enum_class.__new_member__ = __new__
202 enum_class.__new__ = Enum.__new__
203 return enum_class
204
Ethan Furmand9925a12014-09-16 20:35:55 -0700205 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700206 """Either returns an existing member, or creates a new enum class.
207
208 This method is used both when an enum class is given a value to match
209 to an enumeration member (i.e. Color(3)) and for the functional API
210 (i.e. Color = Enum('Color', names='red green blue')).
211
Ethan Furman2da95042014-03-03 12:42:52 -0800212 When used for the functional API:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700213
Ethan Furman2da95042014-03-03 12:42:52 -0800214 `value` will be the name of the new class.
215
216 `names` should be either a string of white-space/comma delimited names
Ethan Furmand9925a12014-09-16 20:35:55 -0700217 (values will start at `start`), or an iterator/mapping of name, value pairs.
Ethan Furman2da95042014-03-03 12:42:52 -0800218
219 `module` should be set to the module this class is being created in;
220 if it is not set, an attempt to find that module will be made, but if
221 it fails the class will not be picklable.
222
223 `qualname` should be set to the actual location this class can be found
224 at in its module; by default it is set to the global scope. If this is
225 not correct, unpickling will fail in some circumstances.
226
227 `type`, if set, will be mixed in as the first base class.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700228
229 """
230 if names is None: # simple value lookup
231 return cls.__new__(cls, value)
232 # otherwise, functional API: we're creating a new Enum type
Ethan Furmand9925a12014-09-16 20:35:55 -0700233 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700234
235 def __contains__(cls, member):
Ethan Furman0081f232014-09-16 17:31:23 -0700236 return isinstance(member, cls) and member._name_ in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700237
Ethan Furman64a99722013-09-22 16:18:19 -0700238 def __delattr__(cls, attr):
239 # nicer error message when someone tries to delete an attribute
240 # (see issue19025).
241 if attr in cls._member_map_:
242 raise AttributeError(
243 "%s: cannot delete Enum member." % cls.__name__)
244 super().__delattr__(attr)
245
Ethan Furman388a3922013-08-12 06:51:41 -0700246 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700247 return (['__class__', '__doc__', '__members__', '__module__'] +
248 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700249
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700250 def __getattr__(cls, name):
251 """Return the enum member matching `name`
252
253 We use __getattr__ instead of descriptors or inserting into the enum
254 class' __dict__ in order to support `name` and `value` being both
255 properties for enum members (which live in the class' __dict__) and
256 enum members themselves.
257
258 """
259 if _is_dunder(name):
260 raise AttributeError(name)
261 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700262 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700263 except KeyError:
264 raise AttributeError(name) from None
265
266 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700267 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700268
269 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700270 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700271
272 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700273 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700274
Ethan Furman2131a4a2013-09-14 18:11:24 -0700275 @property
276 def __members__(cls):
277 """Returns a mapping of member name->value.
278
279 This mapping lists all enum members, including aliases. Note that this
280 is a read-only view of the internal mapping.
281
282 """
283 return MappingProxyType(cls._member_map_)
284
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700285 def __repr__(cls):
286 return "<enum %r>" % cls.__name__
287
Ethan Furman2131a4a2013-09-14 18:11:24 -0700288 def __reversed__(cls):
289 return (cls._member_map_[name] for name in reversed(cls._member_names_))
290
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700291 def __setattr__(cls, name, value):
292 """Block attempts to reassign Enum members.
293
294 A simple assignment to the class namespace only changes one of the
295 several possible ways to get an Enum member from the Enum class,
296 resulting in an inconsistent Enumeration.
297
298 """
299 member_map = cls.__dict__.get('_member_map_', {})
300 if name in member_map:
301 raise AttributeError('Cannot reassign members.')
302 super().__setattr__(name, value)
303
Ethan Furmand9925a12014-09-16 20:35:55 -0700304 def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700305 """Convenience method to create a new Enum class.
306
307 `names` can be:
308
309 * A string containing member names, separated either with spaces or
Ethan Furmand9925a12014-09-16 20:35:55 -0700310 commas. Values are incremented by 1 from `start`.
311 * An iterable of member names. Values are incremented by 1 from `start`.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700312 * An iterable of (member name, value) pairs.
Ethan Furmand9925a12014-09-16 20:35:55 -0700313 * A mapping of member name -> value pairs.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700314
315 """
316 metacls = cls.__class__
317 bases = (cls, ) if type is None else (type, cls)
318 classdict = metacls.__prepare__(class_name, bases)
319
320 # special processing needed for names?
321 if isinstance(names, str):
322 names = names.replace(',', ' ').split()
323 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
Ethan Furmand9925a12014-09-16 20:35:55 -0700324 names = [(e, i) for (i, e) in enumerate(names, start)]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700325
326 # Here, names is either an iterable of (name, value) or a mapping.
327 for item in names:
328 if isinstance(item, str):
329 member_name, member_value = item, names[item]
330 else:
331 member_name, member_value = item
332 classdict[member_name] = member_value
333 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
334
335 # TODO: replace the frame hack if a blessed way to know the calling
336 # module is ever developed
337 if module is None:
338 try:
339 module = sys._getframe(2).f_globals['__name__']
340 except (AttributeError, ValueError) as exc:
341 pass
342 if module is None:
343 _make_class_unpicklable(enum_class)
344 else:
345 enum_class.__module__ = module
Ethan Furmanca1b7942014-02-08 11:36:27 -0800346 if qualname is not None:
347 enum_class.__qualname__ = qualname
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700348
349 return enum_class
350
351 @staticmethod
352 def _get_mixins_(bases):
353 """Returns the type for creating enum members, and the first inherited
354 enum class.
355
356 bases: the tuple of bases that was given to __new__
357
358 """
359 if not bases:
360 return object, Enum
361
362 # double check that we are not subclassing a class with existing
363 # enumeration members; while we're at it, see if any other data
364 # type has been mixed in so we can use the correct __new__
365 member_type = first_enum = None
366 for base in bases:
367 if (base is not Enum and
368 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700369 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700370 raise TypeError("Cannot extend enumerations")
371 # base is now the last base in bases
372 if not issubclass(base, Enum):
373 raise TypeError("new enumerations must be created as "
374 "`ClassName([mixin_type,] enum_type)`")
375
376 # get correct mix-in type (either mix-in type of Enum subclass, or
377 # first base if last base is Enum)
378 if not issubclass(bases[0], Enum):
379 member_type = bases[0] # first data type
380 first_enum = bases[-1] # enum type
381 else:
382 for base in bases[0].__mro__:
383 # most common: (IntEnum, int, Enum, object)
384 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
385 # <class 'int'>, <Enum 'Enum'>,
386 # <class 'object'>)
387 if issubclass(base, Enum):
388 if first_enum is None:
389 first_enum = base
390 else:
391 if member_type is None:
392 member_type = base
393
394 return member_type, first_enum
395
396 @staticmethod
397 def _find_new_(classdict, member_type, first_enum):
398 """Returns the __new__ to be used for creating the enum members.
399
400 classdict: the class dictionary given to __new__
401 member_type: the data type whose __new__ will be used by default
402 first_enum: enumeration to check for an overriding __new__
403
404 """
405 # now find the correct __new__, checking to see of one was defined
406 # by the user; also check earlier enum classes in case a __new__ was
407 # saved as __new_member__
408 __new__ = classdict.get('__new__', None)
409
410 # should __new__ be saved as __new_member__ later?
411 save_new = __new__ is not None
412
413 if __new__ is None:
414 # check all possibles for __new_member__ before falling back to
415 # __new__
416 for method in ('__new_member__', '__new__'):
417 for possible in (member_type, first_enum):
418 target = getattr(possible, method, None)
419 if target not in {
420 None,
421 None.__new__,
422 object.__new__,
423 Enum.__new__,
424 }:
425 __new__ = target
426 break
427 if __new__ is not None:
428 break
429 else:
430 __new__ = object.__new__
431
432 # if a non-object.__new__ is used then whatever value/tuple was
433 # assigned to the enum member name will be passed to __new__ and to the
434 # new enum member's __init__
435 if __new__ is object.__new__:
436 use_args = False
437 else:
438 use_args = True
439
440 return __new__, save_new, use_args
441
442
443class Enum(metaclass=EnumMeta):
444 """Generic enumeration.
445
446 Derive from this class to define new enumerations.
447
448 """
449 def __new__(cls, value):
450 # all enum instances are actually created during class construction
451 # without calling this method; this method is called by the metaclass'
452 # __call__ (i.e. Color(3) ), and by pickle
453 if type(value) is cls:
454 # For lookups like Color(Color.red)
455 return value
456 # by-value search for a matching enum member
457 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700458 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700459 if value in cls._value2member_map_:
460 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700461 except TypeError:
462 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700463 for member in cls._member_map_.values():
Ethan Furman0081f232014-09-16 17:31:23 -0700464 if member._value_ == value:
Ethan Furman2aa27322013-07-19 19:35:56 -0700465 return member
Ethan Furman0081f232014-09-16 17:31:23 -0700466 raise ValueError("%r is not a valid %s" % (value, cls.__name__))
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700467
468 def __repr__(self):
469 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700470 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700471
472 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700473 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700474
Ethan Furman388a3922013-08-12 06:51:41 -0700475 def __dir__(self):
Ethan Furman0ae550b2014-10-14 08:58:32 -0700476 added_behavior = [
477 m
478 for cls in self.__class__.mro()
479 for m in cls.__dict__
Ethan Furman354ecf12015-03-11 08:43:12 -0700480 if m[0] != '_' and m not in self._member_map_
Ethan Furman0ae550b2014-10-14 08:58:32 -0700481 ]
Ethan Furmanec5f8eb2014-10-21 13:40:35 -0700482 return (['__class__', '__doc__', '__module__'] + added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700483
Ethan Furmanec15a822013-08-31 19:17:41 -0700484 def __format__(self, format_spec):
485 # mixed-in Enums should use the mixed-in type's __format__, otherwise
486 # we can get strange results with the Enum name showing up instead of
487 # the value
488
489 # pure Enum branch
490 if self._member_type_ is object:
491 cls = str
492 val = str(self)
493 # mix-in branch
494 else:
495 cls = self._member_type_
Ethan Furman0081f232014-09-16 17:31:23 -0700496 val = self._value_
Ethan Furmanec15a822013-08-31 19:17:41 -0700497 return cls.__format__(val, format_spec)
498
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700499 def __hash__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700500 return hash(self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700501
Ethan Furmanca1b7942014-02-08 11:36:27 -0800502 def __reduce_ex__(self, proto):
Ethan Furmandc870522014-02-18 12:37:12 -0800503 return self.__class__, (self._value_, )
Ethan Furmanca1b7942014-02-08 11:36:27 -0800504
Ethan Furman33918c12013-09-27 23:02:02 -0700505 # DynamicClassAttribute is used to provide access to the `name` and
506 # `value` properties of enum members while keeping some measure of
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700507 # protection from modification, while still allowing for an enumeration
508 # to have members named `name` and `value`. This works because enumeration
509 # members are not set directly on the enum class -- __getattr__ is
510 # used to look them up.
511
Ethan Furmane03ea372013-09-25 07:14:41 -0700512 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700513 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700514 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700515 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700516
Ethan Furmane03ea372013-09-25 07:14:41 -0700517 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700518 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700519 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700520 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700521
522
523class IntEnum(int, Enum):
524 """Enum where members are also (and must be) ints"""
Ethan Furmanf24bb352013-07-18 17:05:39 -0700525
526
527def unique(enumeration):
528 """Class decorator for enumerations ensuring unique member values."""
529 duplicates = []
530 for name, member in enumeration.__members__.items():
531 if name != member.name:
532 duplicates.append((name, member.name))
533 if duplicates:
534 alias_details = ', '.join(
535 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
536 raise ValueError('duplicate values found in %r: %s' %
537 (enumeration, alias_details))
538 return enumeration