blob: d670ad7d86196f10b8409b641c546603c80864dc [file] [log] [blame]
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001import sys
Ethan Furmane03ea372013-09-25 07:14:41 -07002from types import MappingProxyType, DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -07003
Ethan Furmane5754ab2015-09-17 22:03:52 -07004
Ethan Furmanc16595e2016-09-10 23:36:59 -07005__all__ = [
6 'EnumMeta',
Ethan Furman0063ff42020-09-21 17:23:13 -07007 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
Ethan Furmanc16595e2016-09-10 23:36:59 -07008 'auto', 'unique',
9 ]
Ethan Furman6b3d64a2013-06-14 16:55:46 -070010
11
Ethan Furman101e0742013-09-15 12:34:36 -070012def _is_descriptor(obj):
13 """Returns True if obj is a descriptor, False otherwise."""
14 return (
15 hasattr(obj, '__get__') or
16 hasattr(obj, '__set__') or
17 hasattr(obj, '__delete__'))
18
19
Ethan Furman6b3d64a2013-06-14 16:55:46 -070020def _is_dunder(name):
21 """Returns True if a __dunder__ name, False otherwise."""
Brennan D Baraban8b914d22019-03-03 14:09:11 -080022 return (len(name) > 4 and
23 name[:2] == name[-2:] == '__' and
24 name[2] != '_' and
25 name[-3] != '_')
Ethan Furman6b3d64a2013-06-14 16:55:46 -070026
27
28def _is_sunder(name):
29 """Returns True if a _sunder_ name, False otherwise."""
Brennan D Baraban8b914d22019-03-03 14:09:11 -080030 return (len(name) > 2 and
31 name[0] == name[-1] == '_' and
Ethan Furman6b3d64a2013-06-14 16:55:46 -070032 name[1:2] != '_' and
Brennan D Baraban8b914d22019-03-03 14:09:11 -080033 name[-2:-1] != '_')
34
Ethan Furman6b3d64a2013-06-14 16:55:46 -070035
Ethan Furman6b3d64a2013-06-14 16:55:46 -070036def _make_class_unpicklable(cls):
37 """Make the given class un-picklable."""
Ethan Furmanca1b7942014-02-08 11:36:27 -080038 def _break_on_call_reduce(self, proto):
Ethan Furman6b3d64a2013-06-14 16:55:46 -070039 raise TypeError('%r cannot be pickled' % self)
Ethan Furmanca1b7942014-02-08 11:36:27 -080040 cls.__reduce_ex__ = _break_on_call_reduce
Ethan Furman6b3d64a2013-06-14 16:55:46 -070041 cls.__module__ = '<unknown>'
42
Ethan Furman3515dcc2016-09-18 13:15:41 -070043_auto_null = object()
Ethan Furmanc16595e2016-09-10 23:36:59 -070044class auto:
45 """
46 Instances are replaced with an appropriate value in Enum class suites.
47 """
Ethan Furman3515dcc2016-09-18 13:15:41 -070048 value = _auto_null
Ethan Furmanc16595e2016-09-10 23:36:59 -070049
Ethan Furman101e0742013-09-15 12:34:36 -070050
Ethan Furman6b3d64a2013-06-14 16:55:46 -070051class _EnumDict(dict):
Ethan Furman101e0742013-09-15 12:34:36 -070052 """Track enum member order and ensure member names are not reused.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070053
54 EnumMeta will use the names found in self._member_names as the
55 enumeration member names.
56
57 """
58 def __init__(self):
59 super().__init__()
60 self._member_names = []
Ethan Furmanc16595e2016-09-10 23:36:59 -070061 self._last_values = []
Ethan Furmana4b1bb42018-01-22 07:56:37 -080062 self._ignore = []
Ethan Onstottd9a43e22020-04-28 13:20:55 -040063 self._auto_called = False
Ethan Furman6b3d64a2013-06-14 16:55:46 -070064
65 def __setitem__(self, key, value):
Ethan Furman101e0742013-09-15 12:34:36 -070066 """Changes anything not dundered or not a descriptor.
Ethan Furman6b3d64a2013-06-14 16:55:46 -070067
68 If an enum member name is used twice, an error is raised; duplicate
69 values are not checked for.
70
71 Single underscore (sunder) names are reserved.
72
73 """
74 if _is_sunder(key):
Ethan Furmanee47e5c2016-08-31 00:12:15 -070075 if key not in (
Ethan Furman3515dcc2016-09-18 13:15:41 -070076 '_order_', '_create_pseudo_member_',
Ethan Furmana4b1bb42018-01-22 07:56:37 -080077 '_generate_next_value_', '_missing_', '_ignore_',
Ethan Furmanee47e5c2016-08-31 00:12:15 -070078 ):
Zackery Spytz2ec67522020-09-13 14:27:51 -060079 raise ValueError(f'_sunder_ names, such as "{key}", are '
80 'reserved for future Enum use')
Ethan Furmanc16595e2016-09-10 23:36:59 -070081 if key == '_generate_next_value_':
Ethan Onstottd9a43e22020-04-28 13:20:55 -040082 # check if members already defined as auto()
83 if self._auto_called:
84 raise TypeError("_generate_next_value_ must be defined before members")
Ethan Furmanc16595e2016-09-10 23:36:59 -070085 setattr(self, '_generate_next_value', value)
Ethan Furmana4b1bb42018-01-22 07:56:37 -080086 elif key == '_ignore_':
87 if isinstance(value, str):
88 value = value.replace(',',' ').split()
89 else:
90 value = list(value)
91 self._ignore = value
92 already = set(value) & set(self._member_names)
93 if already:
94 raise ValueError('_ignore_ cannot specify already set names: %r' % (already, ))
Ethan Furman101e0742013-09-15 12:34:36 -070095 elif _is_dunder(key):
Ethan Furmane8e61272016-08-20 07:19:31 -070096 if key == '__order__':
97 key = '_order_'
Ethan Furman101e0742013-09-15 12:34:36 -070098 elif key in self._member_names:
99 # descriptor overwriting an enum?
100 raise TypeError('Attempted to reuse key: %r' % key)
Ethan Furmana4b1bb42018-01-22 07:56:37 -0800101 elif key in self._ignore:
102 pass
Ethan Furman101e0742013-09-15 12:34:36 -0700103 elif not _is_descriptor(value):
104 if key in self:
105 # enum overwriting a descriptor?
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700106 raise TypeError('%r already defined as: %r' % (key, self[key]))
Ethan Furmanc16595e2016-09-10 23:36:59 -0700107 if isinstance(value, auto):
Ethan Furman3515dcc2016-09-18 13:15:41 -0700108 if value.value == _auto_null:
109 value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
Ethan Furmanfc23a942020-09-16 12:37:54 -0700110 self._auto_called = True
Ethan Furman3515dcc2016-09-18 13:15:41 -0700111 value = value.value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700112 self._member_names.append(key)
Ethan Furmanc16595e2016-09-10 23:36:59 -0700113 self._last_values.append(value)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700114 super().__setitem__(key, value)
115
116
Ezio Melotti9a3777e2013-08-17 15:53:55 +0300117# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
118# until EnumMeta finishes running the first time the Enum class doesn't exist.
119# This is also why there are checks in EnumMeta like `if Enum is not None`
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700120Enum = None
121
Ethan Furman332dbc72016-08-20 00:00:52 -0700122
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700123class EnumMeta(type):
124 """Metaclass for Enum"""
125 @classmethod
Ethan Furman332dbc72016-08-20 00:00:52 -0700126 def __prepare__(metacls, cls, bases):
Ethan Furman3064dbf2020-09-16 07:11:57 -0700127 # check that previous enum members do not exist
128 metacls._check_for_existing_members(cls, bases)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700129 # create the namespace dict
130 enum_dict = _EnumDict()
131 # inherit previous flags and _generate_next_value_ function
Ethan Furman3064dbf2020-09-16 07:11:57 -0700132 member_type, first_enum = metacls._get_mixins_(cls, bases)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700133 if first_enum is not None:
134 enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
135 return enum_dict
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700136
Ethan Furman65a5a472016-09-01 23:55:19 -0700137 def __new__(metacls, cls, bases, classdict):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700138 # an Enum class is final once enumeration items have been defined; it
139 # cannot be mixed with other types (int, float, etc.) if it has an
140 # inherited __new__ unless a new __new__ is defined (or the resulting
141 # class will fail).
Ethan Furmana4b1bb42018-01-22 07:56:37 -0800142 #
143 # remove any keys listed in _ignore_
144 classdict.setdefault('_ignore_', []).append('_ignore_')
145 ignore = classdict['_ignore_']
146 for key in ignore:
147 classdict.pop(key, None)
Ethan Furman3064dbf2020-09-16 07:11:57 -0700148 member_type, first_enum = metacls._get_mixins_(cls, bases)
Ethan Furmanc2667362020-12-07 00:17:31 -0800149 __new__, save_new, use_args = metacls._find_new_(
150 classdict, member_type, first_enum,
151 )
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700152
153 # save enum items into separate mapping so they don't get baked into
154 # the new class
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700155 enum_members = {k: classdict[k] for k in classdict._member_names}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700156 for name in classdict._member_names:
157 del classdict[name]
158
Ethan Furmane8e61272016-08-20 07:19:31 -0700159 # adjust the sunders
160 _order_ = classdict.pop('_order_', None)
161
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700162 # check for illegal enum names (any others?)
Brennan D Baraban8b914d22019-03-03 14:09:11 -0800163 invalid_names = set(enum_members) & {'mro', ''}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700164 if invalid_names:
165 raise ValueError('Invalid enum member name: {0}'.format(
166 ','.join(invalid_names)))
167
Ethan Furman48a724f2015-04-11 23:23:06 -0700168 # create a default docstring if one has not been provided
169 if '__doc__' not in classdict:
170 classdict['__doc__'] = 'An enumeration.'
171
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700172 # create our new Enum type
173 enum_class = super().__new__(metacls, cls, bases, classdict)
Ethan Furman520ad572013-07-19 19:47:21 -0700174 enum_class._member_names_ = [] # names in definition order
INADA Naokie57f91a2018-06-19 01:14:26 +0900175 enum_class._member_map_ = {} # name->value map
Ethan Furman5e5a8232013-08-04 08:42:23 -0700176 enum_class._member_type_ = member_type
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700177
orlnub1230fb9fad2018-09-12 20:28:53 +0300178 # save DynamicClassAttribute attributes from super classes so we know
179 # if we can take the shortcut of storing members in the class dict
180 dynamic_attributes = {k for c in enum_class.mro()
181 for k, v in c.__dict__.items()
182 if isinstance(v, DynamicClassAttribute)}
Ethan Furman354ecf12015-03-11 08:43:12 -0700183
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700184 # Reverse value->name map for hashable values.
Ethan Furman520ad572013-07-19 19:47:21 -0700185 enum_class._value2member_map_ = {}
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700186
Ethan Furman2da95042014-03-03 12:42:52 -0800187 # If a custom type is mixed into the Enum, and it does not know how
188 # to pickle itself, pickle.dumps will succeed but pickle.loads will
189 # fail. Rather than have the error show up later and possibly far
190 # from the source, sabotage the pickle protocol for this class so
191 # that pickle.dumps also fails.
192 #
193 # However, if the new class implements its own __reduce_ex__, do not
194 # sabotage -- it's on them to make sure it works correctly. We use
195 # __reduce_ex__ instead of any of the others as it is preferred by
196 # pickle over __reduce__, and it handles all pickle protocols.
197 if '__reduce_ex__' not in classdict:
Ethan Furmandc870522014-02-18 12:37:12 -0800198 if member_type is not object:
199 methods = ('__getnewargs_ex__', '__getnewargs__',
200 '__reduce_ex__', '__reduce__')
Ethan Furman2da95042014-03-03 12:42:52 -0800201 if not any(m in member_type.__dict__ for m in methods):
Ethan Furmandc870522014-02-18 12:37:12 -0800202 _make_class_unpicklable(enum_class)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700203
204 # instantiate them, checking for duplicates as we go
205 # we instantiate first instead of checking for duplicates first in case
206 # a custom __new__ is doing something funky with the values -- such as
207 # auto-numbering ;)
208 for member_name in classdict._member_names:
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700209 value = enum_members[member_name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700210 if not isinstance(value, tuple):
211 args = (value, )
212 else:
213 args = value
214 if member_type is tuple: # special case for tuple enums
215 args = (args, ) # wrap it one more time
216 if not use_args:
217 enum_member = __new__(enum_class)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700218 if not hasattr(enum_member, '_value_'):
219 enum_member._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700220 else:
221 enum_member = __new__(enum_class, *args)
Ethan Furmanb41803e2013-07-25 13:50:45 -0700222 if not hasattr(enum_member, '_value_'):
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700223 if member_type is object:
224 enum_member._value_ = value
225 else:
226 enum_member._value_ = member_type(*args)
Ethan Furman520ad572013-07-19 19:47:21 -0700227 value = enum_member._value_
Ethan Furman520ad572013-07-19 19:47:21 -0700228 enum_member._name_ = member_name
Ethan Furmanc850f342013-09-15 16:59:35 -0700229 enum_member.__objclass__ = enum_class
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700230 enum_member.__init__(*args)
231 # If another member with the same value was already defined, the
232 # new member becomes an alias to the existing one.
Ethan Furman520ad572013-07-19 19:47:21 -0700233 for name, canonical_member in enum_class._member_map_.items():
Ethan Furman0081f232014-09-16 17:31:23 -0700234 if canonical_member._value_ == enum_member._value_:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700235 enum_member = canonical_member
236 break
237 else:
238 # Aliases don't appear in member names (only in __members__).
Ethan Furman520ad572013-07-19 19:47:21 -0700239 enum_class._member_names_.append(member_name)
Ethan Furman354ecf12015-03-11 08:43:12 -0700240 # performance boost for any member that would not shadow
241 # a DynamicClassAttribute
orlnub1230fb9fad2018-09-12 20:28:53 +0300242 if member_name not in dynamic_attributes:
Ethan Furman354ecf12015-03-11 08:43:12 -0700243 setattr(enum_class, member_name, enum_member)
244 # now add to _member_map_
Ethan Furman520ad572013-07-19 19:47:21 -0700245 enum_class._member_map_[member_name] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700246 try:
247 # This may fail if value is not hashable. We can't add the value
248 # to the map, and by-value lookups for this value will be
249 # linear.
Ethan Furman520ad572013-07-19 19:47:21 -0700250 enum_class._value2member_map_[value] = enum_member
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700251 except TypeError:
252 pass
253
254 # double check that repr and friends are not the mixin's or various
255 # things break (such as pickle)
Ethan Furman22415ad2020-09-15 16:28:25 -0700256 # however, if the method is defined in the Enum itself, don't replace
257 # it
Ethan Furmandc870522014-02-18 12:37:12 -0800258 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
Ethan Furman22415ad2020-09-15 16:28:25 -0700259 if name in classdict:
260 continue
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700261 class_method = getattr(enum_class, name)
262 obj_method = getattr(member_type, name, None)
263 enum_method = getattr(first_enum, name, None)
264 if obj_method is not None and obj_method is class_method:
265 setattr(enum_class, name, enum_method)
266
267 # replace any other __new__ with our own (as long as Enum is not None,
268 # anyway) -- again, this is to support pickle
269 if Enum is not None:
270 # if the user defined their own __new__, save it before it gets
271 # clobbered in case they subclass later
272 if save_new:
273 enum_class.__new_member__ = __new__
274 enum_class.__new__ = Enum.__new__
Ethan Furmane8e61272016-08-20 07:19:31 -0700275
276 # py3 support for definition order (helps keep py2/py3 code in sync)
277 if _order_ is not None:
278 if isinstance(_order_, str):
279 _order_ = _order_.replace(',', ' ').split()
280 if _order_ != enum_class._member_names_:
281 raise TypeError('member order does not match _order_')
282
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700283 return enum_class
284
Ethan Furman5de67b12016-04-13 23:52:09 -0700285 def __bool__(self):
286 """
287 classes/types should always be True.
288 """
289 return True
290
Ethan Furmand9925a12014-09-16 20:35:55 -0700291 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700292 """Either returns an existing member, or creates a new enum class.
293
294 This method is used both when an enum class is given a value to match
295 to an enumeration member (i.e. Color(3)) and for the functional API
Ethan Furman23bb6f42016-11-21 09:22:05 -0800296 (i.e. Color = Enum('Color', names='RED GREEN BLUE')).
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700297
Ethan Furman2da95042014-03-03 12:42:52 -0800298 When used for the functional API:
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700299
Ethan Furman2da95042014-03-03 12:42:52 -0800300 `value` will be the name of the new class.
301
302 `names` should be either a string of white-space/comma delimited names
Ethan Furmand9925a12014-09-16 20:35:55 -0700303 (values will start at `start`), or an iterator/mapping of name, value pairs.
Ethan Furman2da95042014-03-03 12:42:52 -0800304
305 `module` should be set to the module this class is being created in;
306 if it is not set, an attempt to find that module will be made, but if
307 it fails the class will not be picklable.
308
309 `qualname` should be set to the actual location this class can be found
310 at in its module; by default it is set to the global scope. If this is
311 not correct, unpickling will fail in some circumstances.
312
313 `type`, if set, will be mixed in as the first base class.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700314
315 """
316 if names is None: # simple value lookup
317 return cls.__new__(cls, value)
318 # otherwise, functional API: we're creating a new Enum type
Ethan Furmand9925a12014-09-16 20:35:55 -0700319 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700320
321 def __contains__(cls, member):
Rahul Jha94306522018-09-10 23:51:04 +0530322 if not isinstance(member, Enum):
323 raise TypeError(
324 "unsupported operand type(s) for 'in': '%s' and '%s'" % (
325 type(member).__qualname__, cls.__class__.__qualname__))
Ethan Furman0081f232014-09-16 17:31:23 -0700326 return isinstance(member, cls) and member._name_ in cls._member_map_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700327
Ethan Furman64a99722013-09-22 16:18:19 -0700328 def __delattr__(cls, attr):
329 # nicer error message when someone tries to delete an attribute
330 # (see issue19025).
331 if attr in cls._member_map_:
332 raise AttributeError(
333 "%s: cannot delete Enum member." % cls.__name__)
334 super().__delattr__(attr)
335
Ethan Furman388a3922013-08-12 06:51:41 -0700336 def __dir__(self):
Ethan Furman64a99722013-09-22 16:18:19 -0700337 return (['__class__', '__doc__', '__members__', '__module__'] +
338 self._member_names_)
Ethan Furman388a3922013-08-12 06:51:41 -0700339
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700340 def __getattr__(cls, name):
341 """Return the enum member matching `name`
342
343 We use __getattr__ instead of descriptors or inserting into the enum
344 class' __dict__ in order to support `name` and `value` being both
345 properties for enum members (which live in the class' __dict__) and
346 enum members themselves.
347
348 """
349 if _is_dunder(name):
350 raise AttributeError(name)
351 try:
Ethan Furman520ad572013-07-19 19:47:21 -0700352 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700353 except KeyError:
354 raise AttributeError(name) from None
355
356 def __getitem__(cls, name):
Ethan Furman520ad572013-07-19 19:47:21 -0700357 return cls._member_map_[name]
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700358
359 def __iter__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700360 return (cls._member_map_[name] for name in cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700361
362 def __len__(cls):
Ethan Furman520ad572013-07-19 19:47:21 -0700363 return len(cls._member_names_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700364
Ethan Furman2131a4a2013-09-14 18:11:24 -0700365 @property
366 def __members__(cls):
367 """Returns a mapping of member name->value.
368
369 This mapping lists all enum members, including aliases. Note that this
370 is a read-only view of the internal mapping.
371
372 """
373 return MappingProxyType(cls._member_map_)
374
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700375 def __repr__(cls):
376 return "<enum %r>" % cls.__name__
377
Ethan Furman2131a4a2013-09-14 18:11:24 -0700378 def __reversed__(cls):
379 return (cls._member_map_[name] for name in reversed(cls._member_names_))
380
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700381 def __setattr__(cls, name, value):
382 """Block attempts to reassign Enum members.
383
384 A simple assignment to the class namespace only changes one of the
385 several possible ways to get an Enum member from the Enum class,
386 resulting in an inconsistent Enumeration.
387
388 """
389 member_map = cls.__dict__.get('_member_map_', {})
390 if name in member_map:
391 raise AttributeError('Cannot reassign members.')
392 super().__setattr__(name, value)
393
anentropicb8e21f12018-04-16 04:40:35 +0100394 def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700395 """Convenience method to create a new Enum class.
396
397 `names` can be:
398
399 * A string containing member names, separated either with spaces or
Ethan Furmand9925a12014-09-16 20:35:55 -0700400 commas. Values are incremented by 1 from `start`.
401 * An iterable of member names. Values are incremented by 1 from `start`.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700402 * An iterable of (member name, value) pairs.
Ethan Furmand9925a12014-09-16 20:35:55 -0700403 * A mapping of member name -> value pairs.
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700404
405 """
406 metacls = cls.__class__
407 bases = (cls, ) if type is None else (type, cls)
Ethan Furman3064dbf2020-09-16 07:11:57 -0700408 _, first_enum = cls._get_mixins_(cls, bases)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700409 classdict = metacls.__prepare__(class_name, bases)
410
411 # special processing needed for names?
412 if isinstance(names, str):
413 names = names.replace(',', ' ').split()
Dong-hee Nadcc8ce42017-06-22 01:52:32 +0900414 if isinstance(names, (tuple, list)) and names and isinstance(names[0], str):
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700415 original_names, names = names, []
Ethan Furmanc16595e2016-09-10 23:36:59 -0700416 last_values = []
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700417 for count, name in enumerate(original_names):
Ethan Furmanc16595e2016-09-10 23:36:59 -0700418 value = first_enum._generate_next_value_(name, start, count, last_values[:])
419 last_values.append(value)
420 names.append((name, value))
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700421
422 # Here, names is either an iterable of (name, value) or a mapping.
423 for item in names:
424 if isinstance(item, str):
425 member_name, member_value = item, names[item]
426 else:
427 member_name, member_value = item
428 classdict[member_name] = member_value
429 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
430
431 # TODO: replace the frame hack if a blessed way to know the calling
432 # module is ever developed
433 if module is None:
434 try:
435 module = sys._getframe(2).f_globals['__name__']
Pablo Galindo293dd232019-11-19 21:34:03 +0000436 except (AttributeError, ValueError, KeyError):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700437 pass
438 if module is None:
439 _make_class_unpicklable(enum_class)
440 else:
441 enum_class.__module__ = module
Ethan Furmanca1b7942014-02-08 11:36:27 -0800442 if qualname is not None:
443 enum_class.__qualname__ = qualname
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700444
445 return enum_class
446
orlnub1230fb9fad2018-09-12 20:28:53 +0300447 def _convert_(cls, name, module, filter, source=None):
448 """
449 Create a new Enum subclass that replaces a collection of global constants
450 """
451 # convert all constants from source (or module) that pass filter() to
452 # a new Enum called name, and export the enum and its members back to
453 # module;
454 # also, replace the __reduce_ex__ method so unpickling works in
455 # previous Python versions
456 module_globals = vars(sys.modules[module])
457 if source:
458 source = vars(source)
459 else:
460 source = module_globals
461 # _value2member_map_ is populated in the same order every time
462 # for a consistent reverse mapping of number to name when there
463 # are multiple names for the same number.
464 members = [
465 (name, value)
466 for name, value in source.items()
467 if filter(name)]
468 try:
469 # sort by value
470 members.sort(key=lambda t: (t[1], t[0]))
471 except TypeError:
472 # unless some values aren't comparable, in which case sort by name
473 members.sort(key=lambda t: t[0])
474 cls = cls(name, members, module=module)
475 cls.__reduce_ex__ = _reduce_ex_by_name
476 module_globals.update(cls.__members__)
477 module_globals[name] = cls
478 return cls
479
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700480 @staticmethod
Ethan Furman3064dbf2020-09-16 07:11:57 -0700481 def _check_for_existing_members(class_name, bases):
482 for chain in bases:
483 for base in chain.__mro__:
484 if issubclass(base, Enum) and base._member_names_:
485 raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__))
486
487 @staticmethod
488 def _get_mixins_(class_name, bases):
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700489 """Returns the type for creating enum members, and the first inherited
490 enum class.
491
492 bases: the tuple of bases that was given to __new__
493
494 """
495 if not bases:
496 return object, Enum
497
Ethan Furman5bdab642018-09-21 19:03:09 -0700498 def _find_data_type(bases):
Ethan Furmanbff01f32020-09-15 15:56:26 -0700499 data_types = []
Ethan Furman5bdab642018-09-21 19:03:09 -0700500 for chain in bases:
Ethan Furmanbff01f32020-09-15 15:56:26 -0700501 candidate = None
Ethan Furman5bdab642018-09-21 19:03:09 -0700502 for base in chain.__mro__:
503 if base is object:
504 continue
Ethan Furmanc2667362020-12-07 00:17:31 -0800505 elif issubclass(base, Enum):
506 if base._member_type_ is not object:
507 data_types.append(base._member_type_)
508 break
Ethan Furman5bdab642018-09-21 19:03:09 -0700509 elif '__new__' in base.__dict__:
Ethan Furmancd453852018-10-05 23:29:36 -0700510 if issubclass(base, Enum):
Ethan Furman5bdab642018-09-21 19:03:09 -0700511 continue
Ethan Furmanbff01f32020-09-15 15:56:26 -0700512 data_types.append(candidate or base)
513 break
Ethan Furmanc2667362020-12-07 00:17:31 -0800514 else:
Ethan Furmanbff01f32020-09-15 15:56:26 -0700515 candidate = base
516 if len(data_types) > 1:
Ethan Furman3064dbf2020-09-16 07:11:57 -0700517 raise TypeError('%r: too many data types: %r' % (class_name, data_types))
Ethan Furmanbff01f32020-09-15 15:56:26 -0700518 elif data_types:
519 return data_types[0]
520 else:
521 return None
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700522
Ethan Furman5bdab642018-09-21 19:03:09 -0700523 # ensure final parent class is an Enum derivative, find any concrete
524 # data type, and check that Enum has no members
525 first_enum = bases[-1]
526 if not issubclass(first_enum, Enum):
527 raise TypeError("new enumerations should be created as "
528 "`EnumName([mixin_type, ...] [data_type,] enum_type)`")
529 member_type = _find_data_type(bases) or object
530 if first_enum._member_names_:
531 raise TypeError("Cannot extend enumerations")
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700532 return member_type, first_enum
533
534 @staticmethod
535 def _find_new_(classdict, member_type, first_enum):
536 """Returns the __new__ to be used for creating the enum members.
537
538 classdict: the class dictionary given to __new__
539 member_type: the data type whose __new__ will be used by default
540 first_enum: enumeration to check for an overriding __new__
541
542 """
543 # now find the correct __new__, checking to see of one was defined
544 # by the user; also check earlier enum classes in case a __new__ was
545 # saved as __new_member__
546 __new__ = classdict.get('__new__', None)
547
548 # should __new__ be saved as __new_member__ later?
549 save_new = __new__ is not None
550
551 if __new__ is None:
552 # check all possibles for __new_member__ before falling back to
553 # __new__
554 for method in ('__new_member__', '__new__'):
555 for possible in (member_type, first_enum):
556 target = getattr(possible, method, None)
557 if target not in {
558 None,
559 None.__new__,
560 object.__new__,
561 Enum.__new__,
562 }:
563 __new__ = target
564 break
565 if __new__ is not None:
566 break
567 else:
568 __new__ = object.__new__
569
570 # if a non-object.__new__ is used then whatever value/tuple was
571 # assigned to the enum member name will be passed to __new__ and to the
572 # new enum member's __init__
573 if __new__ is object.__new__:
574 use_args = False
575 else:
576 use_args = True
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700577 return __new__, save_new, use_args
578
579
580class Enum(metaclass=EnumMeta):
581 """Generic enumeration.
582
583 Derive from this class to define new enumerations.
584
585 """
586 def __new__(cls, value):
587 # all enum instances are actually created during class construction
588 # without calling this method; this method is called by the metaclass'
589 # __call__ (i.e. Color(3) ), and by pickle
590 if type(value) is cls:
Ethan Furman23bb6f42016-11-21 09:22:05 -0800591 # For lookups like Color(Color.RED)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700592 return value
593 # by-value search for a matching enum member
594 # see if it's in the reverse mapping (for hashable values)
Ethan Furman2aa27322013-07-19 19:35:56 -0700595 try:
Andrew Svetlov34ae04f2018-12-26 20:45:33 +0200596 return cls._value2member_map_[value]
597 except KeyError:
598 # Not found, no need to do long O(n) search
599 pass
Ethan Furman2aa27322013-07-19 19:35:56 -0700600 except TypeError:
601 # not there, now do long search -- O(n) behavior
Ethan Furman520ad572013-07-19 19:47:21 -0700602 for member in cls._member_map_.values():
Ethan Furman0081f232014-09-16 17:31:23 -0700603 if member._value_ == value:
Ethan Furman2aa27322013-07-19 19:35:56 -0700604 return member
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700605 # still not found -- try _missing_ hook
Ethan Furman019f0a02018-09-12 11:43:34 -0700606 try:
607 exc = None
608 result = cls._missing_(value)
609 except Exception as e:
610 exc = e
611 result = None
612 if isinstance(result, cls):
613 return result
614 else:
Walter Dörwald323842c2019-07-18 20:37:13 +0200615 ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
Ethan Furman019f0a02018-09-12 11:43:34 -0700616 if result is None and exc is None:
617 raise ve_exc
618 elif exc is None:
619 exc = TypeError(
620 'error in %s._missing_: returned %r instead of None or a valid member'
621 % (cls.__name__, result)
622 )
623 exc.__context__ = ve_exc
624 raise exc
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700625
Ethan Furmanc16595e2016-09-10 23:36:59 -0700626 def _generate_next_value_(name, start, count, last_values):
627 for last_value in reversed(last_values):
628 try:
629 return last_value + 1
630 except TypeError:
631 pass
632 else:
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700633 return start
Ethan Furmanc16595e2016-09-10 23:36:59 -0700634
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700635 @classmethod
636 def _missing_(cls, value):
Ethan Furmanc95ad7a2020-09-16 10:26:50 -0700637 return None
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700638
639 def __repr__(self):
640 return "<%s.%s: %r>" % (
Ethan Furman520ad572013-07-19 19:47:21 -0700641 self.__class__.__name__, self._name_, self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700642
643 def __str__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700644 return "%s.%s" % (self.__class__.__name__, self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700645
Ethan Furman388a3922013-08-12 06:51:41 -0700646 def __dir__(self):
Ethan Furman0ae550b2014-10-14 08:58:32 -0700647 added_behavior = [
648 m
649 for cls in self.__class__.mro()
650 for m in cls.__dict__
Ethan Furman354ecf12015-03-11 08:43:12 -0700651 if m[0] != '_' and m not in self._member_map_
Angelin BOOZ68526fe2020-09-21 15:11:06 +0200652 ] + [m for m in self.__dict__ if m[0] != '_']
Ethan Furmanec5f8eb2014-10-21 13:40:35 -0700653 return (['__class__', '__doc__', '__module__'] + added_behavior)
Ethan Furman388a3922013-08-12 06:51:41 -0700654
Ethan Furmanec15a822013-08-31 19:17:41 -0700655 def __format__(self, format_spec):
656 # mixed-in Enums should use the mixed-in type's __format__, otherwise
657 # we can get strange results with the Enum name showing up instead of
658 # the value
659
thatneat2f19e822019-07-04 11:28:37 -0700660 # pure Enum branch, or branch with __str__ explicitly overridden
661 str_overridden = type(self).__str__ != Enum.__str__
662 if self._member_type_ is object or str_overridden:
Ethan Furmanec15a822013-08-31 19:17:41 -0700663 cls = str
664 val = str(self)
665 # mix-in branch
666 else:
667 cls = self._member_type_
Ethan Furman0081f232014-09-16 17:31:23 -0700668 val = self._value_
Ethan Furmanec15a822013-08-31 19:17:41 -0700669 return cls.__format__(val, format_spec)
670
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700671 def __hash__(self):
Ethan Furman520ad572013-07-19 19:47:21 -0700672 return hash(self._name_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700673
Ethan Furmanca1b7942014-02-08 11:36:27 -0800674 def __reduce_ex__(self, proto):
Ethan Furmandc870522014-02-18 12:37:12 -0800675 return self.__class__, (self._value_, )
Ethan Furmanca1b7942014-02-08 11:36:27 -0800676
Ethan Furman33918c12013-09-27 23:02:02 -0700677 # DynamicClassAttribute is used to provide access to the `name` and
678 # `value` properties of enum members while keeping some measure of
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700679 # protection from modification, while still allowing for an enumeration
680 # to have members named `name` and `value`. This works because enumeration
681 # members are not set directly on the enum class -- __getattr__ is
682 # used to look them up.
683
Ethan Furmane03ea372013-09-25 07:14:41 -0700684 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700685 def name(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700686 """The name of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700687 return self._name_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700688
Ethan Furmane03ea372013-09-25 07:14:41 -0700689 @DynamicClassAttribute
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700690 def value(self):
Ethan Furmanc850f342013-09-15 16:59:35 -0700691 """The value of the Enum member."""
Ethan Furman520ad572013-07-19 19:47:21 -0700692 return self._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700693
694
695class IntEnum(int, Enum):
Ethan Furman0063ff42020-09-21 17:23:13 -0700696 """
697 Enum where members are also (and must be) ints
698 """
699
700
701class StrEnum(str, Enum):
702 """
703 Enum where members are also (and must be) strings
704 """
705
706 def __new__(cls, *values):
707 if len(values) > 3:
708 raise TypeError('too many arguments for str(): %r' % (values, ))
709 if len(values) == 1:
710 # it must be a string
711 if not isinstance(values[0], str):
712 raise TypeError('%r is not a string' % (values[0], ))
713 if len(values) > 1:
714 # check that encoding argument is a string
715 if not isinstance(values[1], str):
716 raise TypeError('encoding must be a string, not %r' % (values[1], ))
717 if len(values) > 2:
718 # check that errors argument is a string
719 if not isinstance(values[2], str):
720 raise TypeError('errors must be a string, not %r' % (values[2], ))
721 value = str(*values)
722 member = str.__new__(cls, value)
723 member._value_ = value
724 return member
Ethan Furmanf24bb352013-07-18 17:05:39 -0700725
Ethan Furmand986d162020-09-22 13:00:07 -0700726 __str__ = str.__str__
727
Ethan Furmanf24bb352013-07-18 17:05:39 -0700728
Ethan Furman24e837f2015-03-18 17:27:57 -0700729def _reduce_ex_by_name(self, proto):
730 return self.name
731
Ethan Furman65a5a472016-09-01 23:55:19 -0700732class Flag(Enum):
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700733 """Support for flags"""
Ethan Furmanc16595e2016-09-10 23:36:59 -0700734
735 def _generate_next_value_(name, start, count, last_values):
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700736 """
737 Generate the next value when not given.
738
739 name: the name of the member
HongWeipengbb16fb22019-09-21 13:22:54 +0800740 start: the initial start value or None
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700741 count: the number of existing members
742 last_value: the last value assigned or None
743 """
744 if not count:
745 return start if start is not None else 1
Ethan Furmanc16595e2016-09-10 23:36:59 -0700746 for last_value in reversed(last_values):
747 try:
748 high_bit = _high_bit(last_value)
749 break
Ethan Furman3515dcc2016-09-18 13:15:41 -0700750 except Exception:
Ethan Furmanc16595e2016-09-10 23:36:59 -0700751 raise TypeError('Invalid Flag value: %r' % last_value) from None
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700752 return 2 ** (high_bit+1)
753
754 @classmethod
755 def _missing_(cls, value):
756 original_value = value
757 if value < 0:
758 value = ~value
759 possible_member = cls._create_pseudo_member_(value)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700760 if original_value < 0:
761 possible_member = ~possible_member
762 return possible_member
763
764 @classmethod
765 def _create_pseudo_member_(cls, value):
Ethan Furman3515dcc2016-09-18 13:15:41 -0700766 """
767 Create a composite member iff value contains only members.
768 """
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700769 pseudo_member = cls._value2member_map_.get(value, None)
770 if pseudo_member is None:
Ethan Furman3515dcc2016-09-18 13:15:41 -0700771 # verify all bits are accounted for
772 _, extra_flags = _decompose(cls, value)
773 if extra_flags:
Walter Dörwald323842c2019-07-18 20:37:13 +0200774 raise ValueError("%r is not a valid %s" % (value, cls.__qualname__))
Ethan Furman3515dcc2016-09-18 13:15:41 -0700775 # construct a singleton enum pseudo-member
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700776 pseudo_member = object.__new__(cls)
777 pseudo_member._name_ = None
778 pseudo_member._value_ = value
Ethan Furman28cf6632017-01-24 12:12:06 -0800779 # use setdefault in case another thread already created a composite
780 # with this value
781 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700782 return pseudo_member
783
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700784 def __contains__(self, other):
785 if not isinstance(other, self.__class__):
Rahul Jha94306522018-09-10 23:51:04 +0530786 raise TypeError(
787 "unsupported operand type(s) for 'in': '%s' and '%s'" % (
788 type(other).__qualname__, self.__class__.__qualname__))
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700789 return other._value_ & self._value_ == other._value_
790
Ethan Furman7219e272020-09-16 13:01:00 -0700791 def __iter__(self):
792 members, extra_flags = _decompose(self.__class__, self.value)
793 return (m for m in members if m._value_ != 0)
794
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700795 def __repr__(self):
796 cls = self.__class__
797 if self._name_ is not None:
798 return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_)
Ethan Furman3515dcc2016-09-18 13:15:41 -0700799 members, uncovered = _decompose(cls, self._value_)
Ethan Furman27682d22016-09-04 11:39:01 -0700800 return '<%s.%s: %r>' % (
801 cls.__name__,
802 '|'.join([str(m._name_ or m._value_) for m in members]),
803 self._value_,
804 )
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700805
806 def __str__(self):
807 cls = self.__class__
808 if self._name_ is not None:
809 return '%s.%s' % (cls.__name__, self._name_)
Ethan Furman3515dcc2016-09-18 13:15:41 -0700810 members, uncovered = _decompose(cls, self._value_)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700811 if len(members) == 1 and members[0]._name_ is None:
812 return '%s.%r' % (cls.__name__, members[0]._value_)
813 else:
814 return '%s.%s' % (
815 cls.__name__,
816 '|'.join([str(m._name_ or m._value_) for m in members]),
817 )
818
Ethan Furman25d94bb2016-09-02 16:32:32 -0700819 def __bool__(self):
820 return bool(self._value_)
821
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700822 def __or__(self, other):
823 if not isinstance(other, self.__class__):
824 return NotImplemented
825 return self.__class__(self._value_ | other._value_)
826
827 def __and__(self, other):
828 if not isinstance(other, self.__class__):
829 return NotImplemented
830 return self.__class__(self._value_ & other._value_)
831
832 def __xor__(self, other):
833 if not isinstance(other, self.__class__):
834 return NotImplemented
835 return self.__class__(self._value_ ^ other._value_)
836
837 def __invert__(self):
Ethan Furman3515dcc2016-09-18 13:15:41 -0700838 members, uncovered = _decompose(self.__class__, self._value_)
Serhiy Storchaka81108372017-09-26 00:55:55 +0300839 inverted = self.__class__(0)
840 for m in self.__class__:
841 if m not in members and not (m._value_ & self._value_):
842 inverted = inverted | m
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700843 return self.__class__(inverted)
844
845
Ethan Furman65a5a472016-09-01 23:55:19 -0700846class IntFlag(int, Flag):
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700847 """Support for integer-based Flags"""
848
849 @classmethod
Ethan Furman3515dcc2016-09-18 13:15:41 -0700850 def _missing_(cls, value):
851 if not isinstance(value, int):
Walter Dörwald323842c2019-07-18 20:37:13 +0200852 raise ValueError("%r is not a valid %s" % (value, cls.__qualname__))
Ethan Furman3515dcc2016-09-18 13:15:41 -0700853 new_member = cls._create_pseudo_member_(value)
854 return new_member
855
856 @classmethod
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700857 def _create_pseudo_member_(cls, value):
858 pseudo_member = cls._value2member_map_.get(value, None)
859 if pseudo_member is None:
Ethan Furman3515dcc2016-09-18 13:15:41 -0700860 need_to_create = [value]
861 # get unaccounted for bits
862 _, extra_flags = _decompose(cls, value)
863 # timer = 10
864 while extra_flags:
865 # timer -= 1
866 bit = _high_bit(extra_flags)
867 flag_value = 2 ** bit
868 if (flag_value not in cls._value2member_map_ and
869 flag_value not in need_to_create
870 ):
871 need_to_create.append(flag_value)
872 if extra_flags == -flag_value:
873 extra_flags = 0
874 else:
875 extra_flags ^= flag_value
876 for value in reversed(need_to_create):
877 # construct singleton pseudo-members
878 pseudo_member = int.__new__(cls, value)
879 pseudo_member._name_ = None
880 pseudo_member._value_ = value
Ethan Furman28cf6632017-01-24 12:12:06 -0800881 # use setdefault in case another thread already created a composite
882 # with this value
883 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700884 return pseudo_member
885
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700886 def __or__(self, other):
887 if not isinstance(other, (self.__class__, int)):
888 return NotImplemented
Ethan Furman3515dcc2016-09-18 13:15:41 -0700889 result = self.__class__(self._value_ | self.__class__(other)._value_)
890 return result
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700891
892 def __and__(self, other):
893 if not isinstance(other, (self.__class__, int)):
894 return NotImplemented
895 return self.__class__(self._value_ & self.__class__(other)._value_)
896
897 def __xor__(self, other):
898 if not isinstance(other, (self.__class__, int)):
899 return NotImplemented
900 return self.__class__(self._value_ ^ self.__class__(other)._value_)
901
902 __ror__ = __or__
903 __rand__ = __and__
904 __rxor__ = __xor__
905
906 def __invert__(self):
Ethan Furman3515dcc2016-09-18 13:15:41 -0700907 result = self.__class__(~self._value_)
908 return result
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700909
910
911def _high_bit(value):
Ethan Furman04439532016-09-02 15:50:21 -0700912 """returns index of highest bit, or -1 if value is zero or negative"""
Ethan Furman3515dcc2016-09-18 13:15:41 -0700913 return value.bit_length() - 1
Ethan Furmanee47e5c2016-08-31 00:12:15 -0700914
Ethan Furmanf24bb352013-07-18 17:05:39 -0700915def unique(enumeration):
916 """Class decorator for enumerations ensuring unique member values."""
917 duplicates = []
918 for name, member in enumeration.__members__.items():
919 if name != member.name:
920 duplicates.append((name, member.name))
921 if duplicates:
922 alias_details = ', '.join(
923 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
924 raise ValueError('duplicate values found in %r: %s' %
925 (enumeration, alias_details))
926 return enumeration
Ethan Furman3515dcc2016-09-18 13:15:41 -0700927
928def _decompose(flag, value):
929 """Extract all members from the value."""
930 # _decompose is only called if the value is not named
931 not_covered = value
932 negative = value < 0
Ethan Furman3515dcc2016-09-18 13:15:41 -0700933 members = []
HongWeipeng0b41a922019-11-27 06:36:02 +0800934 for member in flag:
935 member_value = member.value
Ethan Furman3515dcc2016-09-18 13:15:41 -0700936 if member_value and member_value & value == member_value:
937 members.append(member)
938 not_covered &= ~member_value
HongWeipeng0b41a922019-11-27 06:36:02 +0800939 if not negative:
940 tmp = not_covered
941 while tmp:
942 flag_value = 2 ** _high_bit(tmp)
943 if flag_value in flag._value2member_map_:
944 members.append(flag._value2member_map_[flag_value])
945 not_covered &= ~flag_value
946 tmp &= ~flag_value
Ethan Furman3515dcc2016-09-18 13:15:41 -0700947 if not members and value in flag._value2member_map_:
948 members.append(flag._value2member_map_[value])
949 members.sort(key=lambda m: m._value_, reverse=True)
950 if len(members) > 1 and members[0].value == value:
951 # we have the breakdown, don't need the value member itself
952 members.pop(0)
953 return members, not_covered