Skip to content

Commit 6379924

Browse files
authored
[3.9] bpo-42248: [Enum] ensure exceptions raised in _missing_ are released (GH-25350). (GH-25370)
(cherry picked from commit 8c14f5a) Co-authored-by: Ethan Furman <[email protected]>
1 parent de06baa commit 6379924

File tree

3 files changed

+51
-13
lines changed

3 files changed

+51
-13
lines changed

Lib/enum.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -669,19 +669,24 @@ def __new__(cls, value):
669669
except Exception as e:
670670
exc = e
671671
result = None
672-
if isinstance(result, cls):
673-
return result
674-
else:
675-
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
676-
if result is None and exc is None:
677-
raise ve_exc
678-
elif exc is None:
679-
exc = TypeError(
680-
'error in %s._missing_: returned %r instead of None or a valid member'
681-
% (cls.__name__, result)
682-
)
683-
exc.__context__ = ve_exc
684-
raise exc
672+
try:
673+
if isinstance(result, cls):
674+
return result
675+
else:
676+
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
677+
if result is None and exc is None:
678+
raise ve_exc
679+
elif exc is None:
680+
exc = TypeError(
681+
'error in %s._missing_: returned %r instead of None or a valid member'
682+
% (cls.__name__, result)
683+
)
684+
exc.__context__ = ve_exc
685+
raise exc
686+
finally:
687+
# ensure all variables that could hold an exception are destroyed
688+
exc = None
689+
ve_exc = None
685690

686691
def _generate_next_value_(name, start, count, last_values):
687692
"""

Lib/test/test_enum.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,38 @@ def _missing_(cls, item):
18971897
else:
18981898
raise Exception('Exception not raised.')
18991899

1900+
def test_missing_exceptions_reset(self):
1901+
import weakref
1902+
#
1903+
class TestEnum(enum.Enum):
1904+
VAL1 = 'val1'
1905+
VAL2 = 'val2'
1906+
#
1907+
class Class1:
1908+
def __init__(self):
1909+
# Gracefully handle an exception of our own making
1910+
try:
1911+
raise ValueError()
1912+
except ValueError:
1913+
pass
1914+
#
1915+
class Class2:
1916+
def __init__(self):
1917+
# Gracefully handle an exception of Enum's making
1918+
try:
1919+
TestEnum('invalid_value')
1920+
except ValueError:
1921+
pass
1922+
# No strong refs here so these are free to die.
1923+
class_1_ref = weakref.ref(Class1())
1924+
class_2_ref = weakref.ref(Class2())
1925+
#
1926+
# The exception raised by Enum creates a reference loop and thus
1927+
# Class2 instances will stick around until the next gargage collection
1928+
# cycle, unlike Class1.
1929+
self.assertIs(class_1_ref(), None)
1930+
self.assertIs(class_2_ref(), None)
1931+
19001932
def test_multiple_mixin(self):
19011933
class MaxMixin:
19021934
@classproperty
Lines changed: 1 addition & 0 deletions
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)