8000 [3.8] bpo-42248: [Enum] ensure exceptions raised in ``_missing_`` are… · python/cpython@f396a1a · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit f396a1a

Browse files
authored
[3.8] bpo-42248: [Enum] ensure exceptions raised in _missing_ are released (GH-25350). (GH-25369)
(cherry picked from commit 8c14f5a) Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
1 parent 04425a9 commit f396a1a

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
@@ -654,19 +654,24 @@ def __new__(cls, value):
654654
except Exception as e:
655655
exc = e
656656
result = None
657-
if isinstance(result, cls):
658-
return result
659-
else:
660-
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__))
661-
if result is None and exc is None:
662-
raise ve_exc
663-
elif exc is None:
664-
exc = TypeError(
665-
'error in %s._missing_: returned %r instead of None or a valid member'
666-
% (cls.__name__, result)
667-
)
668-
exc.__context__ = ve_exc
669-
raise exc
657+
try:
658+
if isinstance(result, cls):
659+
return result
660+
else:
661+
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__))
662+
if result is None and exc is None:
663+
raise ve_exc
664+
elif exc is None:
665+
exc = TypeError(
666+
'error in %s._missing_: returned %r instead of None or a valid member'
667+
% (cls.__name__, result)
668+
)
669+
exc.__context__ = ve_exc
670+
raise exc
671+
finally:
672+
# ensure all variables that could hold an exception are destroyed
673+
exc = None
674+
ve_exc = None
670675

671676
def _generate_next_value_(name, start, count, last_values):
672677
"""

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)
0