From: Ethan Furman Date: Mon, 12 Apr 2021 22:03:29 +0000 (-0700) Subject: [3.9] bpo-42248: [Enum] ensure exceptions raised in ``_missing_`` are released (GH... X-Git-Tag: v3.9.5~80 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6379924ecd51e346b42b0293da0f4442a0f67707;p=thirdparty%2FPython%2Fcpython.git [3.9] bpo-42248: [Enum] ensure exceptions raised in ``_missing_`` are released (GH-25350). (GH-25370) (cherry picked from commit 8c14f5a787b21d5a1eae5d5ee981431d1c0e055f) Co-authored-by: Ethan Furman --- diff --git a/Lib/enum.py b/Lib/enum.py index 1fddb1c75e8b..be74796a8a0c 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -669,19 +669,24 @@ class Enum(metaclass=EnumMeta): except Exception as e: exc = e result = None - if isinstance(result, cls): - return result - else: - ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) - if result is None and exc is None: - raise ve_exc - elif exc is None: - exc = TypeError( - 'error in %s._missing_: returned %r instead of None or a valid member' - % (cls.__name__, result) - ) - exc.__context__ = ve_exc - raise exc + try: + if isinstance(result, cls): + return result + else: + ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) + if result is None and exc is None: + raise ve_exc + elif exc is None: + exc = TypeError( + 'error in %s._missing_: returned %r instead of None or a valid member' + % (cls.__name__, result) + ) + exc.__context__ = ve_exc + raise exc + finally: + # ensure all variables that could hold an exception are destroyed + exc = None + ve_exc = None def _generate_next_value_(name, start, count, last_values): """ diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 4e2298632552..fc2d61d59cd4 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1897,6 +1897,38 @@ class TestEnum(unittest.TestCase): else: raise Exception('Exception not raised.') + def test_missing_exceptions_reset(self): + import weakref + # + class TestEnum(enum.Enum): + VAL1 = 'val1' + VAL2 = 'val2' + # + class Class1: + def __init__(self): + # Gracefully handle an exception of our own making + try: + raise ValueError() + except ValueError: + pass + # + class Class2: + def __init__(self): + # Gracefully handle an exception of Enum's making + try: + TestEnum('invalid_value') + except ValueError: + pass + # No strong refs here so these are free to die. + class_1_ref = weakref.ref(Class1()) + class_2_ref = weakref.ref(Class2()) + # + # The exception raised by Enum creates a reference loop and thus + # Class2 instances will stick around until the next gargage collection + # cycle, unlike Class1. + self.assertIs(class_1_ref(), None) + self.assertIs(class_2_ref(), None) + def test_multiple_mixin(self): class MaxMixin: @classproperty diff --git a/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst b/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst new file mode 100644 index 000000000000..0722d35a37a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst @@ -0,0 +1 @@ +[Enum] ensure exceptions raised in ``_missing__`` are released