blob: 794f68e60ca98061c6705e37be8b7b35e191aefa [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
115 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700116 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700117
Ethan Furmanca1b7942014-02-08 11:36:27 -0800118 # check for a supported pickle protocols, and if not present sabotage
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700119 # pickling, since it won't work anyway
Ethan Furmanca1b7942014-02-08 11:36:27 -0800120 if member_type is not object:
121 methods = ('__getnewargs_ex__', '__getnewargs__',
122 '__reduce_ex__', '__reduce__')
123 if not any(map(member_type.__dict__.get, methods)):
124 _make_class_unpicklable(enum_class)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700125
126 # instantiate them, checking for duplicates as we go
127 # we instantiate first instead of checking for duplicates first in case
128 # a custom __new__ is doing something funky with the values -- such as
129 # auto-numbering ;)
130 for member_name in classdict._member_names:
131 value = members[member_name]
132 if not isinstance(value, tuple):
133 args = (value, )
134 else:
135 args = value
136 if member_type is tuple: # special case for tuple enums
137 args = (args, ) # wrap it one more time
138 if not use_args:
139 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700140 if not hasattr(enum_member, '_value_'):
141 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700142 else:
143 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700144 if not hasattr(enum_member, '_value_'):
145 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700146 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700147 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700148 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700149 enum_member.__init__(*args)
150 # If another member with the same value was already defined, the
151 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700152 for name, canonical_member in enum_class._member_map_.items():
153 if canonical_member.value == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700154 enum_member = canonical_member
155 break
156 else:
157 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700158 enum_class._member_names_.append(member_name)
159 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700160 try:
161 # This may fail if value is not hashable. We can't add the value
162 # to the map, and by-value lookups for this value will be
163 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700164 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700165 except TypeError:
166 pass
167
168 # double check that repr and friends are not the mixin's or various
169 # things break (such as pickle)
Ethan Furmanca1b7942014-02-08 11:36:27 -0800170 for name in ('__repr__', '__str__', '__format__', '__getnewargs__', '__reduce_ex__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700171 class_method = getattr(enum_class, name)
172 obj_method = getattr(member_type, name, None)
173 enum_method = getattr(first_enum, name, None)
174 if obj_method is not None and obj_method is class_method:
175 setattr(enum_class, name, enum_method)
176
177 # replace any other __new__ with our own (as long as Enum is not None,
178 # anyway) -- again, this is to support pickle
179 if Enum is not None:
180 # if the user defined their own __new__, save it before it gets
181 # clobbered in case they subclass later
182 if save_new:
183 enum_class.__new_member__ = __new__
184 enum_class.__new__ = Enum.__new__
185 return enum_class
186
Ethan Furmanca1b7942014-02-08 11:36:27 -0800187 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700188 """Either returns an existing member, or creates a new enum class.
189
190 This method is used both when an enum class is given a value to match
191 to an enumeration member (i.e. Color(3)) and for the functional API
192 (i.e. Color = Enum('Color', names='red green blue')).
193
194 When used for the functional API: `module`, if set, will be stored in
195 the new class' __module__ attribute; `type`, if set, will be mixed in
196 as the first base class.
197
198 Note: if `module` is not set this routine will attempt to discover the
199 calling module by walking the frame stack; if this is unsuccessful
200 the resulting class will not be pickleable.
201
202 """
203 if names is None: # simple value lookup
204 return cls.__new__(cls, value)
205 # otherwise, functional API: we're creating a new Enum type
Ethan Furmanca1b7942014-02-08 11:36:27 -0800206 return cls._create_(value, names, module=module, qualname=qualname, type=type)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700207
208 def __contains__(cls, member):
Ethan Furman520ad572013-07-19 19:47:21 -0700209 return isinstance(member, cls) and member.name in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700210
Ethan Furman64a99722013-09-22 16:18:19 -0700211 def __delattr__(cls, attr):
212 # nicer error message when someone tries to delete an attribute
213 # (see issue19025).
214 if attr in cls._member_map_:
215 raise AttributeError(
216 "%s: cannot delete Enum member." % cls.__name__)
217 super().__delattr__(attr)
218
Ethan Furman388a3922013-08-12 06:51:41 -0700219 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700220 return (['__class__', '__doc__', '__members__', '__module__'] +
221 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700222
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700223 def __getattr__(cls, name):
224 """Return the enum member matching `name`
225
226 We use __getattr__ instead of descriptors or inserting into the enum
227 class' __dict__ in order to support `name` and `value` being both
228 properties for enum members (which live in the class' __dict__) and
229 enum members themselves.
230
231 """
232 if _is_dunder(name):
233 raise AttributeError(name)
234 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700235 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700236 except KeyError:
237 raise AttributeError(name) from None
238
239 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700240 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700241
242 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700243 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700244
245 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700246 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700247
Ethan Furman2131a4a2013-09-14 18:11:24 -0700248 @property
249 def __members__(cls):
250 """Returns a mapping of member name->value.
251
252 This mapping lists all enum members, including aliases. Note that this
253 is a read-only view of the internal mapping.
254
255 """
256 return MappingProxyType(cls._member_map_)
257
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700258 def __repr__(cls):
259 return "<enum %r>" % cls.__name__
260
Ethan Furman2131a4a2013-09-14 18:11:24 -0700261 def __reversed__(cls):
262 return (cls._member_map_[name] for name in reversed(cls._member_names_))
263
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700264 def __setattr__(cls, name, value):
265 """Block attempts to reassign Enum members.
266
267 A simple assignment to the class namespace only changes one of the
268 several possible ways to get an Enum member from the Enum class,
269 resulting in an inconsistent Enumeration.
270
271 """
272 member_map = cls.__dict__.get('_member_map_', {})
273 if name in member_map:
274 raise AttributeError('Cannot reassign members.')
275 super().__setattr__(name, value)
276
Ethan Furmanca1b7942014-02-08 11:36:27 -0800277 def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700278 """Convenience method to create a new Enum class.
279
280 `names` can be:
281
282 * A string containing member names, separated either with spaces or
283 commas. Values are auto-numbered from 1.
284 * An iterable of member names. Values are auto-numbered from 1.
285 * An iterable of (member name, value) pairs.
286 * A mapping of member name -> value.
287
288 """
289 metacls = cls.__class__
290 bases = (cls, ) if type is None else (type, cls)
291 classdict = metacls.__prepare__(class_name, bases)
292
293 # special processing needed for names?
294 if isinstance(names, str):
295 names = names.replace(',', ' ').split()
296 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
297 names = [(e, i) for (i, e) in enumerate(names, 1)]
298
299 # Here, names is either an iterable of (name, value) or a mapping.
300 for item in names:
301 if isinstance(item, str):
302 member_name, member_value = item, names[item]
303 else:
304 member_name, member_value = item
305 classdict[member_name] = member_value
306 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
307
308 # TODO: replace the frame hack if a blessed way to know the calling
309 # module is ever developed
310 if module is None:
311 try:
312 module = sys._getframe(2).f_globals['__name__']
313 except (AttributeError, ValueError) as exc:
314 pass
315 if module is None:
316 _make_class_unpicklable(enum_class)
317 else:
318 enum_class.__module__ = module
Ethan Furmanca1b7942014-02-08 11:36:27 -0800319 if qualname is not None:
320 enum_class.__qualname__ = qualname
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700321
322 return enum_class
323
324 @staticmethod
325 def _get_mixins_(bases):
326 """Returns the type for creating enum members, and the first inherited
327 enum class.
328
329 bases: the tuple of bases that was given to __new__
330
331 """
332 if not bases:
333 return object, Enum
334
335 # double check that we are not subclassing a class with existing
336 # enumeration members; while we're at it, see if any other data
337 # type has been mixed in so we can use the correct __new__
338 member_type = first_enum = None
339 for base in bases:
340 if (base is not Enum and
341 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700342 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700343 raise TypeError("Cannot extend enumerations")
344 # base is now the last base in bases
345 if not issubclass(base, Enum):
346 raise TypeError("new enumerations must be created as "
347 "`ClassName([mixin_type,] enum_type)`")
348
349 # get correct mix-in type (either mix-in type of Enum subclass, or
350 # first base if last base is Enum)
351 if not issubclass(bases[0], Enum):
352 member_type = bases[0] # first data type
353 first_enum = bases[-1] # enum type
354 else:
355 for base in bases[0].__mro__:
356 # most common: (IntEnum, int, Enum, object)
357 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
358 # <class 'int'>, <Enum 'Enum'>,
359 # <class 'object'>)
360 if issubclass(base, Enum):
361 if first_enum is None:
362 first_enum = base
363 else:
364 if member_type is None:
365 member_type = base
366
367 return member_type, first_enum
368
369 @staticmethod
370 def _find_new_(classdict, member_type, first_enum):
371 """Returns the __new__ to be used for creating the enum members.
372
373 classdict: the class dictionary given to __new__
374 member_type: the data type whose __new__ will be used by default
375 first_enum: enumeration to check for an overriding __new__
376
377 """
378 # now find the correct __new__, checking to see of one was defined
379 # by the user; also check earlier enum classes in case a __new__ was
380 # saved as __new_member__
381 __new__ = classdict.get('__new__', None)
382
383 # should __new__ be saved as __new_member__ later?
384 save_new = __new__ is not None
385
386 if __new__ is None:
387 # check all possibles for __new_member__ before falling back to
388 # __new__
389 for method in ('__new_member__', '__new__'):
390 for possible in (member_type, first_enum):
391 target = getattr(possible, method, None)
392 if target not in {
393 None,
394 None.__new__,
395 object.__new__,
396 Enum.__new__,
397 }:
398 __new__ = target
399 break
400 if __new__ is not None:
401 break
402 else:
403 __new__ = object.__new__
404
405 # if a non-object.__new__ is used then whatever value/tuple was
406 # assigned to the enum member name will be passed to __new__ and to the
407 # new enum member's __init__
408 if __new__ is object.__new__:
409 use_args = False
410 else:
411 use_args = True
412
413 return __new__, save_new, use_args
414
415
416class Enum(metaclass=EnumMeta):
417 """Generic enumeration.
418
419 Derive from this class to define new enumerations.
420
421 """
422 def __new__(cls, value):
423 # all enum instances are actually created during class construction
424 # without calling this method; this method is called by the metaclass'
425 # __call__ (i.e. Color(3) ), and by pickle
426 if type(value) is cls:
427 # For lookups like Color(Color.red)
428 return value
429 # by-value search for a matching enum member
430 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700431 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700432 if value in cls._value2member_map_:
433 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700434 except TypeError:
435 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700436 for member in cls._member_map_.values():
Ethan Furman2aa27322013-07-19 19:35:56 -0700437 if member.value == value:
438 return member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700439 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
440
441 def __repr__(self):
442 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700443 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700444
445 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700446 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700447
Ethan Furman388a3922013-08-12 06:51:41 -0700448 def __dir__(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700449 added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
Ethan Furman64a99722013-09-22 16:18:19 -0700450 return (['__class__', '__doc__', '__module__', 'name', 'value'] +
451 added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700452
Ethan Furmanec15a822013-08-31 19:17:41 -0700453 def __format__(self, format_spec):
454 # mixed-in Enums should use the mixed-in type's __format__, otherwise
455 # we can get strange results with the Enum name showing up instead of
456 # the value
457
458 # pure Enum branch
459 if self._member_type_ is object:
460 cls = str
461 val = str(self)
462 # mix-in branch
463 else:
464 cls = self._member_type_
465 val = self.value
466 return cls.__format__(val, format_spec)
467
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700468 def __getnewargs__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700469 return (self._value_, )
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700470
471 def __hash__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700472 return hash(self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700473
Ethan Furmanca1b7942014-02-08 11:36:27 -0800474 def __reduce_ex__(self, proto):
475 return self.__class__, self.__getnewargs__()
476
Ethan Furman33918c12013-09-27 23:02:02 -0700477 # DynamicClassAttribute is used to provide access to the `name` and
478 # `value` properties of enum members while keeping some measure of
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700479 # protection from modification, while still allowing for an enumeration
480 # to have members named `name` and `value`. This works because enumeration
481 # members are not set directly on the enum class -- __getattr__ is
482 # used to look them up.
483
Ethan Furmane03ea372013-09-25 07:14:41 -0700484 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700485 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700486 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700487 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700488
Ethan Furmane03ea372013-09-25 07:14:41 -0700489 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700490 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700491 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700492 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700493
494
495class IntEnum(int, Enum):
496 """Enum where members are also (and must be) ints"""
Ethan Furmanf24bb352013-07-18 17:05:39 -0700497
498
499def unique(enumeration):
500 """Class decorator for enumerations ensuring unique member values."""
501 duplicates = []
502 for name, member in enumeration.__members__.items():
503 if name != member.name:
504 duplicates.append((name, member.name))
505 if duplicates:
506 alias_details = ', '.join(
507 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
508 raise ValueError('duplicate values found in %r: %s' %
509 (enumeration, alias_details))
510 return enumeration