diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2023-06-09 15:56:05 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-09 15:56:05 (GMT) |
commit | 59f009e5898a006cdc8f5249be589de6edfe5cd0 (patch) | |
tree | 1bc9e58acd1b564fbc2fbbbc2431d823073ae5f5 | |
parent | 8e755923c97d689ba7c7fe8deb50c1b169263264 (diff) | |
download | cpython-59f009e5898a006cdc8f5249be589de6edfe5cd0.zip cpython-59f009e5898a006cdc8f5249be589de6edfe5cd0.tar.gz cpython-59f009e5898a006cdc8f5249be589de6edfe5cd0.tar.bz2 |
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.
-rw-r--r-- | Lib/enum.py | 15 | ||||
-rw-r--r-- | Lib/test/test_enum.py | 39 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst | 1 |
3 files changed, 47 insertions, 8 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index a22bcca..47e31b1 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1439,12 +1439,11 @@ class Flag(Enum, boundary=STRICT): else: pseudo_member._name_ = None # use setdefault in case another thread already created a composite - # with this value, but only if all members are known - # note: zero is a special case -- add it - if not unknown: - pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) - if neg_value is not None: - cls._value2member_map_[neg_value] = pseudo_member + # with this value + # note: zero is a special case -- always add it + pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) + if neg_value is not None: + cls._value2member_map_[neg_value] = pseudo_member return pseudo_member def __contains__(self, other): @@ -1520,8 +1519,8 @@ class Flag(Enum, boundary=STRICT): # use all bits self._inverted_ = self.__class__(~self._value_) else: - # calculate flags not in this member - self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_) + # use canonical bits (i.e. calculate flags not in this member) + self._inverted_ = self.__class__(self._singles_mask_ ^ self._value_) if isinstance(self._inverted_, self.__class__): self._inverted_._inverted_ = self return self._inverted_ diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 02dc7c1..54b7d18 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3045,6 +3045,33 @@ class OldTestFlag(unittest.TestCase): WHITE = RED|GREEN|BLUE BLANCO = RED|GREEN|BLUE + class Complete(Flag): + A = 0x01 + B = 0x02 + + class Partial(Flag): + A = 0x01 + B = 0x02 + MASK = 0xff + + class CompleteInt(IntFlag): + A = 0x01 + B = 0x02 + + class PartialInt(IntFlag): + A = 0x01 + B = 0x02 + MASK = 0xff + + class CompleteIntStrict(IntFlag, boundary=STRICT): + A = 0x01 + B = 0x02 + + class PartialIntStrict(IntFlag, boundary=STRICT): + A = 0x01 + B = 0x02 + MASK = 0xff + def test_or(self): Perm = self.Perm for i in Perm: @@ -3103,6 +3130,18 @@ class OldTestFlag(unittest.TestCase): Open = self.Open self.assertIs(Open.WO & ~Open.WO, Open.RO) self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + Complete = self.Complete + self.assertIs(~Complete.A, Complete.B) + Partial = self.Partial + self.assertIs(~Partial.A, Partial.B) + CompleteInt = self.CompleteInt + self.assertIs(~CompleteInt.A, CompleteInt.B) + PartialInt = self.PartialInt + self.assertIs(~PartialInt.A, PartialInt(254)) + CompleteIntStrict = self.CompleteIntStrict + self.assertIs(~CompleteIntStrict.A, CompleteIntStrict.B) + PartialIntStrict = self.PartialIntStrict + self.assertIs(~PartialIntStrict.A, PartialIntStrict.B) def test_bool(self): Perm = self.Perm diff --git a/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst b/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst new file mode 100644 index 0000000..2d4e209 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst @@ -0,0 +1 @@ +Fix flag inversion when alias/mask members exist. |