From 08143359141000fcf730ebffb58f4ba46b35e9c1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 16 Jun 2022 11:16:57 +0300 Subject: [PATCH 1/2] gh-93820: Pickle enum.Flag by name --- Lib/enum.py | 11 ++++- Lib/test/test_enum.py | 44 ++++++++++++++++++- ...2-06-16-11-16-53.gh-issue-93820.00X0Y5.rst | 1 + 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-06-16-11-16-53.gh-issue-93820.00X0Y5.rst diff --git a/Lib/enum.py b/Lib/enum.py index ee32d5d4e058bc..e68f741933675b 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1370,7 +1370,16 @@ class Flag(Enum, boundary=STRICT): """ def __reduce_ex__(self, proto): - return self.__class__, (self._value_, ) + for m in self: + rest = self._value_ & ~m._value_ + if rest: + return _or_, (m, self.__class__(rest)) + else: + break + if self._name_ is None: + return self.__class__, (self._value_,) + else: + return getattr, (self.__class__, self._name_) _numeric_repr_ = repr diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 28594b010d80d3..33b3f9dae74a42 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -65,10 +65,27 @@ class FloatStooges(float, Enum): class FlagStooges(Flag): LARRY = 1 CURLY = 2 - MOE = 3 + MOE = 4 except Exception as exc: FlagStooges = exc +class FlagStoogesWithZero(Flag): + NOFLAG = 0 + LARRY = 1 + CURLY = 2 + MOE = 4 + +class IntFlagStooges(IntFlag): + LARRY = 1 + CURLY = 2 + MOE = 4 + +class IntFlagStoogesWithZero(IntFlag): + NOFLAG = 0 + LARRY = 1 + CURLY = 2 + MOE = 4 + # for pickle test and subclass tests class Name(StrEnum): BDFL = 'Guido van Rossum' @@ -2956,9 +2973,32 @@ def test_programatic_function_from_dict(self): def test_pickle(self): if isinstance(FlagStooges, Exception): raise FlagStooges - test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE) + test_pickle_dump_load(self.assertIs, FlagStooges.CURLY) + test_pickle_dump_load(self.assertEqual, + FlagStooges.CURLY|FlagStooges.MOE) + test_pickle_dump_load(self.assertEqual, + FlagStooges.CURLY&~FlagStooges.CURLY) test_pickle_dump_load(self.assertIs, FlagStooges) + test_pickle_dump_load(self.assertIs, FlagStoogesWithZero.CURLY) + test_pickle_dump_load(self.assertEqual, + FlagStoogesWithZero.CURLY|FlagStoogesWithZero.MOE) + test_pickle_dump_load(self.assertIs, FlagStoogesWithZero.NOFLAG) + + test_pickle_dump_load(self.assertIs, IntFlagStooges.CURLY) + test_pickle_dump_load(self.assertEqual, + IntFlagStooges.CURLY|IntFlagStooges.MOE) + test_pickle_dump_load(self.assertEqual, + IntFlagStooges.CURLY|IntFlagStooges.MOE|0x30) + test_pickle_dump_load(self.assertEqual, IntFlagStooges(0)) + test_pickle_dump_load(self.assertEqual, IntFlagStooges(0x30)) + test_pickle_dump_load(self.assertIs, IntFlagStooges) + + test_pickle_dump_load(self.assertIs, IntFlagStoogesWithZero.CURLY) + test_pickle_dump_load(self.assertEqual, + IntFlagStoogesWithZero.CURLY|IntFlagStoogesWithZero.MOE) + test_pickle_dump_load(self.assertIs, IntFlagStoogesWithZero.NOFLAG) + @unittest.skipIf( python_version >= (3, 12), '__contains__ now returns True/False for all inputs', diff --git a/Misc/NEWS.d/next/Library/2022-06-16-11-16-53.gh-issue-93820.00X0Y5.rst b/Misc/NEWS.d/next/Library/2022-06-16-11-16-53.gh-issue-93820.00X0Y5.rst new file mode 100644 index 00000000000000..70bb1e6c0cd764 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-16-11-16-53.gh-issue-93820.00X0Y5.rst @@ -0,0 +1 @@ +Pickle :class:`enum.Flag` by name. From 79f7f7e9a25cbe55904a373b4f00f2fd0ac16ee6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 16 Jun 2022 16:10:01 +0300 Subject: [PATCH 2/2] Optimize. --- Lib/enum.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index e68f741933675b..71f0abfe847db5 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1370,16 +1370,21 @@ class Flag(Enum, boundary=STRICT): """ def __reduce_ex__(self, proto): - for m in self: - rest = self._value_ & ~m._value_ + cls = self.__class__ + unknown = self._value_ & ~cls._flag_mask_ + member_value = self._value_ & cls._flag_mask_ + if unknown and member_value: + return _or_, (cls(member_value), unknown) + for val in _iter_bits_lsb(member_value): + rest = member_value & ~val if rest: - return _or_, (m, self.__class__(rest)) + return _or_, (cls(rest), cls._value2member_map_.get(val)) else: break if self._name_ is None: - return self.__class__, (self._value_,) + return cls, (self._value_,) else: - return getattr, (self.__class__, self._name_) + return getattr, (cls, self._name_) _numeric_repr_ = repr