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