summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2021-04-12 15:51:20 (GMT)
committerGitHub <noreply@github.com>2021-04-12 15:51:20 (GMT)
commit8c14f5a787b21d5a1eae5d5ee981431d1c0e055f (patch)
tree839f0496da66f0459427ef58c83a4af56eae001e
parent67c0b3d89c4da9750fdb43fc66d3924681b22d2e (diff)
downloadcpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.zip
cpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.tar.gz
cpython-8c14f5a787b21d5a1eae5d5ee981431d1c0e055f.tar.bz2
bpo-42248: [Enum] ensure exceptions raised in ``_missing_`` are released (GH-25350)
-rw-r--r--Lib/enum.py43
-rw-r--r--Lib/test/test_enum.py32
-rw-r--r--Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst1
3 files changed, 57 insertions, 19 deletions
diff --git a/Lib/enum.py b/Lib/enum.py
index f31779b..b102a4e 100644
--- a/Lib/enum.py
+++ b/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):
"""
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 6002cd8..5af7cd2 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -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
diff --git a/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst b/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst
new file mode 100644
index 0000000..0722d35
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-04-11-21-10-57.bpo-42248.pedB1E.rst
@@ -0,0 +1 @@
+[Enum] ensure exceptions raised in ``_missing__`` are released