blob: c28f3452a75372a5a61c068e975ebd44ddd4540f [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
Ethan Furman48a724f2015-04-11 23:23:06 -0700109 # create a default docstring if one has not been provided
110 if '__doc__' not in classdict:
111 classdict['__doc__'] = 'An enumeration.'
112
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700113 # create our new Enum type
114 enum_class = super().__new__(metacls, cls, bases, classdict)
Ethan Furman520ad572013-07-19 19:47:21 -0700115 enum_class._member_names_ = [] # names in definition order
116 enum_class._member_map_ = OrderedDict() # name->value map
Ethan Furman5e5a8232013-08-04 08:42:23 -0700117 enum_class._member_type_ = member_type
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700118
Ethan Furman354ecf12015-03-11 08:43:12 -0700119 # save attributes from super classes so we know if we can take
120 # the shortcut of storing members in the class dict
121 base_attributes = {a for b in bases for a in b.__dict__}
122
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700123 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700124 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700125
Ethan Furman2da95042014-03-03 12:42:52 -0800126 # If a custom type is mixed into the Enum, and it does not know how
127 # to pickle itself, pickle.dumps will succeed but pickle.loads will
128 # fail. Rather than have the error show up later and possibly far
129 # from the source, sabotage the pickle protocol for this class so
130 # that pickle.dumps also fails.
131 #
132 # However, if the new class implements its own __reduce_ex__, do not
133 # sabotage -- it's on them to make sure it works correctly. We use
134 # __reduce_ex__ instead of any of the others as it is preferred by
135 # pickle over __reduce__, and it handles all pickle protocols.
136 if '__reduce_ex__' not in classdict:
Ethan Furmandc870522014-02-18 12:37:12 -0800137 if member_type is not object:
138 methods = ('__getnewargs_ex__', '__getnewargs__',
139 '__reduce_ex__', '__reduce__')
Ethan Furman2da95042014-03-03 12:42:52 -0800140 if not any(m in member_type.__dict__ for m in methods):
Ethan Furmandc870522014-02-18 12:37:12 -0800141 _make_class_unpicklable(enum_class)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700142
143 # instantiate them, checking for duplicates as we go
144 # we instantiate first instead of checking for duplicates first in case
145 # a custom __new__ is doing something funky with the values -- such as
146 # auto-numbering ;)
147 for member_name in classdict._member_names:
148 value = members[member_name]
149 if not isinstance(value, tuple):
150 args = (value, )
151 else:
152 args = value
153 if member_type is tuple: # special case for tuple enums
154 args = (args, ) # wrap it one more time
155 if not use_args:
156 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700157 if not hasattr(enum_member, '_value_'):
158 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700159 else:
160 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700161 if not hasattr(enum_member, '_value_'):
162 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700163 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700164 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700165 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700166 enum_member.__init__(*args)
167 # If another member with the same value was already defined, the
168 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700169 for name, canonical_member in enum_class._member_map_.items():
Ethan Furman0081f232014-09-16 17:31:23 -0700170 if canonical_member._value_ == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700171 enum_member = canonical_member
172 break
173 else:
174 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700175 enum_class._member_names_.append(member_name)
Ethan Furman354ecf12015-03-11 08:43:12 -0700176 # performance boost for any member that would not shadow
177 # a DynamicClassAttribute
178 if member_name not in base_attributes:
179 setattr(enum_class, member_name, enum_member)
180 # now add to _member_map_
Ethan Furman520ad572013-07-19 19:47:21 -0700181 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700182 try:
183 # This may fail if value is not hashable. We can't add the value
184 # to the map, and by-value lookups for this value will be
185 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700186 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700187 except TypeError:
188 pass
189
190 # double check that repr and friends are not the mixin's or various
191 # things break (such as pickle)
Ethan Furmandc870522014-02-18 12:37:12 -0800192 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700193 class_method = getattr(enum_class, name)
194 obj_method = getattr(member_type, name, None)
195 enum_method = getattr(first_enum, name, None)
196 if obj_method is not None and obj_method is class_method:
197 setattr(enum_class, name, enum_method)
198
199 # replace any other __new__ with our own (as long as Enum is not None,
200 # anyway) -- again, this is to support pickle
201 if Enum is not None:
202 # if the user defined their own __new__, save it before it gets
203 # clobbered in case they subclass later
204 if save_new:
205 enum_class.__new_member__ = __new__
206 enum_class.__new__ = Enum.__new__
207 return enum_class
208
Ethan Furmand9925a12014-09-16 20:35:55 -0700209 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700210 """Either returns an existing member, or creates a new enum class.
211
212 This method is used both when an enum class is given a value to match
213 to an enumeration member (i.e. Color(3)) and for the functional API
214 (i.e. Color = Enum('Color', names='red green blue')).
215
Ethan Furman2da95042014-03-03 12:42:52 -0800216 When used for the functional API:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700217
Ethan Furman2da95042014-03-03 12:42:52 -0800218 `value` will be the name of the new class.
219
220 `names` should be either a string of white-space/comma delimited names
Ethan Furmand9925a12014-09-16 20:35:55 -0700221 (values will start at `start`), or an iterator/mapping of name, value pairs.
Ethan Furman2da95042014-03-03 12:42:52 -0800222
223 `module` should be set to the module this class is being created in;
224 if it is not set, an attempt to find that module will be made, but if
225 it fails the class will not be picklable.
226
227 `qualname` should be set to the actual location this class can be found
228 at in its module; by default it is set to the global scope. If this is
229 not correct, unpickling will fail in some circumstances.
230
231 `type`, if set, will be mixed in as the first base class.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700232
233 """
234 if names is None: # simple value lookup
235 return cls.__new__(cls, value)
236 # otherwise, functional API: we're creating a new Enum type
Ethan Furmand9925a12014-09-16 20:35:55 -0700237 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700238
239 def __contains__(cls, member):
Ethan Furman0081f232014-09-16 17:31:23 -0700240 return isinstance(member, cls) and member._name_ in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700241
Ethan Furman64a99722013-09-22 16:18:19 -0700242 def __delattr__(cls, attr):
243 # nicer error message when someone tries to delete an attribute
244 # (see issue19025).
245 if attr in cls._member_map_:
246 raise AttributeError(
247 "%s: cannot delete Enum member." % cls.__name__)
248 super().__delattr__(attr)
249
Ethan Furman388a3922013-08-12 06:51:41 -0700250 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700251 return (['__class__', '__doc__', '__members__', '__module__'] +
252 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700253
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700254 def __getattr__(cls, name):
255 """Return the enum member matching `name`
256
257 We use __getattr__ instead of descriptors or inserting into the enum
258 class' __dict__ in order to support `name` and `value` being both
259 properties for enum members (which live in the class' __dict__) and
260 enum members themselves.
261
262 """
263 if _is_dunder(name):
264 raise AttributeError(name)
265 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700266 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700267 except KeyError:
268 raise AttributeError(name) from None
269
270 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700271 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700272
273 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700274 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700275
276 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700277 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700278
Ethan Furman2131a4a2013-09-14 18:11:24 -0700279 @property
280 def __members__(cls):
281 """Returns a mapping of member name->value.
282
283 This mapping lists all enum members, including aliases. Note that this
284 is a read-only view of the internal mapping.
285
286 """
287 return MappingProxyType(cls._member_map_)
288
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700289 def __repr__(cls):
290 return "<enum %r>" % cls.__name__
291
Ethan Furman2131a4a2013-09-14 18:11:24 -0700292 def __reversed__(cls):
293 return (cls._member_map_[name] for name in reversed(cls._member_names_))
294
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700295 def __setattr__(cls, name, value):
296 """Block attempts to reassign Enum members.
297
298 A simple assignment to the class namespace only changes one of the
299 several possible ways to get an Enum member from the Enum class,
300 resulting in an inconsistent Enumeration.
301
302 """
303 member_map = cls.__dict__.get('_member_map_', {})
304 if name in member_map:
305 raise AttributeError('Cannot reassign members.')
306 super().__setattr__(name, value)
307
Ethan Furmand9925a12014-09-16 20:35:55 -0700308 def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700309 """Convenience method to create a new Enum class.
310
311 `names` can be:
312
313 * A string containing member names, separated either with spaces or
Ethan Furmand9925a12014-09-16 20:35:55 -0700314 commas. Values are incremented by 1 from `start`.
315 * An iterable of member names. Values are incremented by 1 from `start`.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700316 * An iterable of (member name, value) pairs.
Ethan Furmand9925a12014-09-16 20:35:55 -0700317 * A mapping of member name -> value pairs.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700318
319 """
320 metacls = cls.__class__
321 bases = (cls, ) if type is None else (type, cls)
322 classdict = metacls.__prepare__(class_name, bases)
323
324 # special processing needed for names?
325 if isinstance(names, str):
326 names = names.replace(',', ' ').split()
327 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
Ethan Furmand9925a12014-09-16 20:35:55 -0700328 names = [(e, i) for (i, e) in enumerate(names, start)]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700329
330 # Here, names is either an iterable of (name, value) or a mapping.
331 for item in names:
332 if isinstance(item, str):
333 member_name, member_value = item, names[item]
334 else:
335 member_name, member_value = item
336 classdict[member_name] = member_value
337 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
338
339 # TODO: replace the frame hack if a blessed way to know the calling
340 # module is ever developed
341 if module is None:
342 try:
343 module = sys._getframe(2).f_globals['__name__']
344 except (AttributeError, ValueError) as exc:
345 pass
346 if module is None:
347 _make_class_unpicklable(enum_class)
348 else:
349 enum_class.__module__ = module
Ethan Furmanca1b7942014-02-08 11:36:27 -0800350 if qualname is not None:
351 enum_class.__qualname__ = qualname
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700352
353 return enum_class
354
355 @staticmethod
356 def _get_mixins_(bases):
357 """Returns the type for creating enum members, and the first inherited
358 enum class.
359
360 bases: the tuple of bases that was given to __new__
361
362 """
363 if not bases:
364 return object, Enum
365
366 # double check that we are not subclassing a class with existing
367 # enumeration members; while we're at it, see if any other data
368 # type has been mixed in so we can use the correct __new__
369 member_type = first_enum = None
370 for base in bases:
371 if (base is not Enum and
372 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700373 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700374 raise TypeError("Cannot extend enumerations")
375 # base is now the last base in bases
376 if not issubclass(base, Enum):
377 raise TypeError("new enumerations must be created as "
378 "`ClassName([mixin_type,] enum_type)`")
379
380 # get correct mix-in type (either mix-in type of Enum subclass, or
381 # first base if last base is Enum)
382 if not issubclass(bases[0], Enum):
383 member_type = bases[0] # first data type
384 first_enum = bases[-1] # enum type
385 else:
386 for base in bases[0].__mro__:
387 # most common: (IntEnum, int, Enum, object)
388 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
389 # <class 'int'>, <Enum 'Enum'>,
390 # <class 'object'>)
391 if issubclass(base, Enum):
392 if first_enum is None:
393 first_enum = base
394 else:
395 if member_type is None:
396 member_type = base
397
398 return member_type, first_enum
399
400 @staticmethod
401 def _find_new_(classdict, member_type, first_enum):
402 """Returns the __new__ to be used for creating the enum members.
403
404 classdict: the class dictionary given to __new__
405 member_type: the data type whose __new__ will be used by default
406 first_enum: enumeration to check for an overriding __new__
407
408 """
409 # now find the correct __new__, checking to see of one was defined
410 # by the user; also check earlier enum classes in case a __new__ was
411 # saved as __new_member__
412 __new__ = classdict.get('__new__', None)
413
414 # should __new__ be saved as __new_member__ later?
415 save_new = __new__ is not None
416
417 if __new__ is None:
418 # check all possibles for __new_member__ before falling back to
419 # __new__
420 for method in ('__new_member__', '__new__'):
421 for possible in (member_type, first_enum):
422 target = getattr(possible, method, None)
423 if target not in {
424 None,
425 None.__new__,
426 object.__new__,
427 Enum.__new__,
428 }:
429 __new__ = target
430 break
431 if __new__ is not None:
432 break
433 else:
434 __new__ = object.__new__
435
436 # if a non-object.__new__ is used then whatever value/tuple was
437 # assigned to the enum member name will be passed to __new__ and to the
438 # new enum member's __init__
439 if __new__ is object.__new__:
440 use_args = False
441 else:
442 use_args = True
443
444 return __new__, save_new, use_args
445
446
447class Enum(metaclass=EnumMeta):
448 """Generic enumeration.
449
450 Derive from this class to define new enumerations.
451
452 """
453 def __new__(cls, value):
454 # all enum instances are actually created during class construction
455 # without calling this method; this method is called by the metaclass'
456 # __call__ (i.e. Color(3) ), and by pickle
457 if type(value) is cls:
458 # For lookups like Color(Color.red)
459 return value
460 # by-value search for a matching enum member
461 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700462 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700463 if value in cls._value2member_map_:
464 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700465 except TypeError:
466 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700467 for member in cls._member_map_.values():
Ethan Furman0081f232014-09-16 17:31:23 -0700468 if member._value_ == value:
Ethan Furman2aa27322013-07-19 19:35:56 -0700469 return member
Ethan Furman0081f232014-09-16 17:31:23 -0700470 raise ValueError("%r is not a valid %s" % (value, cls.__name__))
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700471
472 def __repr__(self):
473 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700474 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700475
476 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700477 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700478
Ethan Furman388a3922013-08-12 06:51:41 -0700479 def __dir__(self):
Ethan Furman0ae550b2014-10-14 08:58:32 -0700480 added_behavior = [
481 m
482 for cls in self.__class__.mro()
483 for m in cls.__dict__
Ethan Furman354ecf12015-03-11 08:43:12 -0700484 if m[0] != '_' and m not in self._member_map_
Ethan Furman0ae550b2014-10-14 08:58:32 -0700485 ]
Ethan Furmanec5f8eb2014-10-21 13:40:35 -0700486 return (['__class__', '__doc__', '__module__'] + added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700487
Ethan Furmanec15a822013-08-31 19:17:41 -0700488 def __format__(self, format_spec):
489 # mixed-in Enums should use the mixed-in type's __format__, otherwise
490 # we can get strange results with the Enum name showing up instead of
491 # the value
492
493 # pure Enum branch
494 if self._member_type_ is object:
495 cls = str
496 val = str(self)
497 # mix-in branch
498 else:
499 cls = self._member_type_
Ethan Furman0081f232014-09-16 17:31:23 -0700500 val = self._value_
Ethan Furmanec15a822013-08-31 19:17:41 -0700501 return cls.__format__(val, format_spec)
502
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700503 def __hash__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700504 return hash(self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700505
Ethan Furmanca1b7942014-02-08 11:36:27 -0800506 def __reduce_ex__(self, proto):
Ethan Furmandc870522014-02-18 12:37:12 -0800507 return self.__class__, (self._value_, )
Ethan Furmanca1b7942014-02-08 11:36:27 -0800508
Ethan Furman33918c12013-09-27 23:02:02 -0700509 # DynamicClassAttribute is used to provide access to the `name` and
510 # `value` properties of enum members while keeping some measure of
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700511 # protection from modification, while still allowing for an enumeration
512 # to have members named `name` and `value`. This works because enumeration
513 # members are not set directly on the enum class -- __getattr__ is
514 # used to look them up.
515
Ethan Furmane03ea372013-09-25 07:14:41 -0700516 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700517 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700518 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700519 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700520
Ethan Furmane03ea372013-09-25 07:14:41 -0700521 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700522 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700523 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700524 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700525
Ethan Furman24e837f2015-03-18 17:27:57 -0700526 @classmethod
527 def _convert(cls, name, module, filter, source=None):
528 """
529 Create a new Enum subclass that replaces a collection of global constants
530 """
531 # convert all constants from source (or module) that pass filter() to
532 # a new Enum called name, and export the enum and its members back to
533 # module;
534 # also, replace the __reduce_ex__ method so unpickling works in
535 # previous Python versions
536 module_globals = vars(sys.modules[module])
537 if source:
538 source = vars(source)
539 else:
540 source = module_globals
541 members = {name: value for name, value in source.items()
542 if filter(name)}
543 cls = cls(name, members, module=module)
544 cls.__reduce_ex__ = _reduce_ex_by_name
545 module_globals.update(cls.__members__)
546 module_globals[name] = cls
547 return cls
548
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700549
550class IntEnum(int, Enum):
551 """Enum where members are also (and must be) ints"""
Ethan Furmanf24bb352013-07-18 17:05:39 -0700552
553
Ethan Furman24e837f2015-03-18 17:27:57 -0700554def _reduce_ex_by_name(self, proto):
555 return self.name
556
Ethan Furmanf24bb352013-07-18 17:05:39 -0700557def unique(enumeration):
558 """Class decorator for enumerations ensuring unique member values."""
559 duplicates = []
560 for name, member in enumeration.__members__.items():
561 if name != member.name:
562 duplicates.append((name, member.name))
563 if duplicates:
564 alias_details = ', '.join(
565 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
566 raise ValueError('duplicate values found in %r: %s' %
567 (enumeration, alias_details))
568 return enumeration