blob: 45bfbb4e29e9aed66bd17c0570ad095aaf819f53 [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
20 name[-3:-2] != '_')
21
22
23def _is_sunder(name):
24 """Returns True if a _sunder_ name, False otherwise."""
25 return (name[0] == name[-1] == '_' and
26 name[1:2] != '_' and
27 name[-2:-1] != '_')
28
29
30def _make_class_unpicklable(cls):
31 """Make the given class un-picklable."""
32 def _break_on_call_reduce(self):
33 raise TypeError('%r cannot be pickled' % self)
34 cls.__reduce__ = _break_on_call_reduce
35 cls.__module__ = '<unknown>'
36
Ethan Furman101e0742013-09-15 12:34:36 -070037
Ethan Furman6b3d64a2013-06-14 16:55:46 -070038class _EnumDict(dict):
Ethan Furman101e0742013-09-15 12:34:36 -070039 """Track enum member order and ensure member names are not reused.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070040
41 EnumMeta will use the names found in self._member_names as the
42 enumeration member names.
43
44 """
45 def __init__(self):
46 super().__init__()
47 self._member_names = []
48
49 def __setitem__(self, key, value):
Ethan Furman101e0742013-09-15 12:34:36 -070050 """Changes anything not dundered or not a descriptor.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070051
52 If an enum member name is used twice, an error is raised; duplicate
53 values are not checked for.
54
55 Single underscore (sunder) names are reserved.
56
57 """
58 if _is_sunder(key):
59 raise ValueError('_names_ are reserved for future Enum use')
Ethan Furman101e0742013-09-15 12:34:36 -070060 elif _is_dunder(key):
61 pass
62 elif key in self._member_names:
63 # descriptor overwriting an enum?
64 raise TypeError('Attempted to reuse key: %r' % key)
65 elif not _is_descriptor(value):
66 if key in self:
67 # enum overwriting a descriptor?
68 raise TypeError('Key already defined as: %r' % self[key])
Ethan Furman6b3d64a2013-06-14 16:55:46 -070069 self._member_names.append(key)
70 super().__setitem__(key, value)
71
72
Ethan Furman101e0742013-09-15 12:34:36 -070073
Ezio Melotti9a3777e2013-08-17 15:53:55 +030074# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
75# until EnumMeta finishes running the first time the Enum class doesn't exist.
76# This is also why there are checks in EnumMeta like `if Enum is not None`
Ethan Furman6b3d64a2013-06-14 16:55:46 -070077Enum = None
78
79
80class EnumMeta(type):
81 """Metaclass for Enum"""
82 @classmethod
83 def __prepare__(metacls, cls, bases):
84 return _EnumDict()
85
86 def __new__(metacls, cls, bases, classdict):
87 # an Enum class is final once enumeration items have been defined; it
88 # cannot be mixed with other types (int, float, etc.) if it has an
89 # inherited __new__ unless a new __new__ is defined (or the resulting
90 # class will fail).
91 member_type, first_enum = metacls._get_mixins_(bases)
92 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
93 first_enum)
94
95 # save enum items into separate mapping so they don't get baked into
96 # the new class
97 members = {k: classdict[k] for k in classdict._member_names}
98 for name in classdict._member_names:
99 del classdict[name]
100
101 # check for illegal enum names (any others?)
102 invalid_names = set(members) & {'mro', }
103 if invalid_names:
104 raise ValueError('Invalid enum member name: {0}'.format(
105 ','.join(invalid_names)))
106
107 # create our new Enum type
108 enum_class = super().__new__(metacls, cls, bases, classdict)
Ethan Furman520ad572013-07-19 19:47:21 -0700109 enum_class._member_names_ = [] # names in definition order
110 enum_class._member_map_ = OrderedDict() # name->value map
Ethan Furman5e5a8232013-08-04 08:42:23 -0700111 enum_class._member_type_ = member_type
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700112
113 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700114 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700115
116 # check for a __getnewargs__, and if not present sabotage
117 # pickling, since it won't work anyway
118 if (member_type is not object and
119 member_type.__dict__.get('__getnewargs__') is None
120 ):
121 _make_class_unpicklable(enum_class)
122
123 # instantiate them, checking for duplicates as we go
124 # we instantiate first instead of checking for duplicates first in case
125 # a custom __new__ is doing something funky with the values -- such as
126 # auto-numbering ;)
127 for member_name in classdict._member_names:
128 value = members[member_name]
129 if not isinstance(value, tuple):
130 args = (value, )
131 else:
132 args = value
133 if member_type is tuple: # special case for tuple enums
134 args = (args, ) # wrap it one more time
135 if not use_args:
136 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700137 if not hasattr(enum_member, '_value_'):
138 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700139 else:
140 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700141 if not hasattr(enum_member, '_value_'):
142 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700143 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700144 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700145 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700146 enum_member.__init__(*args)
147 # If another member with the same value was already defined, the
148 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700149 for name, canonical_member in enum_class._member_map_.items():
150 if canonical_member.value == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700151 enum_member = canonical_member
152 break
153 else:
154 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700155 enum_class._member_names_.append(member_name)
156 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700157 try:
158 # This may fail if value is not hashable. We can't add the value
159 # to the map, and by-value lookups for this value will be
160 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700161 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700162 except TypeError:
163 pass
164
165 # double check that repr and friends are not the mixin's or various
166 # things break (such as pickle)
Ethan Furmanec15a822013-08-31 19:17:41 -0700167 for name in ('__repr__', '__str__', '__format__', '__getnewargs__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700168 class_method = getattr(enum_class, name)
169 obj_method = getattr(member_type, name, None)
170 enum_method = getattr(first_enum, name, None)
171 if obj_method is not None and obj_method is class_method:
172 setattr(enum_class, name, enum_method)
173
174 # replace any other __new__ with our own (as long as Enum is not None,
175 # anyway) -- again, this is to support pickle
176 if Enum is not None:
177 # if the user defined their own __new__, save it before it gets
178 # clobbered in case they subclass later
179 if save_new:
180 enum_class.__new_member__ = __new__
181 enum_class.__new__ = Enum.__new__
182 return enum_class
183
184 def __call__(cls, value, names=None, *, module=None, type=None):
185 """Either returns an existing member, or creates a new enum class.
186
187 This method is used both when an enum class is given a value to match
188 to an enumeration member (i.e. Color(3)) and for the functional API
189 (i.e. Color = Enum('Color', names='red green blue')).
190
191 When used for the functional API: `module`, if set, will be stored in
192 the new class' __module__ attribute; `type`, if set, will be mixed in
193 as the first base class.
194
195 Note: if `module` is not set this routine will attempt to discover the
196 calling module by walking the frame stack; if this is unsuccessful
197 the resulting class will not be pickleable.
198
199 """
200 if names is None: # simple value lookup
201 return cls.__new__(cls, value)
202 # otherwise, functional API: we're creating a new Enum type
203 return cls._create_(value, names, module=module, type=type)
204
205 def __contains__(cls, member):
Ethan Furman520ad572013-07-19 19:47:21 -0700206 return isinstance(member, cls) and member.name in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700207
Ethan Furman64a99722013-09-22 16:18:19 -0700208 def __delattr__(cls, attr):
209 # nicer error message when someone tries to delete an attribute
210 # (see issue19025).
211 if attr in cls._member_map_:
212 raise AttributeError(
213 "%s: cannot delete Enum member." % cls.__name__)
214 super().__delattr__(attr)
215
Ethan Furman388a3922013-08-12 06:51:41 -0700216 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700217 return (['__class__', '__doc__', '__members__', '__module__'] +
218 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700219
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700220 def __getattr__(cls, name):
221 """Return the enum member matching `name`
222
223 We use __getattr__ instead of descriptors or inserting into the enum
224 class' __dict__ in order to support `name` and `value` being both
225 properties for enum members (which live in the class' __dict__) and
226 enum members themselves.
227
228 """
229 if _is_dunder(name):
230 raise AttributeError(name)
231 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700232 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700233 except KeyError:
234 raise AttributeError(name) from None
235
236 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700237 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700238
239 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700240 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700241
242 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700243 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700244
Ethan Furman2131a4a2013-09-14 18:11:24 -0700245 @property
246 def __members__(cls):
247 """Returns a mapping of member name->value.
248
249 This mapping lists all enum members, including aliases. Note that this
250 is a read-only view of the internal mapping.
251
252 """
253 return MappingProxyType(cls._member_map_)
254
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700255 def __repr__(cls):
256 return "<enum %r>" % cls.__name__
257
Ethan Furman2131a4a2013-09-14 18:11:24 -0700258 def __reversed__(cls):
259 return (cls._member_map_[name] for name in reversed(cls._member_names_))
260
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700261 def __setattr__(cls, name, value):
262 """Block attempts to reassign Enum members.
263
264 A simple assignment to the class namespace only changes one of the
265 several possible ways to get an Enum member from the Enum class,
266 resulting in an inconsistent Enumeration.
267
268 """
269 member_map = cls.__dict__.get('_member_map_', {})
270 if name in member_map:
271 raise AttributeError('Cannot reassign members.')
272 super().__setattr__(name, value)
273
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700274 def _create_(cls, class_name, names=None, *, module=None, type=None):
275 """Convenience method to create a new Enum class.
276
277 `names` can be:
278
279 * A string containing member names, separated either with spaces or
280 commas. Values are auto-numbered from 1.
281 * An iterable of member names. Values are auto-numbered from 1.
282 * An iterable of (member name, value) pairs.
283 * A mapping of member name -> value.
284
285 """
286 metacls = cls.__class__
287 bases = (cls, ) if type is None else (type, cls)
288 classdict = metacls.__prepare__(class_name, bases)
289
290 # special processing needed for names?
291 if isinstance(names, str):
292 names = names.replace(',', ' ').split()
293 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
294 names = [(e, i) for (i, e) in enumerate(names, 1)]
295
296 # Here, names is either an iterable of (name, value) or a mapping.
297 for item in names:
298 if isinstance(item, str):
299 member_name, member_value = item, names[item]
300 else:
301 member_name, member_value = item
302 classdict[member_name] = member_value
303 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
304
305 # TODO: replace the frame hack if a blessed way to know the calling
306 # module is ever developed
307 if module is None:
308 try:
309 module = sys._getframe(2).f_globals['__name__']
310 except (AttributeError, ValueError) as exc:
311 pass
312 if module is None:
313 _make_class_unpicklable(enum_class)
314 else:
315 enum_class.__module__ = module
316
317 return enum_class
318
319 @staticmethod
320 def _get_mixins_(bases):
321 """Returns the type for creating enum members, and the first inherited
322 enum class.
323
324 bases: the tuple of bases that was given to __new__
325
326 """
327 if not bases:
328 return object, Enum
329
330 # double check that we are not subclassing a class with existing
331 # enumeration members; while we're at it, see if any other data
332 # type has been mixed in so we can use the correct __new__
333 member_type = first_enum = None
334 for base in bases:
335 if (base is not Enum and
336 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700337 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700338 raise TypeError("Cannot extend enumerations")
339 # base is now the last base in bases
340 if not issubclass(base, Enum):
341 raise TypeError("new enumerations must be created as "
342 "`ClassName([mixin_type,] enum_type)`")
343
344 # get correct mix-in type (either mix-in type of Enum subclass, or
345 # first base if last base is Enum)
346 if not issubclass(bases[0], Enum):
347 member_type = bases[0] # first data type
348 first_enum = bases[-1] # enum type
349 else:
350 for base in bases[0].__mro__:
351 # most common: (IntEnum, int, Enum, object)
352 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
353 # <class 'int'>, <Enum 'Enum'>,
354 # <class 'object'>)
355 if issubclass(base, Enum):
356 if first_enum is None:
357 first_enum = base
358 else:
359 if member_type is None:
360 member_type = base
361
362 return member_type, first_enum
363
364 @staticmethod
365 def _find_new_(classdict, member_type, first_enum):
366 """Returns the __new__ to be used for creating the enum members.
367
368 classdict: the class dictionary given to __new__
369 member_type: the data type whose __new__ will be used by default
370 first_enum: enumeration to check for an overriding __new__
371
372 """
373 # now find the correct __new__, checking to see of one was defined
374 # by the user; also check earlier enum classes in case a __new__ was
375 # saved as __new_member__
376 __new__ = classdict.get('__new__', None)
377
378 # should __new__ be saved as __new_member__ later?
379 save_new = __new__ is not None
380
381 if __new__ is None:
382 # check all possibles for __new_member__ before falling back to
383 # __new__
384 for method in ('__new_member__', '__new__'):
385 for possible in (member_type, first_enum):
386 target = getattr(possible, method, None)
387 if target not in {
388 None,
389 None.__new__,
390 object.__new__,
391 Enum.__new__,
392 }:
393 __new__ = target
394 break
395 if __new__ is not None:
396 break
397 else:
398 __new__ = object.__new__
399
400 # if a non-object.__new__ is used then whatever value/tuple was
401 # assigned to the enum member name will be passed to __new__ and to the
402 # new enum member's __init__
403 if __new__ is object.__new__:
404 use_args = False
405 else:
406 use_args = True
407
408 return __new__, save_new, use_args
409
410
411class Enum(metaclass=EnumMeta):
412 """Generic enumeration.
413
414 Derive from this class to define new enumerations.
415
416 """
417 def __new__(cls, value):
418 # all enum instances are actually created during class construction
419 # without calling this method; this method is called by the metaclass'
420 # __call__ (i.e. Color(3) ), and by pickle
421 if type(value) is cls:
422 # For lookups like Color(Color.red)
423 return value
424 # by-value search for a matching enum member
425 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700426 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700427 if value in cls._value2member_map_:
428 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700429 except TypeError:
430 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700431 for member in cls._member_map_.values():
Ethan Furman2aa27322013-07-19 19:35:56 -0700432 if member.value == value:
433 return member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700434 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
435
436 def __repr__(self):
437 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700438 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700439
440 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700441 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700442
Ethan Furman388a3922013-08-12 06:51:41 -0700443 def __dir__(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700444 added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
Ethan Furman64a99722013-09-22 16:18:19 -0700445 return (['__class__', '__doc__', '__module__', 'name', 'value'] +
446 added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700447
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700448 def __eq__(self, other):
449 if type(other) is self.__class__:
450 return self is other
451 return NotImplemented
452
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
474 # _RouteClassAttributeToGetattr is used to provide access to the `name`
475 # and `value` properties of enum members while keeping some measure of
476 # protection from modification, while still allowing for an enumeration
477 # to have members named `name` and `value`. This works because enumeration
478 # members are not set directly on the enum class -- __getattr__ is
479 # used to look them up.
480
Ethan Furmane03ea372013-09-25 07:14:41 -0700481 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700482 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700483 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700484 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700485
Ethan Furmane03ea372013-09-25 07:14:41 -0700486 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700487 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700488 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700489 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700490
491
492class IntEnum(int, Enum):
493 """Enum where members are also (and must be) ints"""
Ethan Furmanf24bb352013-07-18 17:05:39 -0700494
495
496def unique(enumeration):
497 """Class decorator for enumerations ensuring unique member values."""
498 duplicates = []
499 for name, member in enumeration.__members__.items():
500 if name != member.name:
501 duplicates.append((name, member.name))
502 if duplicates:
503 alias_details = ', '.join(
504 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
505 raise ValueError('duplicate values found in %r: %s' %
506 (enumeration, alias_details))
507 return enumeration