mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
bpo-42248: [Enum] ensure exceptions raised in `_missing_` are released (GH-25350)
This commit is contained in:
parent
67c0b3d89c
commit
8c14f5a787
3 changed files with 57 additions and 19 deletions
43
Lib/enum.py
43
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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
[Enum] ensure exceptions raised in ``_missing__`` are released
|
||||
Loading…
Add table
Add a link
Reference in a new issue