8000 [3.11] gh-105497: [Enum] Fix flag mask inversion when unnamed flags e… · python/cpython@318f6ae · GitHub
[go: up one dir, main page]

Skip to content

Commit 318f6ae

Browse files
[3.11] gh-105497: [Enum] Fix flag mask inversion when unnamed flags exist (GH-106468) (#106621)
gh-105497: [Enum] Fix flag mask inversion when unnamed flags exist (GH-106468) For example: class Flag(enum.Flag): A = 0x01 B = 0x02 MASK = 0xff ~Flag.MASK is Flag(0) (cherry picked from commit 95b7426) Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
1 parent d63953d commit 318f6ae

File tree

3 files changed

+86
-61
lines changed

3 files changed

+86
-61
lines changed

Lib/enum.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,14 +1522,10 @@ def __xor__(self, other):
15221522

15231523
def __invert__(self):
15241524
if self._inverted_ is None:
1525-
if self._boundary_ is KEEP:
1526-
# use all bits
1525+
if self._boundary_ in (EJECT, KEEP):
15271526
self._inverted_ = self.__class__(~self._value_)
15281527
else:
1529-
# use canonical bits (i.e. calculate flags not in this member)
1530-
self._inverted_ = self.__class__(self._singles_mask_ ^ self._value_)
1531-
if isinstance(self._inverted_, self.__class__):
1532-
self._inverted_._inverted_ = self
1528+
self._inverted_ = self.__class__(self._singles_mask_ & ~self._value_)
15331529
return self._inverted_
15341530

15351531
__rand__ = __and__

Lib/test/test_enum.py

Lines changed: 83 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,89 @@ def test_default_missing_with_wrong_type_value(self):
789789
self.MainEnum('RED')
790790
self.assertIs(ctx.exception.__context__, None)
791791

792+
def test_closed_invert_expectations(self):
793+
class ClosedAB(self.enum_type):
794+
A = 1
795+
B = 2
796+
MASK = 3
797+
A, B = ClosedAB
798+
AB_MASK = ClosedAB.MASK
799+
#
800+
self.assertIs(~A, B)
801+
self.assertIs(~B, A)
802+
self.assertIs(~(A|B), ClosedAB(0))
803+
self.assertIs(~AB_MASK, ClosedAB(0))
804+
self.assertIs(~ClosedAB(0), (A|B))
805+
#
806+
class ClosedXYZ(self.enum_type):
807+
X = 4
808+
Y = 2
809+
Z = 1
810+
MASK = 7
811+
X, Y, Z = ClosedXYZ
812+
XYZ_MASK = ClosedXYZ.MASK
813+
#
814+
self.assertIs(~X, Y|Z)
815+
self.assertIs(~Y, X|Z)
816+
self.assertIs(~Z, X|Y)
817+
self.assertIs(~(X|Y), Z)
818+
self.assertIs(~(X|Z), Y)
819+
self.assertIs(~(Y|Z), X)
820+
self.assertIs(~(X|Y|Z), ClosedXYZ(0))
821+
self.assertIs(~XYZ_MASK, ClosedXYZ(0))
822+
self.assertIs(~ClosedXYZ(0), (X|Y|Z))
823+
824+
def test_open_invert_expectations(self):
825+
class OpenAB(self.enum_type):
826+
A = 1
827+
B = 2
828+
MASK = 255
829+
A, B = OpenAB
830+
AB_MASK = OpenAB.MASK
831+
#
832+
if OpenAB._boundary_ in (EJECT, KEEP):
833+
self.assertIs(~A, OpenAB(254))
834+
self.assertIs(~B, OpenAB(253))
835+
self.assertIs(~(A|B), OpenAB(252))
836+
self.assertIs(~AB_MASK, OpenAB(0))
837+
self.assertIs(~OpenAB(0), AB_MASK)
838+
else:
839+
self.assertIs(~A, B)
840+
self.assertIs(~B, A)
841+
self.assertIs(~(A|B), OpenAB(0))
842+
self.assertIs(~AB_MASK, OpenAB(0))
843+
self.assertIs(~OpenAB(0), (A|B))
844+
#
845+
class OpenXYZ(self.enum_type):
846+
X = 4
847+
Y = 2
848+
Z = 1
849+
MASK = 31
850+
X, Y, Z = OpenXYZ
851+
XYZ_MASK = OpenXYZ.MASK
852+
#
853+
if OpenXYZ._boundary_ in (EJECT, KEEP):
854+
self.assertIs(~X, OpenXYZ(27))
855+
self.assertIs(~Y, OpenXYZ(29))
856+
self.assertIs(~Z, OpenXYZ(30))
857+
self.assertIs(~(X|Y), OpenXYZ(25))
858+
self.assertIs(~(X|Z), OpenXYZ(26))
859+
self.assertIs(~(Y|Z), OpenXYZ(28))
860+
self.assertIs(~(X|Y|Z), OpenXYZ(24))
861+
self.assertIs(~XYZ_MASK, OpenXYZ(0))
862+
self.assertTrue(~OpenXYZ(0), XYZ_MASK)
863+
else:
864+
self.assertIs(~X, Y|Z)
865+
self.assertIs(~Y, X|Z)
866+
self.assertIs(~Z, X|Y)
867+
self.assertIs(~(X|Y), Z)
868+
self.assertIs(~(X|Z), Y)
869+
self.assertIs(~(Y|Z), X)
870+
self.assertIs(~(X|Y|Z), OpenXYZ(0))
871+
self.assertIs(~XYZ_MASK, OpenXYZ(0))
872+
self.assertTrue(~OpenXYZ(0), (X|Y|Z))
873+
874+
792875
class TestPlainEnum(_EnumTests, _PlainOutputTests, unittest.TestCase):
793876
enum_type = Enum
794877

@@ -2913,33 +2996,6 @@ class Color(Flag):
29132996
WHITE = RED|GREEN|BLUE
29142997
BLANCO = RED|GREEN|BLUE
29152998

2916-
class Complete(Flag):
2917-
A = 0x01
2918-
B = 0x02
2919-
2920-
class Partial(Flag):
2921-
A = 0x01
2922-
B = 0x02
2923-
MASK = 0xff
2924-
2925-
class CompleteInt(IntFlag):
2926-
A = 0x01
2927-
B = 0x02
2928-
2929-
class PartialInt(IntFlag):
2930-
A = 0x01
2931-
B = 0x02
2932-
MASK = 0xff
2933-
2934-
class CompleteIntStrict(IntFlag, boundary=STRICT):
2935-
A = 0x01
2936-
B = 0x02
2937-
2938-
class PartialIntStrict(IntFlag, boundary=STRICT):
2939-
A = 0x01
2940-
B = 0x02
2941-
MASK = 0xff
2942-
29432999
def test_or(self):
29443000
Perm = self.Perm
29453001
for i in Perm:
@@ -2983,34 +3039,6 @@ def test_xor(self):
29833039
self.assertIs(Open.RO ^ Open.CE, Open.CE)
29843040
self.assertIs(Open.CE ^ Open.CE, Open.RO)
29853041

2986-
def test_invert(self):
2987-
Perm = self.Perm
2988-
RW = Perm.R | Perm.W
2989-
RX = Perm.R | Perm.X
2990-
WX = Perm.W | Perm.X
2991-
RWX = Perm.R | Perm.W | Perm.X
2992-
values = list(Perm) + [RW, RX, WX, RWX, Perm(0)]
2993-
for i in values:
2994-
self.assertIs(type(~i), Perm)
2995-
self.assertEqual(~~i, i)
2996-
for i in Perm:
2997-
self.assertIs(~~i, i)
2998-
Open = self.Open
2999-
self.assertIs(Open.WO & ~Open.WO, Open.RO)
3000-
self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE)
3001-
Complete = self.Complete
3002-
self.assertIs(~Complete.A, Complete.B)
3003-
Partial = self.Partial
3004-
self.assertIs(~Partial.A, Partial.B)
3005-
CompleteInt = self.CompleteInt
3006-
self.assertIs(~CompleteInt.A, CompleteInt.B)
3007-
PartialInt = self.PartialInt
3008-
self.assertIs(~PartialInt.A, PartialInt(254))
3009-
CompleteIntStrict = self.CompleteIntStrict
3010-
self.assertIs(~CompleteIntStrict.A, CompleteIntStrict.B)
3011-
PartialIntStrict = self.PartialIntStrict
3012-
self.assertIs(~PartialIntStrict.A, PartialIntStrict.B)
3013-
30143042
def test_bool(self):
30153043
Perm = self.Perm
30163044
for f in Perm:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix flag mask inversion when unnamed flags exist.

0 commit comments

Comments
 (0)
0