Skip to content

Commit

Permalink
[3.9] bpo-42248: [Enum] ensure exceptions raised in _missing_ are…
Browse files Browse the repository at this point in the history
… released (GH-25350). (GH-25370)

(cherry picked from commit 8c14f5a)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
  • Loading branch information
ethanfurman authored Apr 12, 2021
1 parent de06baa commit 6379924
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
31 changes: 18 additions & 13 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,19 +669,24 @@ def __new__(cls, value):
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):
"""
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,38 @@ def _missing_(cls, item):
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Enum] ensure exceptions raised in ``_missing__`` are released

0 comments on commit 6379924

Please sign in to comment.