89047cd5b60fafe194297254bd02cc8606381b96
[software/python-on-guile.git] / modules / language / python / module / enum.py
1 module(enum) ## needed in guile atm
2
3 import sys
4 from types import MappingProxyType, DynamicClassAttribute
5 from functools import reduce
6 from operator import or_ as _or_
7
8 # try _collections first to reduce startup cost
9 try:
10 from _collections import OrderedDict
11 except ImportError:
12 from collections import OrderedDict
13
14
15 __all__ = [
16 'EnumMeta',
17 'Enum', 'IntEnum', 'Flag', 'IntFlag',
18 'auto', 'unique',
19 ]
20
21
22 def _is_descriptor(obj):
23 """Returns True if obj is a descriptor, False otherwise."""
24 return (
25 hasattr(obj, '__get__') or
26 hasattr(obj, '__set__') or
27 hasattr(obj, '__delete__'))
28
29
30 def _is_dunder(name):
31 """Returns True if a __dunder__ name, False otherwise."""
32 return (name[:2] == name[-2:] == '__' and
33 name[2:3] != '_' and
34 name[-3:-2] != '_' and
35 len(name) > 4)
36
37
38 def _is_sunder(name):
39 """Returns True if a _sunder_ name, False otherwise."""
40 return (name[0] == name[-1] == '_' and
41 name[1:2] != '_' and
42 name[-2:-1] != '_' and
43 len(name) > 2)
44
45 def _make_class_unpicklable(cls):
46 """Make the given class un-picklable."""
47 def _break_on_call_reduce(self, proto):
48 raise TypeError('%r cannot be pickled' % self)
49 cls.__reduce_ex__ = _break_on_call_reduce
50 cls.__module__ = '<unknown>'
51
52 _auto_null = object()
53 class auto:
54 """
55 Instances are replaced with an appropriate value in Enum class suites.
56 """
57 value = _auto_null
58
59
60 class _EnumDict(dict):
61 """Track enum member order and ensure member names are not reused.
62
63 EnumMeta will use the names found in self._member_names as the
64 enumeration member names.
65
66 """
67 def __init__(self):
68 super().__init__()
69 self._member_names = []
70 self._last_values = []
71
72 def __setitem__(self, key, value):
73 """Changes anything not dundered or not a descriptor.
74
75 If an enum member name is used twice, an error is raised; duplicate
76 values are not checked for.
77
78 Single underscore (sunder) names are reserved.
79
80 """
81 if _is_sunder(key):
82 if key not in (
83 '_order_', '_create_pseudo_member_',
84 '_generate_next_value_', '_missing_',
85 ):
86 raise ValueError('_names_ are reserved for future Enum use')
87 if key == '_generate_next_value_':
88 setattr(self, '_generate_next_value', value)
89 elif _is_dunder(key):
90 if key == '__order__':
91 key = '_order_'
92 elif key in self._member_names:
93 # descriptor overwriting an enum?
94 raise TypeError('Attempted to reuse key: %r' % key)
95 elif not _is_descriptor(value):
96 if key in self:
97 # enum overwriting a descriptor?
98 raise TypeError('%r already defined as: %r' % (key, self[key]))
99 if isinstance(value, auto):
100 if value.value == _auto_null:
101 value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
102 value = value.value
103 self._member_names.append(key)
104 self._last_values.append(value)
105 super().__setitem__(key, value)
106
107
108 # Dummy value for Enum as EnumMeta explicitly checks for it, but of course
109 # until EnumMeta finishes running the first time the Enum class doesn't exist.
110 # This is also why there are checks in EnumMeta like `if Enum is not None`
111 Enum = None
112
113
114 class EnumMeta(type):
115 """Metaclass for Enum"""
116 @classmethod
117 def __prepare__(metacls, cls, bases):
118 # create the namespace dict
119 enum_dict = _EnumDict()
120 # inherit previous flags and _generate_next_value_ function
121 member_type, first_enum = metacls._get_mixins_(bases)
122 if first_enum is not None:
123 enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
124 return enum_dict
125
126 def __new__(metacls, cls, bases, classdict):
127 # an Enum class is final once enumeration items have been defined; it
128 # cannot be mixed with other types (int, float, etc.) if it has an
129 # inherited __new__ unless a new __new__ is defined (or the resulting
130 # class will fail).
131 member_type, first_enum = metacls._get_mixins_(bases)
132 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
133 first_enum)
134
135 # save enum items into separate mapping so they don't get baked into
136 # the new class
137 enum_members = {k: classdict[k] for k in classdict._member_names}
138 for name in classdict._member_names:
139 del classdict[name]
140
141 # adjust the sunders
142 _order_ = classdict.pop('_order_', None)
143
144 # check for illegal enum names (any others?)
145 invalid_names = set(enum_members) & {'mro', }
146 if invalid_names:
147 raise ValueError('Invalid enum member name: {0}'.format(
148 ','.join(invalid_names)))
149
150 # create a default docstring if one has not been provided
151 if '__doc__' not in classdict:
152 classdict['__doc__'] = 'An enumeration.'
153
154 # create our new Enum type
155 enum_class = super().__new__(metacls, cls, bases, classdict)
156 enum_class._member_names_ = [] # names in definition order
157 enum_class._member_map_ = OrderedDict() # name->value map
158 enum_class._member_type_ = member_type
159
160 # save attributes from super classes so we know if we can take
161 # the shortcut of storing members in the class dict
162 base_attributes = {a for b in enum_class.mro() for a in b.__dict__}
163
164 # Reverse value->name map for hashable values.
165 enum_class._value2member_map_ = {}
166
167 # If a custom type is mixed into the Enum, and it does not know how
168 # to pickle itself, pickle.dumps will succeed but pickle.loads will
169 # fail. Rather than have the error show up later and possibly far
170 # from the source, sabotage the pickle protocol for this class so
171 # that pickle.dumps also fails.
172 #
173 # However, if the new class implements its own __reduce_ex__, do not
174 # sabotage -- it's on them to make sure it works correctly. We use
175 # __reduce_ex__ instead of any of the others as it is preferred by
176 # pickle over __reduce__, and it handles all pickle protocols.
177 if '__reduce_ex__' not in classdict:
178 if member_type is not object:
179 methods = ('__getnewargs_ex__', '__getnewargs__',
180 '__reduce_ex__', '__reduce__')
181 if not any(m in member_type.__dict__ for m in methods):
182 _make_class_unpicklable(enum_class)
183
184 # instantiate them, checking for duplicates as we go
185 # we instantiate first instead of checking for duplicates first in case
186 # a custom __new__ is doing something funky with the values -- such as
187 # auto-numbering ;)
188 for member_name in classdict._member_names:
189 value = enum_members[member_name]
190 if not isinstance(value, tuple):
191 args = (value, )
192 else:
193 args = value
194 if member_type is tuple: # special case for tuple enums
195 args = (args, ) # wrap it one more time
196 if not use_args:
197 enum_member = __new__(enum_class)
198 if not hasattr(enum_member, '_value_'):
199 enum_member._value_ = value
200 else:
201 enum_member = __new__(enum_class, *args)
202 if not hasattr(enum_member, '_value_'):
203 if member_type is object:
204 enum_member._value_ = value
205 else:
206 enum_member._value_ = member_type(*args)
207 value = enum_member._value_
208 enum_member._name_ = member_name
209 enum_member.__objclass__ = enum_class
210 enum_member.__init__(*args)
211 # If another member with the same value was already defined, the
212 # new member becomes an alias to the existing one.
213 for name, canonical_member in enum_class._member_map_.items():
214 if canonical_member._value_ == enum_member._value_:
215 enum_member = canonical_member
216 break
217 else:
218 # Aliases don't appear in member names (only in __members__).
219 enum_class._member_names_.append(member_name)
220 # performance boost for any member that would not shadow
221 # a DynamicClassAttribute
222 if member_name not in base_attributes:
223 setattr(enum_class, member_name, enum_member)
224 # now add to _member_map_
225 enum_class._member_map_[member_name] = enum_member
226 try:
227 # This may fail if value is not hashable. We can't add the value
228 # to the map, and by-value lookups for this value will be
229 # linear.
230 enum_class._value2member_map_[value] = enum_member
231 except TypeError:
232 pass
233
234 # double check that repr and friends are not the mixin's or various
235 # things break (such as pickle)
236 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
237 class_method = getattr(enum_class, name)
238 obj_method = getattr(member_type, name, None)
239 enum_method = getattr(first_enum, name, None)
240 if obj_method is not None and obj_method is class_method:
241 setattr(enum_class, name, enum_method)
242
243 # replace any other __new__ with our own (as long as Enum is not None,
244 # anyway) -- again, this is to support pickle
245 if Enum is not None:
246 # if the user defined their own __new__, save it before it gets
247 # clobbered in case they subclass later
248 if save_new:
249 enum_class.__new_member__ = __new__
250 enum_class.__new__ = Enum.__new__
251
252 # py3 support for definition order (helps keep py2/py3 code in sync)
253 if _order_ is not None:
254 if isinstance(_order_, str):
255 _order_ = _order_.replace(',', ' ').split()
256 if _order_ != enum_class._member_names_:
257 raise TypeError('member order does not match _order_')
258
259 return enum_class
260
261 def __bool__(self):
262 """
263 classes/types should always be True.
264 """
265 return True
266
267 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
268 """Either returns an existing member, or creates a new enum class.
269
270 This method is used both when an enum class is given a value to match
271 to an enumeration member (i.e. Color(3)) and for the functional API
272 (i.e. Color = Enum('Color', names='RED GREEN BLUE')).
273
274 When used for the functional API:
275
276 `value` will be the name of the new class.
277
278 `names` should be either a string of white-space/comma delimited names
279 (values will start at `start`), or an iterator/mapping of name, value pairs.
280
281 `module` should be set to the module this class is being created in;
282 if it is not set, an attempt to find that module will be made, but if
283 it fails the class will not be picklable.
284
285 `qualname` should be set to the actual location this class can be found
286 at in its module; by default it is set to the global scope. If this is
287 not correct, unpickling will fail in some circumstances.
288
289 `type`, if set, will be mixed in as the first base class.
290
291 """
292 if names is None: # simple value lookup
293 return cls.__new__(cls, value)
294 # otherwise, functional API: we're creating a new Enum type
295 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
296
297 def __contains__(cls, member):
298 return isinstance(member, cls) and member._name_ in cls._member_map_
299
300 def __delattr__(cls, attr):
301 # nicer error message when someone tries to delete an attribute
302 # (see issue19025).
303 if attr in cls._member_map_:
304 raise AttributeError(
305 "%s: cannot delete Enum member." % cls.__name__)
306 super().__delattr__(attr)
307
308 def __dir__(self):
309 return (['__class__', '__doc__', '__members__', '__module__'] +
310 self._member_names_)
311
312 def __getattr__(cls, name):
313 """Return the enum member matching `name`
314
315 We use __getattr__ instead of descriptors or inserting into the enum
316 class' __dict__ in order to support `name` and `value` being both
317 properties for enum members (which live in the class' __dict__) and
318 enum members themselves.
319
320 """
321 if _is_dunder(name):
322 raise AttributeError(name)
323 try:
324 return cls._member_map_[name]
325 except KeyError:
326 raise AttributeError(name) from None
327
328 def __getitem__(cls, name):
329 return cls._member_map_[name]
330
331 def __iter__(cls):
332 return (cls._member_map_[name] for name in cls._member_names_)
333
334 def __len__(cls):
335 return len(cls._member_names_)
336
337 @property
338 def __members__(cls):
339 """Returns a mapping of member name->value.
340
341 This mapping lists all enum members, including aliases. Note that this
342 is a read-only view of the internal mapping.
343
344 """
345 return MappingProxyType(cls._member_map_)
346
347 def __repr__(cls):
348 return "<enum %r>" % cls.__name__
349
350 def __reversed__(cls):
351 return (cls._member_map_[name] for name in reversed(cls._member_names_))
352
353 def __setattr__(cls, name, value):
354 """Block attempts to reassign Enum members.
355
356 A simple assignment to the class namespace only changes one of the
357 several possible ways to get an Enum member from the Enum class,
358 resulting in an inconsistent Enumeration.
359
360 """
361 member_map = cls.__dict__.get('_member_map_', {})
362 if name in member_map:
363 raise AttributeError('Cannot reassign members.')
364 super().__setattr__(name, value)
365
366 def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None, start=1):
367 """Convenience method to create a new Enum class.
368
369 `names` can be:
370
371 * A string containing member names, separated either with spaces or
372 commas. Values are incremented by 1 from `start`.
373 * An iterable of member names. Values are incremented by 1 from `start`.
374 * An iterable of (member name, value) pairs.
375 * A mapping of member name -> value pairs.
376
377 """
378 metacls = cls.__class__
379 bases = (cls, ) if type is None else (type, cls)
380 _, first_enum = cls._get_mixins_(bases)
381 classdict = metacls.__prepare__(class_name, bases)
382
383 # special processing needed for names?
384 if isinstance(names, str):
385 names = names.replace(',', ' ').split()
386 if isinstance(names, (tuple, list)) and names and isinstance(names[0], str):
387 original_names, names = names, []
388 last_values = []
389 for count, name in enumerate(original_names):
390 value = first_enum._generate_next_value_(name, start, count, last_values[:])
391 last_values.append(value)
392 names.append((name, value))
393
394 # Here, names is either an iterable of (name, value) or a mapping.
395 for item in names:
396 if isinstance(item, str):
397 member_name, member_value = item, names[item]
398 else:
399 member_name, member_value = item
400 classdict[member_name] = member_value
401 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
402
403 # TODO: replace the frame hack if a blessed way to know the calling
404 # module is ever developed
405 if module is None:
406 try:
407 module = sys._getframe(2).f_globals['__name__']
408 except (AttributeError, ValueError) as exc:
409 pass
410 if module is None:
411 _make_class_unpicklable(enum_class)
412 else:
413 enum_class.__module__ = module
414 if qualname is not None:
415 enum_class.__qualname__ = qualname
416
417 return enum_class
418
419 @staticmethod
420 def _get_mixins_(bases):
421 """Returns the type for creating enum members, and the first inherited
422 enum class.
423
424 bases: the tuple of bases that was given to __new__
425
426 """
427 if not bases:
428 return object, Enum
429
430 # double check that we are not subclassing a class with existing
431 # enumeration members; while we're at it, see if any other data
432 # type has been mixed in so we can use the correct __new__
433 member_type = first_enum = None
434 for base in bases:
435 if (base is not Enum and
436 issubclass(base, Enum) and
437 base._member_names_):
438 raise TypeError("Cannot extend enumerations")
439 # base is now the last base in bases
440 if not issubclass(base, Enum):
441 raise TypeError("new enumerations must be created as "
442 "`ClassName([mixin_type,] enum_type)`")
443
444 # get correct mix-in type (either mix-in type of Enum subclass, or
445 # first base if last base is Enum)
446 if not issubclass(bases[0], Enum):
447 member_type = bases[0] # first data type
448 first_enum = bases[-1] # enum type
449 else:
450 for base in bases[0].__mro__:
451 # most common: (IntEnum, int, Enum, object)
452 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
453 # <class 'int'>, <Enum 'Enum'>,
454 # <class 'object'>)
455 if issubclass(base, Enum):
456 if first_enum is None:
457 first_enum = base
458 else:
459 if member_type is None:
460 member_type = base
461
462 return member_type, first_enum
463
464 @staticmethod
465 def _find_new_(classdict, member_type, first_enum):
466 """Returns the __new__ to be used for creating the enum members.
467
468 classdict: the class dictionary given to __new__
469 member_type: the data type whose __new__ will be used by default
470 first_enum: enumeration to check for an overriding __new__
471
472 """
473 # now find the correct __new__, checking to see of one was defined
474 # by the user; also check earlier enum classes in case a __new__ was
475 # saved as __new_member__
476 __new__ = classdict.get('__new__', None)
477
478 # should __new__ be saved as __new_member__ later?
479 save_new = __new__ is not None
480
481 if __new__ is None:
482 # check all possibles for __new_member__ before falling back to
483 # __new__
484 for method in ('__new_member__', '__new__'):
485 for possible in (member_type, first_enum):
486 target = getattr(possible, method, None)
487 if target not in {
488 None,
489 None.__new__,
490 object.__new__,
491 Enum.__new__,
492 }:
493 __new__ = target
494 break
495 if __new__ is not None:
496 break
497 else:
498 __new__ = object.__new__
499
500 # if a non-object.__new__ is used then whatever value/tuple was
501 # assigned to the enum member name will be passed to __new__ and to the
502 # new enum member's __init__
503 if __new__ is object.__new__:
504 use_args = False
505 else:
506 use_args = True
507
508 return __new__, save_new, use_args
509
510 class Enum(metaclass=EnumMeta):
511 """Generic enumeration.
512
513 Derive from this class to define new enumerations.
514
515 """
516 def __new__(cls, value):
517 # all enum instances are actually created during class construction
518 # without calling this method; this method is called by the metaclass'
519 # __call__ (i.e. Color(3) ), and by pickle
520 if type(value) is cls:
521 # For lookups like Color(Color.RED)
522 return value
523 # by-value search for a matching enum member
524 # see if it's in the reverse mapping (for hashable values)
525 try:
526 if value in cls._value2member_map_:
527 return cls._value2member_map_[value]
528 except TypeError:
529 # not there, now do long search -- O(n) behavior
530 for member in cls._member_map_.values():
531 if member._value_ == value:
532 return member
533 # still not found -- try _missing_ hook
534 return cls._missing_(value)
535
536 def _generate_next_value_(name, start, count, last_values):
537 for last_value in reversed(last_values):
538 try:
539 return last_value + 1
540 except TypeError:
541 pass
542 else:
543 return start
544
545 @classmethod
546 def _missing_(cls, value):
547 raise ValueError("%r is not a valid %s" % (value, cls.__name__))
548
549 def __repr__(self):
550 return "<%s.%s: %r>" % (
551 self.__class__.__name__, self._name_, self._value_)
552
553 def __str__(self):
554 return "%s.%s" % (self.__class__.__name__, self._name_)
555
556 def __dir__(self):
557 added_behavior = [
558 m
559 for cls in self.__class__.mro()
560 for m in cls.__dict__
561 if m[0] != '_' and m not in self._member_map_
562 ]
563 return (['__class__', '__doc__', '__module__'] + added_behavior)
564
565 def __format__(self, format_spec):
566 # mixed-in Enums should use the mixed-in type's __format__, otherwise
567 # we can get strange results with the Enum name showing up instead of
568 # the value
569
570 # pure Enum branch
571 if self._member_type_ is object:
572 cls = str
573 val = str(self)
574 # mix-in branch
575 else:
576 cls = self._member_type_
577 val = self._value_
578 return cls.__format__(val, format_spec)
579
580 def __hash__(self):
581 return hash(self._name_)
582
583 def __reduce_ex__(self, proto):
584 return self.__class__, (self._value_, )
585
586 # DynamicClassAttribute is used to provide access to the `name` and
587 # `value` properties of enum members while keeping some measure of
588 # protection from modification, while still allowing for an enumeration
589 # to have members named `name` and `value`. This works because enumeration
590 # members are not set directly on the enum class -- __getattr__ is
591 # used to look them up.
592
593 @DynamicClassAttribute
594 def name(self):
595 """The name of the Enum member."""
596 return self._name_
597
598 @DynamicClassAttribute
599 def value(self):
600 """The value of the Enum member."""
601 return self._value_
602
603 @classmethod
604 def _convert(cls, name, module, filter, source=None):
605 """
606 Create a new Enum subclass that replaces a collection of global constants
607 """
608 # convert all constants from source (or module) that pass filter() to
609 # a new Enum called name, and export the enum and its members back to
610 # module;
611 # also, replace the __reduce_ex__ method so unpickling works in
612 # previous Python versions
613 module_globals = vars(sys.modules[module])
614 if source:
615 source = vars(source)
616 else:
617 source = module_globals
618 # We use an OrderedDict of sorted source keys so that the
619 # _value2member_map is populated in the same order every time
620 # for a consistent reverse mapping of number to name when there
621 # are multiple names for the same number rather than varying
622 # between runs due to hash randomization of the module dictionary.
623 members = [
624 (name, source[name])
625 for name in source.keys()
626 if filter(name)]
627 try:
628 # sort by value
629 members.sort(key=lambda t: (t[1], t[0]))
630 except TypeError:
631 # unless some values aren't comparable, in which case sort by name
632 members.sort(key=lambda t: t[0])
633 cls = cls(name, members, module=module)
634 cls.__reduce_ex__ = _reduce_ex_by_name
635 module_globals.update(cls.__members__)
636 module_globals[name] = cls
637 return cls
638
639
640 class IntEnum(int, Enum):
641 """Enum where members are also (and must be) ints"""
642
643
644 def _reduce_ex_by_name(self, proto):
645 return self.name
646
647 class Flag(Enum):
648 """Support for flags"""
649
650 def _generate_next_value_(name, start, count, last_values):
651 """
652 Generate the next value when not given.
653
654 name: the name of the member
655 start: the initital start value or None
656 count: the number of existing members
657 last_value: the last value assigned or None
658 """
659 if not count:
660 return start if start is not None else 1
661 for last_value in reversed(last_values):
662 try:
663 high_bit = _high_bit(last_value)
664 break
665 except Exception:
666 raise TypeError('Invalid Flag value: %r' % last_value) from None
667 return 2 ** (high_bit+1)
668
669 @classmethod
670 def _missing_(cls, value):
671 original_value = value
672 if value < 0:
673 value = ~value
674 possible_member = cls._create_pseudo_member_(value)
675 if original_value < 0:
676 possible_member = ~possible_member
677 return possible_member
678
679 @classmethod
680 def _create_pseudo_member_(cls, value):
681 """
682 Create a composite member iff value contains only members.
683 """
684 pseudo_member = cls._value2member_map_.get(value, None)
685 if pseudo_member is None:
686 # verify all bits are accounted for
687 _, extra_flags = _decompose(cls, value)
688 if extra_flags:
689 raise ValueError("%r is not a valid %s" % (value, cls.__name__))
690 # construct a singleton enum pseudo-member
691 pseudo_member = object.__new__(cls)
692 pseudo_member._name_ = None
693 pseudo_member._value_ = value
694 # use setdefault in case another thread already created a composite
695 # with this value
696 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
697 return pseudo_member
698
699 def __contains__(self, other):
700 if not isinstance(other, self.__class__):
701 return NotImplemented
702 return other._value_ & self._value_ == other._value_
703
704 def __repr__(self):
705 cls = self.__class__
706 if self._name_ is not None:
707 return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_)
708 members, uncovered = _decompose(cls, self._value_)
709 return '<%s.%s: %r>' % (
710 cls.__name__,
711 '|'.join([str(m._name_ or m._value_) for m in members]),
712 self._value_,
713 )
714
715 def __str__(self):
716 cls = self.__class__
717 if self._name_ is not None:
718 return '%s.%s' % (cls.__name__, self._name_)
719 members, uncovered = _decompose(cls, self._value_)
720 if len(members) == 1 and members[0]._name_ is None:
721 return '%s.%r' % (cls.__name__, members[0]._value_)
722 else:
723 return '%s.%s' % (
724 cls.__name__,
725 '|'.join([str(m._name_ or m._value_) for m in members]),
726 )
727
728 def __bool__(self):
729 return bool(self._value_)
730
731 def __or__(self, other):
732 if not isinstance(other, self.__class__):
733 return NotImplemented
734 return self.__class__(self._value_ | other._value_)
735
736 def __and__(self, other):
737 if not isinstance(other, self.__class__):
738 return NotImplemented
739 return self.__class__(self._value_ & other._value_)
740
741 def __xor__(self, other):
742 if not isinstance(other, self.__class__):
743 return NotImplemented
744 return self.__class__(self._value_ ^ other._value_)
745
746 def __invert__(self):
747 members, uncovered = _decompose(self.__class__, self._value_)
748 inverted_members = [
749 m for m in self.__class__
750 if m not in members and not m._value_ & self._value_
751 ]
752 inverted = reduce(_or_, inverted_members, self.__class__(0))
753 return self.__class__(inverted)
754
755
756 class IntFlag(int, Flag):
757 """Support for integer-based Flags"""
758
759 @classmethod
760 def _missing_(cls, value):
761 if not isinstance(value, int):
762 raise ValueError("%r is not a valid %s" % (value, cls.__name__))
763 new_member = cls._create_pseudo_member_(value)
764 return new_member
765
766 @classmethod
767 def _create_pseudo_member_(cls, value):
768 pseudo_member = cls._value2member_map_.get(value, None)
769 if pseudo_member is None:
770 need_to_create = [value]
771 # get unaccounted for bits
772 _, extra_flags = _decompose(cls, value)
773 # timer = 10
774 while extra_flags:
775 # timer -= 1
776 bit = _high_bit(extra_flags)
777 flag_value = 2 ** bit
778 if (flag_value not in cls._value2member_map_ and
779 flag_value not in need_to_create
780 ):
781 need_to_create.append(flag_value)
782 if extra_flags == -flag_value:
783 extra_flags = 0
784 else:
785 extra_flags ^= flag_value
786 for value in reversed(need_to_create):
787 # construct singleton pseudo-members
788 pseudo_member = int.__new__(cls, value)
789 pseudo_member._name_ = None
790 pseudo_member._value_ = value
791 # use setdefault in case another thread already created a composite
792 # with this value
793 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
794 return pseudo_member
795
796 def __or__(self, other):
797 if not isinstance(other, (self.__class__, int)):
798 return NotImplemented
799 result = self.__class__(self._value_ | self.__class__(other)._value_)
800 return result
801
802 def __and__(self, other):
803 if not isinstance(other, (self.__class__, int)):
804 return NotImplemented
805 return self.__class__(self._value_ & self.__class__(other)._value_)
806
807 def __xor__(self, other):
808 if not isinstance(other, (self.__class__, int)):
809 return NotImplemented
810 return self.__class__(self._value_ ^ self.__class__(other)._value_)
811
812 __ror__ = __or__
813 __rand__ = __and__
814 __rxor__ = __xor__
815
816 def __invert__(self):
817 result = self.__class__(~self._value_)
818 return result
819
820
821 def _high_bit(value):
822 """returns index of highest bit, or -1 if value is zero or negative"""
823 return value.bit_length() - 1
824
825 def unique(enumeration):
826 """Class decorator for enumerations ensuring unique member values."""
827 duplicates = []
828 for name, member in enumeration.__members__.items():
829 if name != member.name:
830 duplicates.append((name, member.name))
831 if duplicates:
832 alias_details = ', '.join(
833 ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
834 raise ValueError('duplicate values found in %r: %s' %
835 (enumeration, alias_details))
836 return enumeration
837
838 def _decompose(flag, value):
839 """Extract all members from the value."""
840 # _decompose is only called if the value is not named
841 not_covered = value
842 negative = value < 0
843 # issue29167: wrap accesses to _value2member_map_ in a list to avoid race
844 # conditions between iterating over it and having more psuedo-
845 # members added to it
846 if negative:
847 # only check for named flags
848 flags_to_check = [
849 (m, v)
850 for v, m in list(flag._value2member_map_.items())
851 if m.name is not None
852 ]
853 else:
854 # check for named flags and powers-of-two flags
855 flags_to_check = [
856 (m, v)
857 for v, m in list(flag._value2member_map_.items())
858 if m.name is not None or _power_of_two(v)
859 ]
860 members = []
861 for member, member_value in flags_to_check:
862 if member_value and member_value & value == member_value:
863 members.append(member)
864 not_covered &= ~member_value
865 if not members and value in flag._value2member_map_:
866 members.append(flag._value2member_map_[value])
867 members.sort(key=lambda m: m._value_, reverse=True)
868 if len(members) > 1 and members[0].value == value:
869 # we have the breakdown, don't need the value member itself
870 members.pop(0)
871 return members, not_covered
872
873 def _power_of_two(value):
874 if value < 1:
875 return False
876 return value == 2 ** _high_bit(value)