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