1
1
import sys
2
2
from types import MappingProxyType , DynamicClassAttribute
3
3
from functools import reduce
4
- from operator import or_ as _or_
4
+ from operator import or_ as _or_ , and_ as _and_ , xor , neg
5
5
6
6
# try _collections first to reduce startup cost
7
7
try :
@@ -47,11 +47,12 @@ def _break_on_call_reduce(self, proto):
47
47
cls .__reduce_ex__ = _break_on_call_reduce
48
48
cls .__module__ = '<unknown>'
49
49
50
+ _auto_null = object ()
50
51
class auto :
51
52
"""
52
53
Instances are replaced with an appropriate value in Enum class suites.
53
54
"""
54
- pass
55
+ value = _auto_null
55
56
56
57
57
58
class _EnumDict (dict ):
@@ -77,7 +78,7 @@ def __setitem__(self, key, value):
77
78
"""
78
79
if _is_sunder (key ):
79
80
if key not in (
80
- '_order_' , '_create_pseudo_member_' , '_decompose_' ,
81
+ '_order_' , '_create_pseudo_member_' ,
81
82
'_generate_next_value_' , '_missing_' ,
82
83
):
83
84
raise ValueError ('_names_ are reserved for future Enum use' )
@@ -94,7 +95,9 @@ def __setitem__(self, key, value):
94
95
# enum overwriting a descriptor?
95
96
raise TypeError ('%r already defined as: %r' % (key , self [key ]))
96
97
if isinstance (value , auto ):
97
- value = self ._generate_next_value (key , 1 , len (self ._member_names ), self ._last_values [:])
98
+ if value .value == _auto_null :
99
+ value .value = self ._generate_next_value (key , 1 , len (self ._member_names ), self ._last_values [:])
100
+ value = value .value
98
101
self ._member_names .append (key )
99
102
self ._last_values .append (value )
100
103
super ().__setitem__ (key , value )
@@ -658,7 +661,7 @@ def _generate_next_value_(name, start, count, last_values):
658
661
try :
659
662
high_bit = _high_bit (last_value )
660
663
break
661
- except TypeError :
664
+ except Exception :
662
665
raise TypeError ('Invalid Flag value: %r' % last_value ) from None
663
666
return 2 ** (high_bit + 1 )
664
667
@@ -668,61 +671,38 @@ def _missing_(cls, value):
668
671
if value < 0 :
669
672
value = ~ value
670
673
possible_member = cls ._create_pseudo_member_ (value )
671
- for member in possible_member ._decompose_ ():
672
- if member ._name_ is None and member ._value_ != 0 :
673
- raise ValueError ('%r is not a valid %s' % (original_value , cls .__name__ ))
674
674
if original_value < 0 :
675
675
possible_member = ~ possible_member
676
676
return possible_member
677
677
678
678
@classmethod
679
679
def _create_pseudo_member_ (cls , value ):
680
+ """
681
+ Create a composite member iff value contains only members.
682
+ """
680
683
pseudo_member = cls ._value2member_map_ .get (value , None )
681
684
if pseudo_member is None :
682
- # construct a non-singleton enum pseudo-member
685
+ # verify all bits are accounted for
686
+ _ , extra_flags = _decompose (cls , value )
687
+ if extra_flags :
688
+ raise ValueError ("%r is not a valid %s" % (value , cls .__name__ ))
689
+ # construct a singleton enum pseudo-member
683
690
pseudo_member = object .__new__ (cls )
684
691
pseudo_member ._name_ = None
685
692
pseudo_member ._value_ = value
686
693
cls ._value2member_map_ [value ] = pseudo_member
687
694
return pseudo_member
688
695
689
- def _decompose_ (self ):
690
- """Extract all members from the value."""
691
- value = self ._value_
692
- members = []
693
- cls = self .__class__
694
- for member in sorted (cls , key = lambda m : m ._value_ , reverse = True ):
695
- while _high_bit (value ) > _high_bit (member ._value_ ):
696
- unknown = self ._create_pseudo_member_ (2 ** _high_bit (value ))
697
- members .append (unknown )
698
- value &= ~ unknown ._value_
699
- if (
700
- (value & member ._value_ == member ._value_ )
701
- and (member ._value_ or not members )
702
- ):
703
- value &= ~ member ._value_
704
- members .append (member )
705
- if not members or value :
706
- members .append (self ._create_pseudo_member_ (value ))
707
- members = list (members )
708
- return members
709
-
710
696
def __contains__ (self , other ):
711
697
if not isinstance (other , self .__class__ ):
712
698
return NotImplemented
713
699
return other ._value_ & self ._value_ == other ._value_
714
700
715
- def __iter__ (self ):
716
- if self .value == 0 :
717
- return iter ([])
718
- else :
719
- return iter (self ._decompose_ ())
720
-
721
701
def __repr__ (self ):
722
702
cls = self .__class__
723
703
if self ._name_ is not None :
724
704
return '<%s.%s: %r>' % (cls .__name__ , self ._name_ , self ._value_ )
725
- members = self ._decompose_ ( )
705
+ members , uncovered = _decompose ( cls , self ._value_ )
726
706
return '<%s.%s: %r>' % (
727
707
cls .__name__ ,
728
708
'|' .join ([str (m ._name_ or m ._value_ ) for m in members ]),
@@ -733,7 +713,7 @@ def __str__(self):
733
713
cls = self .__class__
734
714
if self ._name_ is not None :
735
715
return '%s.%s' % (cls .__name__ , self ._name_ )
736
- members = self ._decompose_ ( )
716
+ members , uncovered = _decompose ( cls , self ._value_ )
737
717
if len (members ) == 1 and members [0 ]._name_ is None :
738
718
return '%s.%r' % (cls .__name__ , members [0 ]._value_ )
739
719
else :
@@ -761,35 +741,58 @@ def __xor__(self, other):
761
741
return self .__class__ (self ._value_ ^ other ._value_ )
762
742
763
743
def __invert__ (self ):
764
- members = self ._decompose_ ()
765
- inverted_members = [m for m in self .__class__ if m not in members and not m ._value_ & self ._value_ ]
744
+ members , uncovered = _decompose (self .__class__ , self ._value_ )
745
+ inverted_members = [
746
+ m for m in self .__class__
747
+ if m not in members and not m ._value_ & self ._value_
748
+ ]
766
749
inverted = reduce (_or_ , inverted_members , self .__class__ (0 ))
767
750
return self .__class__ (inverted )
768
751
769
752
770
753
class IntFlag (int , Flag ):
771
754
"""Support for integer-based Flags"""
772
755
756
+ @classmethod
757
+ def _missing_ (cls , value ):
758
+ if not isinstance (value , int ):
759
+ raise ValueError ("%r is not a valid %s" % (value , cls .__name__ ))
760
+ new_member = cls ._create_pseudo_member_ (value )
761
+ return new_member
762
+
773
763
@classmethod
774
764
def _create_pseudo_member_ (cls , value ):
775
765
pseudo_member = cls ._value2member_map_ .get (value , None )
776
766
if pseudo_member is None :
777
- # construct a non-singleton enum pseudo-member
778
- pseudo_member = int .__new__ (cls , value )
779
- pseudo_member ._name_ = None
780
- pseudo_member ._value_ = value
781
- cls ._value2member_map_ [value ] = pseudo_member
767
+ need_to_create = [value ]
768
+ # get unaccounted for bits
769
+ _ , extra_flags = _decompose (cls , value )
770
+ # timer = 10
771
+ while extra_flags :
772
+ # timer -= 1
773
+ bit = _high_bit (extra_flags )
774
+ flag_value = 2 ** bit
775
+ if (flag_value not in cls ._value2member_map_ and
776
+ flag_value not in need_to_create
777
+ ):
778
+ need_to_create .append (flag_value )
779
+ if extra_flags == - flag_value :
780
+ extra_flags = 0
781
+ else :
782
+ extra_flags ^= flag_value
783
+ for value in reversed (need_to_create ):
784
+ # construct singleton pseudo-members
785
+ pseudo_member = int .__new__ (cls , value )
786
+ pseudo_member ._name_ = None
787
+ pseudo_member ._value_ = value
788
+ cls ._value2member_map_ [value ] = pseudo_member
782
789
return pseudo_member
783
790
784
- @classmethod
785
- def _missing_ (cls , value ):
786
- possible_member = cls ._create_pseudo_member_ (value )
787
- return possible_member
788
-
789
791
def __or__ (self , other ):
790
792
if not isinstance (other , (self .__class__ , int )):
791
793
return NotImplemented
792
- return self .__class__ (self ._value_ | self .__class__ (other )._value_ )
794
+ result = self .__class__ (self ._value_ | self .__class__ (other )._value_ )
795
+ return result
793
796
794
797
def __and__ (self , other ):
795
798
if not isinstance (other , (self .__class__ , int )):
@@ -806,17 +809,13 @@ def __xor__(self, other):
806
809
__rxor__ = __xor__
807
810
808
811
def __invert__ (self ):
809
- # members = self._decompose_()
810
- # inverted_members = [m for m in self.__class__ if m not in members and not m._value_ & self._value_]
811
- # inverted = reduce(_or_, inverted_members, self.__class__(0))
812
- return self .__class__ (~ self ._value_ )
813
-
814
-
812
+ result = self .__class__ (~ self ._value_ )
813
+ return result
815
814
816
815
817
816
def _high_bit (value ):
818
817
"""returns index of highest bit, or -1 if value is zero or negative"""
819
- return value .bit_length () - 1 if value > 0 else - 1
818
+ return value .bit_length () - 1
820
819
821
820
def unique (enumeration ):
822
821
"""Class decorator for enumerations ensuring unique member values."""
@@ -830,3 +829,40 @@ def unique(enumeration):
830
829
raise ValueError ('duplicate values found in %r: %s' %
831
830
(enumeration , alias_details ))
832
831
return enumeration
832
+
833
+ def _decompose (flag , value ):
834
+ """Extract all members from the value."""
835
+ # _decompose is only called if the value is not named
836
+ not_covered = value
837
+ negative = value < 0
838
+ if negative :
839
+ # only check for named flags
840
+ flags_to_check = [
841
+ (m , v )
842
+ for v , m in flag ._value2member_map_ .items ()
843
+ if m .name is not None
844
+ ]
845
+ else :
846
+ # check for named flags and powers-of-two flags
847
+ flags_to_check = [
848
+ (m , v )
849
+ for v , m in flag ._value2member_map_ .items ()
850
+ if m .name is not None or _power_of_two (v )
851
+ ]
852
+ members = []
853
+ for member , member_value in flags_to_check :
854
+ if member_value and member_value & value == member_value :
855
+ members .append (member )
856
+ not_covered &= ~ member_value
857
+ if not members and value in flag ._value2member_map_ :
858
+ members .append (flag ._value2member_map_ [value ])
859
+ members .sort (key = lambda m : m ._value_ , reverse = True )
860
+ if len (members ) > 1 and members [0 ].value == value :
861
+ # we have the breakdown, don't need the value member itself
862
+ members .pop (0 )
863
+ return members , not_covered
864
+
865
+ def _power_of_two (value ):
866
+ if value < 1 :
867
+ return False
868
+ return value == 2 ** _high_bit (value )
0 commit comments