blob: c9bd7c047c50cd15fea6289b6397c66fa00ccee9 [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 Furmandc870522014-02-18 12:37:12 -0800119 # pickling, since it won't work anyway.
120 # if new class implements its own __reduce_ex__, do not sabotage
121 if classdict.get('__reduce_ex__') is None:
122 if member_type is not object:
123 methods = ('__getnewargs_ex__', '__getnewargs__',
124 '__reduce_ex__', '__reduce__')
125 if not any(map(member_type.__dict__.get, methods)):
126 _make_class_unpicklable(enum_class)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700127
128 # instantiate them, checking for duplicates as we go
129 # we instantiate first instead of checking for duplicates first in case
130 # a custom __new__ is doing something funky with the values -- such as
131 # auto-numbering ;)
132 for member_name in classdict._member_names:
133 value = members[member_name]
134 if not isinstance(value, tuple):
135 args = (value, )
136 else:
137 args = value
138 if member_type is tuple: # special case for tuple enums
139 args = (args, ) # wrap it one more time
140 if not use_args:
141 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700142 if not hasattr(enum_member, '_value_'):
143 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700144 else:
145 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700146 if not hasattr(enum_member, '_value_'):
147 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700148 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700149 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700150 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700151 enum_member.__init__(*args)
152 # If another member with the same value was already defined, the
153 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700154 for name, canonical_member in enum_class._member_map_.items():
155 if canonical_member.value == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700156 enum_member = canonical_member
157 break
158 else:
159 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700160 enum_class._member_names_.append(member_name)
161 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700162 try:
163 # This may fail if value is not hashable. We can't add the value
164 # to the map, and by-value lookups for this value will be
165 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700166 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700167 except TypeError:
168 pass
169
170 # double check that repr and friends are not the mixin's or various
171 # things break (such as pickle)
Ethan Furmandc870522014-02-18 12:37:12 -0800172 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700173 class_method = getattr(enum_class, name)
174 obj_method = getattr(member_type, name, None)
175 enum_method = getattr(first_enum, name, None)
176 if obj_method is not None and obj_method is class_method:
177 setattr(enum_class, name, enum_method)
178
179 # replace any other __new__ with our own (as long as Enum is not None,
180 # anyway) -- again, this is to support pickle
181 if Enum is not None:
182 # if the user defined their own __new__, save it before it gets
183 # clobbered in case they subclass later
184 if save_new:
185 enum_class.__new_member__ = __new__
186 enum_class.__new__ = Enum.__new__
187 return enum_class
188
Ethan Furmanca1b7942014-02-08 11:36:27 -0800189 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700190 """Either returns an existing member, or creates a new enum class.
191
192 This method is used both when an enum class is given a value to match
193 to an enumeration member (i.e. Color(3)) and for the functional API
194 (i.e. Color = Enum('Color', names='red green blue')).
195
196 When used for the functional API: `module`, if set, will be stored in
Ethan Furmandc870522014-02-18 12:37:12 -0800197 the new class' __module__ attribute; `qualname`, if set, will be stored
198 in the new class' __qualname__ attribute; `type`, if set, will be mixed
199 in as the first base class.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700200
201 Note: if `module` is not set this routine will attempt to discover the
202 calling module by walking the frame stack; if this is unsuccessful
203 the resulting class will not be pickleable.
204
205 """
206 if names is None: # simple value lookup
207 return cls.__new__(cls, value)
208 # otherwise, functional API: we're creating a new Enum type
Ethan Furmanca1b7942014-02-08 11:36:27 -0800209 return cls._create_(value, names, module=module, qualname=qualname, type=type)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700210
211 def __contains__(cls, member):
Ethan Furman520ad572013-07-19 19:47:21 -0700212 return isinstance(member, cls) and member.name in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700213
Ethan Furman64a99722013-09-22 16:18:19 -0700214 def __delattr__(cls, attr):
215 # nicer error message when someone tries to delete an attribute
216 # (see issue19025).
217 if attr in cls._member_map_:
218 raise AttributeError(
219 "%s: cannot delete Enum member." % cls.__name__)
220 super().__delattr__(attr)
221
Ethan Furman388a3922013-08-12 06:51:41 -0700222 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700223 return (['__class__', '__doc__', '__members__', '__module__'] +
224 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700225
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700226 def __getattr__(cls, name):
227 """Return the enum member matching `name`
228
229 We use __getattr__ instead of descriptors or inserting into the enum
230 class' __dict__ in order to support `name` and `value` being both
231 properties for enum members (which live in the class' __dict__) and
232 enum members themselves.
233
234 """
235 if _is_dunder(name):
236 raise AttributeError(name)
237 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700238 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700239 except KeyError:
240 raise AttributeError(name) from None
241
242 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700243 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700244
245 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700246 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700247
248 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700249 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700250
Ethan Furman2131a4a2013-09-14 18:11:24 -0700251 @property
252 def __members__(cls):
253 """Returns a mapping of member name->value.
254
255 This mapping lists all enum members, including aliases. Note that this
256 is a read-only view of the internal mapping.
257
258 """
259 return MappingProxyType(cls._member_map_)
260
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700261 def __repr__(cls):
262 return "<enum %r>" % cls.__name__
263
Ethan Furman2131a4a2013-09-14 18:11:24 -0700264 def __reversed__(cls):
265 return (cls._member_map_[name] for name in reversed(cls._member_names_))
266
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700267 def __setattr__(cls, name, value):
268 """Block attempts to reassign Enum members.
269
270 A simple assignment to the class namespace only changes one of the
271 several possible ways to get an Enum member from the Enum class,
272 resulting in an inconsistent Enumeration.
273
274 """
275 member_map = cls.__dict__.get('_member_map_', {})
276 if name in member_map:
277 raise AttributeError('Cannot reassign members.')
278 super().__setattr__(name, value)
279
Ethan Furmanca1b7942014-02-08 11:36:27 -0800280 def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700281 """Convenience method to create a new Enum class.
282
283 `names` can be:
284
285 * A string containing member names, separated either with spaces or
286 commas. Values are auto-numbered from 1.
287 * An iterable of member names. Values are auto-numbered from 1.
288 * An iterable of (member name, value) pairs.
289 * A mapping of member name -> value.
290
291 """
292 metacls = cls.__class__
293 bases = (cls, ) if type is None else (type, cls)
294 classdict = metacls.__prepare__(class_name, bases)
295
296 # special processing needed for names?
297 if isinstance(names, str):
298 names = names.replace(',', ' ').split()
299 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
300 names = [(e, i) for (i, e) in enumerate(names, 1)]
301
302 # Here, names is either an iterable of (name, value) or a mapping.
303 for item in names:
304 if isinstance(item, str):
305 member_name, member_value = item, names[item]
306 else:
307 member_name, member_value = item
308 classdict[member_name] = member_value
309 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
310
311 # TODO: replace the frame hack if a blessed way to know the calling
312 # module is ever developed
313 if module is None:
314 try:
315 module = sys._getframe(2).f_globals['__name__']
316 except (AttributeError, ValueError) as exc:
317 pass
318 if module is None:
319 _make_class_unpicklable(enum_class)
320 else:
321 enum_class.__module__ = module
Ethan Furmanca1b7942014-02-08 11:36:27 -0800322 if qualname is not None:
323 enum_class.__qualname__ = qualname
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700324
325 return enum_class
326
327 @staticmethod
328 def _get_mixins_(bases):
329 """Returns the type for creating enum members, and the first inherited
330 enum class.
331
332 bases: the tuple of bases that was given to __new__
333
334 """
335 if not bases:
336 return object, Enum
337
338 # double check that we are not subclassing a class with existing
339 # enumeration members; while we're at it, see if any other data
340 # type has been mixed in so we can use the correct __new__
341 member_type = first_enum = None
342 for base in bases:
343 if (base is not Enum and
344 issubclass(base, Enum) and
Ethan Furman520ad572013-07-19 19:47:21 -0700345 base._member_names_):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700346 raise TypeError("Cannot extend enumerations")
347 # base is now the last base in bases
348 if not issubclass(base, Enum):
349 raise TypeError("new enumerations must be created as "
350 "`ClassName([mixin_type,] enum_type)`")
351
352 # get correct mix-in type (either mix-in type of Enum subclass, or
353 # first base if last base is Enum)
354 if not issubclass(bases[0], Enum):
355 member_type = bases[0] # first data type
356 first_enum = bases[-1] # enum type
357 else:
358 for base in bases[0].__mro__:
359 # most common: (IntEnum, int, Enum, object)
360 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
361 # <class 'int'>, <Enum 'Enum'>,
362 # <class 'object'>)
363 if issubclass(base, Enum):
364 if first_enum is None:
365 first_enum = base
366 else:
367 if member_type is None:
368 member_type = base
369
370 return member_type, first_enum
371
372 @staticmethod
373 def _find_new_(classdict, member_type, first_enum):
374 """Returns the __new__ to be used for creating the enum members.
375
376 classdict: the class dictionary given to __new__
377 member_type: the data type whose __new__ will be used by default
378 first_enum: enumeration to check for an overriding __new__
379
380 """
381 # now find the correct __new__, checking to see of one was defined
382 # by the user; also check earlier enum classes in case a __new__ was
383 # saved as __new_member__
384 __new__ = classdict.get('__new__', None)
385
386 # should __new__ be saved as __new_member__ later?
387 save_new = __new__ is not None
388
389 if __new__ is None:
390 # check all possibles for __new_member__ before falling back to
391 # __new__
392 for method in ('__new_member__', '__new__'):
393 for possible in (member_type, first_enum):
394 target = getattr(possible, method, None)
395 if target not in {
396 None,
397 None.__new__,
398 object.__new__,
399 Enum.__new__,
400 }:
401 __new__ = target
402 break
403 if __new__ is not None:
404 break
405 else:
406 __new__ = object.__new__
407
408 # if a non-object.__new__ is used then whatever value/tuple was
409 # assigned to the enum member name will be passed to __new__ and to the
410 # new enum member's __init__
411 if __new__ is object.__new__:
412 use_args = False
413 else:
414 use_args = True
415
416 return __new__, save_new, use_args
417
418
419class Enum(metaclass=EnumMeta):
420 """Generic enumeration.
421
422 Derive from this class to define new enumerations.
423
424 """
425 def __new__(cls, value):
426 # all enum instances are actually created during class construction
427 # without calling this method; this method is called by the metaclass'
428 # __call__ (i.e. Color(3) ), and by pickle
429 if type(value) is cls:
430 # For lookups like Color(Color.red)
431 return value
432 # by-value search for a matching enum member
433 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700434 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700435 if value in cls._value2member_map_:
436 return cls._value2member_map_[value]
Ethan Furman2aa27322013-07-19 19:35:56 -0700437 except TypeError:
438 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700439 for member in cls._member_map_.values():
Ethan Furman2aa27322013-07-19 19:35:56 -0700440 if member.value == value:
441 return member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700442 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
443
444 def __repr__(self):
445 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700446 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700447
448 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700449 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700450
Ethan Furman388a3922013-08-12 06:51:41 -0700451 def __dir__(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700452 added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
Ethan Furman64a99722013-09-22 16:18:19 -0700453 return (['__class__', '__doc__', '__module__', 'name', 'value'] +
454 added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700455
Ethan Furmanec15a822013-08-31 19:17:41 -0700456 def __format__(self, format_spec):
457 # mixed-in Enums should use the mixed-in type's __format__, otherwise
458 # we can get strange results with the Enum name showing up instead of
459 # the value
460
461 # pure Enum branch
462 if self._member_type_ is object:
463 cls = str
464 val = str(self)
465 # mix-in branch
466 else:
467 cls = self._member_type_
468 val = self.value
469 return cls.__format__(val, format_spec)
470
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700471 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):
Ethan Furmandc870522014-02-18 12:37:12 -0800475 return self.__class__, (self._value_, )
Ethan Furmanca1b7942014-02-08 11:36:27 -0800476
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