Skip to content

Commit 74d84cf

Browse files
[3.12] gh-105497: [Enum] Fix Flag inversion when alias/mask members exist. (GH-105542) (#105572)
gh-105497: [Enum] Fix Flag inversion when alias/mask members exist. (GH-105542) When inverting a Flag member (or boundary STRICT), only consider other canonical flags; when inverting an IntFlag member (or boundary KEEP), also consider aliases. (cherry picked from commit 59f009e) Co-authored-by: Ethan Furman <[email protected]>
1 parent da672b2 commit 74d84cf

File tree

3 files changed

+47
-8
lines changed

3 files changed

+47
-8
lines changed

Lib/enum.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -1463,12 +1463,11 @@ def _missing_(cls, value):
14631463
else:
14641464
pseudo_member._name_ = None
14651465
# use setdefault in case another thread already created a composite
1466-
# with this value, but only if all members are known
1467-
# note: zero is a special case -- add it
1468-
if not unknown:
1469-
pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
1470-
if neg_value is not None:
1471-
cls._value2member_map_[neg_value] = pseudo_member
1466+
# with this value
1467+
# note: zero is a special case -- always add it
1468+
pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
1469+
if neg_value is not None:
1470+
cls._value2member_map_[neg_value] = pseudo_member
14721471
return pseudo_member
14731472

14741473
def __contains__(self, other):
@@ -1544,8 +1543,8 @@ def __invert__(self):
15441543
# use all bits
15451544
self._inverted_ = self.__class__(~self._value_)
15461545
else:
1547-
# calculate flags not in this member
1548-
self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_)
1546+
# use canonical bits (i.e. calculate flags not in this member)
1547+
self._inverted_ = self.__class__(self._singles_mask_ ^ self._value_)
15491548
if isinstance(self._inverted_, self.__class__):
15501549
self._inverted_._inverted_ = self
15511550
return self._inverted_

Lib/test/test_enum.py

+39
Original file line numberDiff line numberDiff line change
@@ -3045,6 +3045,33 @@ class Color(Flag):
30453045
WHITE = RED|GREEN|BLUE
30463046
BLANCO = RED|GREEN|BLUE
30473047

3048+
class Complete(Flag):
3049+
A = 0x01
3050+
B = 0x02
3051+
3052+
class Partial(Flag):
3053+
A = 0x01
3054+
B = 0x02
3055+
MASK = 0xff
3056+
3057+
class CompleteInt(IntFlag):
3058+
A = 0x01
3059+
B = 0x02
3060+
3061+
class PartialInt(IntFlag):
3062+
A = 0x01
3063+
B = 0x02
3064+
MASK = 0xff
3065+
3066+
class CompleteIntStrict(IntFlag, boundary=STRICT):
3067+
A = 0x01
3068+
B = 0x02
3069+
3070+
class PartialIntStrict(IntFlag, boundary=STRICT):
3071+
A = 0x01
3072+
B = 0x02
3073+
MASK = 0xff
3074+
30483075
def test_or(self):
30493076
Perm = self.Perm
30503077
for i in Perm:
@@ -3103,6 +3130,18 @@ def test_invert(self):
31033130
Open = self.Open
31043131
self.assertIs(Open.WO & ~Open.WO, Open.RO)
31053132
self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE)
3133+
Complete = self.Complete
3134+
self.assertIs(~Complete.A, Complete.B)
3135+
Partial = self.Partial
3136+
self.assertIs(~Partial.A, Partial.B)
3137+
CompleteInt = self.CompleteInt
3138+
self.assertIs(~CompleteInt.A, CompleteInt.B)
3139+
PartialInt = self.PartialInt
3140+
self.assertIs(~PartialInt.A, PartialInt(254))
3141+
CompleteIntStrict = self.CompleteIntStrict
3142+
self.assertIs(~CompleteIntStrict.A, CompleteIntStrict.B)
3143+
PartialIntStrict = self.PartialIntStrict
3144+
self.assertIs(~PartialIntStrict.A, PartialIntStrict.B)
31063145

31073146
def test_bool(self):
31083147
Perm = self.Perm
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix flag inversion when alias/mask members exist.

0 commit comments

Comments
 (0)