bpo-34536: raise error for invalid _missing_ results (GH-9147)

* raise exception if _missing_ returns None or invalid type
This commit is contained in:
Ethan Furman 2018-09-12 11:43:34 -07:00 committed by GitHub
parent a5d1eb8d8b
commit 019f0a0cb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 1 deletions

View file

@ -585,7 +585,25 @@ class Enum(metaclass=EnumMeta):
if member._value_ == value: if member._value_ == value:
return member return member
# still not found -- try _missing_ hook # 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): def _generate_next_value_(name, start, count, last_values):
for last_value in reversed(last_values): for last_value in reversed(last_values):

View file

@ -3,6 +3,7 @@ import inspect
import pydoc import pydoc
import sys import sys
import unittest import unittest
import sys
import threading import threading
from collections import OrderedDict from collections import OrderedDict
from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto
@ -1697,6 +1698,38 @@ class TestEnum(unittest.TestCase):
third = auto() third = auto()
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) 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): class TestOrder(unittest.TestCase):

View file

@ -0,0 +1,2 @@
`Enum._missing_`: raise `ValueError` if None returned and `TypeError` if
non-member is returned.