blob: 0a7afc45c0b53ac0b207afacef1f8266cfbe7799 [file] [log] [blame]
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001import sys
2from collections import OrderedDict
3from types import MappingProxyType
4
Ethan Furmanf24bb352013-07-18 17:05:39 -07005__all__ = ['Enum', 'IntEnum', 'unique']
Ethan Furman6b3d64a2013-06-14 16:55:46 -07006
7
8class _RouteClassAttributeToGetattr:
9 """Route attribute access on a class to __getattr__.
10
11 This is a descriptor, used to define attributes that act differently when
12 accessed through an instance and through a class. Instance access remains
13 normal, but access to an attribute through a class will be routed to the
14 class's __getattr__ method; this is done by raising AttributeError.
15
16 """
17 def __init__(self, fget=None):
18 self.fget = fget
Ethan Furmanc850f342013-09-15 16:59:35 -070019 if fget.__doc__ is not None:
20 self.__doc__ = fget.__doc__
Ethan Furman6b3d64a2013-06-14 16:55:46 -070021
22 def __get__(self, instance, ownerclass=None):
23 if instance is None:
24 raise AttributeError()
25 return self.fget(instance)
26
27 def __set__(self, instance, value):
28 raise AttributeError("can't set attribute")
29
30 def __delete__(self, instance):
31 raise AttributeError("can't delete attribute")
32
33
Ethan Furman101e0742013-09-15 12:34:36 -070034def _is_descriptor(obj):
35 """Returns True if obj is a descriptor, False otherwise."""
36 return (
37 hasattr(obj, '__get__') or
38 hasattr(obj, '__set__') or
39 hasattr(obj, '__delete__'))
40
41
Ethan Furman6b3d64a2013-06-14 16:55:46 -070042def _is_dunder(name):
43 """Returns True if a __dunder__ name, False otherwise."""
44 return (name[:2] == name[-2:] == '__' and
45 name[2:3] != '_' and
46 name[-3:-2] != '_')
47
48
49def _is_sunder(name):
50 """Returns True if a _sunder_ name, False otherwise."""
51 return (name[0] == name[-1] == '_' and
52 name[1:2] != '_' and
53 name[-2:-1] != '_')
54
55
56def _make_class_unpicklable(cls):
57 """Make the given class un-picklable."""
58 def _break_on_call_reduce(self):
59 raise TypeError('%r cannot be pickled' % self)
60 cls.__reduce__ = _break_on_call_reduce
61 cls.__module__ = '<unknown>'
62
Ethan Furman101e0742013-09-15 12:34:36 -070063
Ethan Furman6b3d64a2013-06-14 16:55:46 -070064class _EnumDict(dict):
Ethan Furman101e0742013-09-15 12:34:36 -070065 """Track enum member order and ensure member names are not reused.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070066
67 EnumMeta will use the names found in self._member_names as the
68 enumeration member names.
69
70 """
71 def __init__(self):
72 super().__init__()
73 self._member_names = []
74
75 def __setitem__(self, key, value):
Ethan Furman101e0742013-09-15 12:34:36 -070076 """Changes anything not dundered or not a descriptor.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070077
78 If an enum member name is used twice, an error is raised; duplicate
79 values are not checked for.
80
81 Single underscore (sunder) names are reserved.
82
83 """
84 if _is_sunder(key):
85 raise ValueError('_names_ are reserved for future Enum use')
Ethan Furman101e0742013-09-15 12:34:36 -070086 elif _is_dunder(key):
87 pass
88 elif key in self._member_names:
89 # descriptor overwriting an enum?
90 raise TypeError('Attempted to reuse key: %r' % key)
91 elif not _is_descriptor(value):
92 if key in self:
93 # enum overwriting a descriptor?
94 raise TypeError('Key already defined as: %r' % self[key])
Ethan Furman6b3d64a2013-06-14 16:55:46 -070095 self._member_names.append(key)
96 super().__setitem__(key, value)
97
98
Ethan Furman101e0742013-09-15 12:34:36 -070099
Ezio Melotti9a3777e2013-08-17 15:53:55 +0300100# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
101# until EnumMeta finishes running the first time the Enum class doesn't exist.
102# This is also why there are checks in EnumMeta like `if Enum is not None`
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700103Enum = None
104
105
106class EnumMeta(type):
107 """Metaclass for Enum"""
108 @classmethod
109 def __prepare__(metacls, cls, bases):
110 return _EnumDict()
111
112 def __new__(metacls, cls, bases, classdict):
113 # an Enum class is final once enumeration items have been defined; it
114 # cannot be mixed with other types (int, float, etc.) if it has an
115 # inherited __new__ unless a new __new__ is defined (or the resulting
116 # class will fail).
117 member_type, first_enum = metacls._get_mixins_(bases)
118 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
119 first_enum)
120
121 # save enum items into separate mapping so they don't get baked into
122 # the new class
123 members = {k: classdict[k] for k in classdict._member_names}
124 for name in classdict._member_names:
125 del classdict[name]
126
127 # check for illegal enum names (any others?)
128 invalid_names = set(members) & {'mro', }
129 if invalid_names:
130 raise ValueError('Invalid enum member name: {0}'.format(
131 ','.join(invalid_names)))
132
133 # create our new Enum type
134 enum_class = super().__new__(metacls, cls, bases, classdict)
Ethan Furman520ad572013-07-19 19:47:21 -0700135 enum_class._member_names_ = [] # names in definition order
136 enum_class._member_map_ = OrderedDict() # name->value map
Ethan Furman5e5a8232013-08-04 08:42:23 -0700137 enum_class._member_type_ = member_type
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700138
139 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700140 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700141
142 # check for a __getnewargs__, and if not present sabotage
143 # pickling, since it won't work anyway
144 if (member_type is not object and
145 member_type.__dict__.get('__getnewargs__') is None
146 ):
147 _make_class_unpicklable(enum_class)
148
149 # instantiate them, checking for duplicates as we go
150 # we instantiate first instead of checking for duplicates first in case
151 # a custom __new__ is doing something funky with the values -- such as
152 # auto-numbering ;)
153 for member_name in classdict._member_names:
154 value = members[member_name]
155 if not isinstance(value, tuple):
156 args = (value, )
157 else:
158 args = value
159 if member_type is tuple: # special case for tuple enums
160 args = (args, ) # wrap it one more time
161 if not use_args:
162 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700163 if not hasattr(enum_member, '_value_'):
164 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700165 else:
166 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700167 if not hasattr(enum_member, '_value_'):
168 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700169 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700170 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700171 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700172 enum_member.__init__(*args)
173 # If another member with the same value was already defined, the
174 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700175 for name, canonical_member in enum_class._member_map_.items():
176 if canonical_member.value == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700177 enum_member = canonical_member
178 break
179 else:
180 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700181 enum_class._member_names_.append(member_name)
182 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700183 try:
184 # This may fail if value is not hashable. We can't add the value
185 # to the map, and by-value lookups for this value will be
186 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700187 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700188 except TypeError:
189 pass
190
191 # double check that repr and friends are not the mixin's or various
192 # things break (such as pickle)
Ethan Furmanec15a822013-08-31 19:17:41 -0700193 for name in ('__repr__', '__str__', '__format__', '__getnewargs__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700194 class_method = getattr(enum_class, name)
195 obj_method = getattr(member_type, name, None)
196 enum_method = getattr(first_enum, name, None)
197 if obj_method is not None and obj_method is class_method:
198 setattr(enum_class, name, enum_method)
199
200 # replace any other __new__ with our own (as long as Enum is not None,
201 # anyway) -- again, this is to support pickle
202 if Enum is not None:
203 # if the user defined their own __new__, save it before it gets
204 # clobbered in case they subclass later
205 if save_new:
206 enum_class.__new_member__ = __new__
207 enum_class.__new__ = Enum.__new__
208 return enum_class
209
210 def __call__(cls, value, names=None, *, module=None, type=None):
211 """Either returns an existing member, or creates a new enum class.
212
213 This method is used both when an enum class is given a value to match
214 to an enumeration member (i.e. Color(3)) and for the functional API
215 (i.e. Color = Enum('Color', names='red green blue')).
216
217 When used for the functional API: `module`, if set, will be stored in
218 the new class' __module__ attribute; `type`, if set, will be mixed in
219 as the first base class.
220
221 Note: if `module` is not set this routine will attempt to discover the
222 calling module by walking the frame stack; if this is unsuccessful
223 the resulting class will not be pickleable.
224
225 """
226 if names is None: # simple value lookup
227 return cls.__new__(cls, value)
228 # otherwise, functional API: we're creating a new Enum type
229 return cls._create_(value, names, module=module, type=type)
230
231 def __contains__(cls, member):
Ethan Furman520ad572013-07-19 19:47:21 -0700232 return isinstance(member, cls) and member.name in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700233
Ethan Furman64a99722013-09-22 16:18:19 -0700234 def __delattr__(cls, attr):
235 # nicer error message when someone tries to delete an attribute
236 # (see issue19025).
237 if attr in cls._member_map_:
238 raise AttributeError(
239 "%s: cannot delete Enum member." % cls.__name__)
240 super().__delattr__(attr)
241
Ethan Furman388a3922013-08-12 06:51:41 -0700242 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700243 return (['__class__', '__doc__', '__members__', '__module__'] +
244 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700245
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700246 def __getattr__(cls, name):
247 """Return the enum member matching `name`
248
249 We use __getattr__ instead of descriptors or inserting into the enum
250 class' __dict__ in order to support `name` and `value` being both
251 properties for enum members (which live in the class' __dict__) and
252 enum members themselves.
253
254 """
255 if _is_dunder(name):
256 raise AttributeError(name)
257 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700258 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700259 except KeyError:
260 raise AttributeError(name) from None
261
262 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700263 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700264
265 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700266 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700267
268 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700269 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700270
Ethan Furman2131a4a2013-09-14 18:11:24 -0700271 @property
272 def __members__(cls):
273 """Returns a mapping of member name->value.
274
275 This mapping lists all enum members, including aliases. Note that this
276 is a read-only view of the internal mapping.
277
278 """
279 return MappingProxyType(cls._member_map_)
280
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700281 def __repr__(cls):
282 return "<enum %r>" % cls.__name__
283
Ethan Furman2131a4a2013-09-14 18:11:24 -0700284 def __reversed__(cls):
285 return (cls._member_map_[name] for name in reversed(cls._member_names_))
286
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700287 def __setattr__(cls, name, value):
288 """Block attempts to reassign Enum members.
289
290 A simple assignment to the class namespace only changes one of the
291 several possible ways to get an Enum member from the Enum class,
292 resulting in an inconsistent Enumeration.
293
294 """
295 member_map = cls.__dict__.get('_member_map_', {})
296 if name in member_map:
297 raise AttributeError('Cannot reassign members.')
298 super().__setattr__(name, value)
299
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700300 def _create_(cls, class_name, names=None, *, module=None, type=None):
301 """Convenience method to create a new Enum class.
302
303 `names` can be:
304
305 * A string containing member names, separated either with spaces or
306 commas. Values are auto-numbered from 1.
307 * An iterable of member names. Values are auto-numbered from 1.
308 * An iterable of (member name, value) pairs.
309 * A mapping of member name -> value.
310
311 """
312 metacls = cls.__class__
313 bases = (cls, ) if type is None else (type, cls)
314 classdict = metacls.__prepare__(class_name, bases)
315
316 # special processing needed for names?
317 if isinstance(names, str):
318 names = names.replace(',', ' ').split()
319 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
320 names = [(e, i) for (i, e) in enumerate(names, 1)]
321
322 # Here, names is either an iterable of (name, value) or a mapping.
323 for item in names:
324 if isinstance(item, str):
325 member_name, member_value = item, names[item]
326 else:
327 member_name, member_value = item
328 classdict[member_name] = member_value
329 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
330
331 # TODO: replace the frame hack if a blessed way to know the calling
332 # module is ever developed
333 if module is None:
334 try:
335 module = sys._getframe(2).f_globals['__name__']
336 except (AttributeError, ValueError) as exc:
337 pass
338 if module is None:
339 _make_class_unpicklable(enum_class)
340 else:
341 enum_class.__module__ = module
342
343 return enum_class
344
345 @staticmethod
346 def _get_mixins_(bases):
347 """Returns the type for creating enum members, and the first inherited
348 enum class.
349
350 bases: the tuple of bases that was given to __new__
351
352 """
353 if not bases:
354 return object, Enum
355
356 # double check that we are not subclassing a class with existing
357 # enumeration members; while we're at it, see if any other data
358 # type has been mixed in so we can use the correct __new__
359 member_type = first_enum = None
360 for base in bases:
361 if (base is not Enum and
362 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700363 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700364 raise TypeError("Cannot extend enumerations")
365 # base is now the last base in bases
366 if not issubclass(base, Enum):
367 raise TypeError("new enumerations must be created as "
368 "`ClassName([mixin_type,] enum_type)`")
369
370 # get correct mix-in type (either mix-in type of Enum subclass, or
371 # first base if last base is Enum)
372 if not issubclass(bases[0], Enum):
373 member_type = bases[0] # first data type
374 first_enum = bases[-1] # enum type
375 else:
376 for base in bases[0].__mro__:
377 # most common: (IntEnum, int, Enum, object)
378 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
379 # <class 'int'>, <Enum 'Enum'>,
380 # <class 'object'>)
381 if issubclass(base, Enum):
382 if first_enum is None:
383 first_enum = base
384 else:
385 if member_type is None:
386 member_type = base
387
388 return member_type, first_enum
389
390 @staticmethod
391 def _find_new_(classdict, member_type, first_enum):
392 """Returns the __new__ to be used for creating the enum members.
393
394 classdict: the class dictionary given to __new__
395 member_type: the data type whose __new__ will be used by default
396 first_enum: enumeration to check for an overriding __new__
397
398 """
399 # now find the correct __new__, checking to see of one was defined
400 # by the user; also check earlier enum classes in case a __new__ was
401 # saved as __new_member__
402 __new__ = classdict.get('__new__', None)
403
404 # should __new__ be saved as __new_member__ later?
405 save_new = __new__ is not None
406
407 if __new__ is None:
408 # check all possibles for __new_member__ before falling back to
409 # __new__
410 for method in ('__new_member__', '__new__'):
411 for possible in (member_type, first_enum):
412 target = getattr(possible, method, None)
413 if target not in {
414 None,
415 None.__new__,
416 object.__new__,
417 Enum.__new__,
418 }:
419 __new__ = target
420 break
421 if __new__ is not None:
422 break
423 else:
424 __new__ = object.__new__
425
426 # if a non-object.__new__ is used then whatever value/tuple was
427 # assigned to the enum member name will be passed to __new__ and to the
428 # new enum member's __init__
429 if __new__ is object.__new__:
430 use_args = False
431 else:
432 use_args = True
433
434 return __new__, save_new, use_args
435
436
437class Enum(metaclass=EnumMeta):
438 """Generic enumeration.
439
440 Derive from this class to define new enumerations.
441
442 """
443 def __new__(cls, value):
444 # all enum instances are actually created during class construction
445 # without calling this method; this method is called by the metaclass'
446 # __call__ (i.e. Color(3) ), and by pickle
447 if type(value) is cls:
448 # For lookups like Color(Color.red)
449 return value
450 # by-value search for a matching enum member
451 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700452 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700453 if value in cls._value2member_map_:
454 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700455 except TypeError:
456 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700457 for member in cls._member_map_.values():
Ethan Furman2aa27322013-07-19 19:35:56 -0700458 if member.value == value:
459 return member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700460 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
461
462 def __repr__(self):
463 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700464 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700465
466 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700467 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700468
Ethan Furman388a3922013-08-12 06:51:41 -0700469 def __dir__(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700470 added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
Ethan Furman64a99722013-09-22 16:18:19 -0700471 return (['__class__', '__doc__', '__module__', 'name', 'value'] +
472 added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700473
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700474 def __eq__(self, other):
475 if type(other) is self.__class__:
476 return self is other
477 return NotImplemented
478
Ethan Furmanec15a822013-08-31 19:17:41 -0700479 def __format__(self, format_spec):
480 # mixed-in Enums should use the mixed-in type's __format__, otherwise
481 # we can get strange results with the Enum name showing up instead of
482 # the value
483
484 # pure Enum branch
485 if self._member_type_ is object:
486 cls = str
487 val = str(self)
488 # mix-in branch
489 else:
490 cls = self._member_type_
491 val = self.value
492 return cls.__format__(val, format_spec)
493
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700494 def __getnewargs__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700495 return (self._value_, )
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700496
497 def __hash__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700498 return hash(self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700499
500 # _RouteClassAttributeToGetattr is used to provide access to the `name`
501 # and `value` properties of enum members while keeping some measure of
502 # protection from modification, while still allowing for an enumeration
503 # to have members named `name` and `value`. This works because enumeration
504 # members are not set directly on the enum class -- __getattr__ is
505 # used to look them up.
506
507 @_RouteClassAttributeToGetattr
508 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700509 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700510 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700511
512 @_RouteClassAttributeToGetattr
513 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700514 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700515 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700516
517
518class IntEnum(int, Enum):
519 """Enum where members are also (and must be) ints"""
Ethan Furmanf24bb352013-07-18 17:05:39 -0700520
521
522def unique(enumeration):
523 """Class decorator for enumerations ensuring unique member values."""
524 duplicates = []
525 for name, member in enumeration.__members__.items():
526 if name != member.name:
527 duplicates.append((name, member.name))
528 if duplicates:
529 alias_details = ', '.join(
530 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
531 raise ValueError('duplicate values found in %r: %s' %
532 (enumeration, alias_details))
533 return enumeration