diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2021-04-12 15:51:20 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-12 15:51:20 (GMT) |
commit | 8c14f5a787b21d5a1eae5d5ee981431d1c0e055f (patch) | |
tree | 839f0496da66f0459427ef58c83a4af56eae001e | |
parent | 67c0b3d89c4da9750fdb43fc66d3924681b22d2e (diff) | |
download | cpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.zip cpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.tar.gz cpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.tar.bz2 |
bpo-42248: [Enum] ensure exceptions raised in ``_missing_`` are released (GH-25350)
-rw-r--r-- | Lib/enum.py | 43 | ||||
-rw-r--r-- | Lib/test/test_enum.py | 32 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst | 1 |
3 files changed, 57 insertions, 19 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index f31779b..b102a4e 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -919,25 +919,30 @@ class Enum(metaclass=EnumType): except Exception as e: exc = e result = None - if isinstance(result, cls): - return result - elif ( - Flag is not None and issubclass(cls, Flag) - and cls._boundary_ is EJECT and isinstance(result, int) - ): - 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) - ) - if not isinstance(exc, ValueError): - exc.__context__ = ve_exc - raise exc + try: + if isinstance(result, cls): + return result + elif ( + Flag is not None and issubclass(cls, Flag) + and cls._boundary_ is EJECT and isinstance(result, int) + ): + 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) + ) + if not isinstance(exc, ValueError): + 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 6002cd8..5af7cd2 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1932,6 +1932,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 0000000..0722d35 --- /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 |