Skip to content

Commit 8c14f5a

Browse files
authored
bpo-42248: [Enum] ensure exceptions raised in _missing_ are released (GH-25350)
1 parent 67c0b3d commit 8c14f5a

File tree

3 files changed

+57
-19
lines changed

3 files changed

+57
-19
lines changed

Lib/enum.py

+24-19
Original file line numberDiff line numberDiff line change
@@ -919,25 +919,30 @@ def __new__(cls, value):
919919
except Exception as e:
920920
exc = e
921921
result = None
922-
if isinstance(result, cls):
923-
return result
924-
elif (
925-
Flag is not None and issubclass(cls, Flag)
926-
and cls._boundary_ is EJECT and isinstance(result, int)
927-
):
928-
return result
929-
else:
930-
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
931-
if result is None and exc is None:
932-
raise ve_exc
933-
elif exc is None:
934-
exc = TypeError(
935-
'error in %s._missing_: returned %r instead of None or a valid member'
936-
% (cls.__name__, result)
937-
)
938-
if not isinstance(exc, ValueError):
939-
exc.__context__ = ve_exc
940-
raise exc
922+
try:
923+
if isinstance(result, cls):
924+
return result
925+
elif (
926+
Flag is not None and issubclass(cls, Flag)
927+
and cls._boundary_ is EJECT and isinstance(result, int)
928+
):
929+
return result
930+
else:
931+
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
932+
if result is None and exc is None:
933+
raise ve_exc
934+
elif exc is None:
935+
exc = TypeError(
936+
'error in %s._missing_: returned %r instead of None or a valid member'
937+
% (cls.__name__, result)
938+
)
939+
if not isinstance(exc, ValueError):
940+
exc.__context__ = ve_exc
941+
raise exc
942+
finally:
943+
# ensure all variables that could hold an exception are destroyed
944+
exc = None
945+
ve_exc = None
941946

942947
def _generate_next_value_(name, start, count, last_values):
943948
"""

Lib/test/test_enum.py

+32
Original file line numberDiff line numberDiff line change
@@ -1932,6 +1932,38 @@ def _missing_(cls, item):
19321932
else:
19331933
raise Exception('Exception not raised.')
19341934

1935+
def test_missing_exceptions_reset(self):
1936+
import weakref
1937+
#
1938+
class TestEnum(enum.Enum):
1939+
VAL1 = 'val1'
1940+
VAL2 = 'val2'
1941+
#
1942+
class Class1:
1943+
def __init__(self):
1944+
# Gracefully handle an exception of our own making
1945+
try:
1946+
raise ValueError()
1947+
except ValueError:
1948+
pass
1949+
#
1950+
class Class2:
1951+
def __init__(self):
1952+
# Gracefully handle an exception of Enum's making
1953+
try:
1954+
TestEnum('invalid_value')
1955+
except ValueError:
1956+
pass
1957+
# No strong refs here so these are free to die.
1958+
class_1_ref = weakref.ref(Class1())
1959+
class_2_ref = weakref.ref(Class2())
1960+
#
1961+
# The exception raised by Enum creates a reference loop and thus
1962+
# Class2 instances will stick around until the next gargage collection
1963+
# cycle, unlike Class1.
1964+
self.assertIs(class_1_ref(), None)
1965+
self.assertIs(class_2_ref(), None)
1966+
19351967
def test_multiple_mixin(self):
19361968
class MaxMixin:
19371969
@classproperty
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Enum] ensure exceptions raised in ``_missing__`` are released

0 commit comments

Comments
 (0)