From 9346ae63702d410d5e06eb1b6cd7bf523f757bf8 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 7 Jul 2022 07:26:56 -0400 Subject: [PATCH] gh-93910: Fix enum performance regression (GH-94614) This removes the performance regression in 3.11, **at the expense of not fixing the "bug" that allows accessing values from values** (e.g. `Color.RED.BLUE`). Using the benchmark @markshannon [presented](https://github.com/python/cpython/issues/93910GH-issuecomment-1165503032), the results are: | Version | Enum | Fast enum | Normal class | | --- | --- | --- | --- | | 3.10 | 2.04 | 0.59 | 0.56 | | 3.11 | 2.78 | 0.31 | 0.15 | | This PR | 1.30 | 0.32 | 0.16 | I share this mostly as information about the source of the regression, as this may be useful. It may be that the lower-risk approach for the beta is just to revert to a previously-known working state. (cherry picked from commit ed136b96737fdbeff864079d12904cb962c6cce5) Co-authored-by: Michael Droettboom --- Lib/enum.py | 17 +++++++++-------- Lib/test/test_enum.py | 1 + ...022-07-06-14-45-12.gh-issue-93910.iZcp67.rst | 3 +++ 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-06-14-45-12.gh-issue-93910.iZcp67.rst diff --git a/Lib/enum.py b/Lib/enum.py index e11093c6cf4683..e5971917cef46b 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -309,16 +309,17 @@ def __set_name__(self, enum_class, member_name): if descriptor and not need_override: # previous enum.property found, no further action needed pass - else: + elif descriptor and need_override: redirect = property() redirect.__set_name__(enum_class, member_name) - if descriptor and need_override: - # previous enum.property found, but some other inherited attribute - # is in the way; copy fget, fset, fdel to this one - redirect.fget = descriptor.fget - redirect.fset = descriptor.fset - redirect.fdel = descriptor.fdel + # Previous enum.property found, but some other inherited attribute + # is in the way; copy fget, fset, fdel to this one. + redirect.fget = descriptor.fget + redirect.fset = descriptor.fset + redirect.fdel = descriptor.fdel setattr(enum_class, member_name, redirect) + else: + setattr(enum_class, member_name, enum_member) # now add to _member_map_ (even aliases) enum_class._member_map_[member_name] = enum_member try: @@ -647,7 +648,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k 'member order does not match _order_:\n %r\n %r' % (enum_class._member_names_, _order_) ) - # + return enum_class def __bool__(cls): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index ac5c52b2b303b3..f9a662746fb1a0 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2611,6 +2611,7 @@ class Private(Enum): self.assertEqual(Private._Private__corporal, 'Radar') self.assertEqual(Private._Private__major_, 'Hoolihan') + @unittest.skip("Accessing all values retained for performance reasons, see GH-93910") def test_exception_for_member_from_member_access(self): with self.assertRaisesRegex(AttributeError, " member has no attribute .NO."): class Di(Enum): diff --git a/Misc/NEWS.d/next/Library/2022-07-06-14-45-12.gh-issue-93910.iZcp67.rst b/Misc/NEWS.d/next/Library/2022-07-06-14-45-12.gh-issue-93910.iZcp67.rst new file mode 100644 index 00000000000000..2e589118e3efd3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-06-14-45-12.gh-issue-93910.iZcp67.rst @@ -0,0 +1,3 @@ +The ability to access the other values of an enum on an enum (e.g. +``Color.RED.BLUE``) has been restored in order to fix a performance +regression.