From 019f0a0cb85ebc234356415f3638b9bd77528e55 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Wed, 12 Sep 2018 11:43:34 -0700 Subject: bpo-34536: raise error for invalid _missing_ results (GH-9147) * raise exception if _missing_ returns None or invalid type --- Lib/enum.py | 20 ++++++++++++- Lib/test/test_enum.py | 33 ++++++++++++++++++++++ .../2018-09-11-15-49-09.bpo-34536.3IPIH5.rst | 2 ++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2018-09-11-15-49-09.bpo-34536.3IPIH5.rst diff --git a/Lib/enum.py b/Lib/enum.py index 0839671..02405c8 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -585,7 +585,25 @@ class Enum(metaclass=EnumMeta): if member._value_ == value: return member # still not found -- try _missing_ hook - return cls._missing_(value) + try: + exc = None + result = cls._missing_(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.__name__)) + 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 def _generate_next_value_(name, start, count, last_values): for last_value in reversed(last_values): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index c04d03f..b8efb83 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,6 +3,7 @@ import inspect import pydoc import sys import unittest +import sys import threading from collections import OrderedDict from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto @@ -1697,6 +1698,38 @@ class TestEnum(unittest.TestCase): third = auto() self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + def test_missing(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + @classmethod + def _missing_(cls, item): + if item == 'three': + return cls.blue + elif item == 'bad return': + # trigger internal error + return 5 + elif item == 'error out': + raise ZeroDivisionError + else: + # trigger not found + return None + self.assertIs(Color('three'), Color.blue) + self.assertRaises(ValueError, Color, 7) + try: + Color('bad return') + except TypeError as exc: + self.assertTrue(isinstance(exc.__context__, ValueError)) + else: + raise Exception('Exception not raised.') + try: + Color('error out') + except ZeroDivisionError as exc: + self.assertTrue(isinstance(exc.__context__, ValueError)) + else: + raise Exception('Exception not raised.') + class TestOrder(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-09-11-15-49-09.bpo-34536.3IPIH5.rst b/Misc/NEWS.d/next/Library/2018-09-11-15-49-09.bpo-34536.3IPIH5.rst new file mode 100644 index 0000000..be45eb5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-11-15-49-09.bpo-34536.3IPIH5.rst @@ -0,0 +1,2 @@ +`Enum._missing_`: raise `ValueError` if None returned and `TypeError` if +non-member is returned. -- cgit v0.12