summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_enum.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-08-31 22:04:55 (GMT)
committerGitHub <noreply@github.com>2023-08-31 22:04:55 (GMT)
commit8c3793a539ce950e552da390b7b24485dd655237 (patch)
treef270c1cc50d17f694166ac1b6af27c2c3006091d /Lib/test/test_enum.py
parenta92c60c99bab716d1d08bccb92dcc0fe6ee93abe (diff)
downloadcpython-8c3793a539ce950e552da390b7b24485dd655237.zip
cpython-8c3793a539ce950e552da390b7b24485dd655237.tar.gz
cpython-8c3793a539ce950e552da390b7b24485dd655237.tar.bz2
[3.12] gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ (GH-108704) (#108733)
gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ (GH-108704) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b2f1e28dd3c1a35721939f400a8ab619b8) Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
Diffstat (limited to 'Lib/test/test_enum.py')
-rw-r--r--Lib/test/test_enum.py284
1 files changed, 229 insertions, 55 deletions
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index dbdc639..dc60cac 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -236,11 +236,82 @@ class _EnumTests:
values = None
def setUp(self):
- class BaseEnum(self.enum_type):
+ if self.__class__.__name__[-5:] == 'Class':
+ class BaseEnum(self.enum_type):
+ @enum.property
+ def first(self):
+ return '%s is first!' % self.name
+ class MainEnum(BaseEnum):
+ first = auto()
+ second = auto()
+ third = auto()
+ if issubclass(self.enum_type, Flag):
+ dupe = 3
+ else:
+ dupe = third
+ self.MainEnum = MainEnum
+ #
+ class NewStrEnum(self.enum_type):
+ def __str__(self):
+ return self.name.upper()
+ first = auto()
+ self.NewStrEnum = NewStrEnum
+ #
+ class NewFormatEnum(self.enum_type):
+ def __format__(self, spec):
+ return self.name.upper()
+ first = auto()
+ self.NewFormatEnum = NewFormatEnum
+ #
+ class NewStrFormatEnum(self.enum_type):
+ def __str__(self):
+ return self.name.title()
+ def __format__(self, spec):
+ return ''.join(reversed(self.name))
+ first = auto()
+ self.NewStrFormatEnum = NewStrFormatEnum
+ #
+ class NewBaseEnum(self.enum_type):
+ def __str__(self):
+ return self.name.title()
+ def __format__(self, spec):
+ return ''.join(reversed(self.name))
+ class NewSubEnum(NewBaseEnum):
+ first = auto()
+ self.NewSubEnum = NewSubEnum
+ #
+ class LazyGNV(self.enum_type):
+ def _generate_next_value_(name, start, last, values):
+ pass
+ self.LazyGNV = LazyGNV
+ #
+ class BusyGNV(self.enum_type):
+ @staticmethod
+ def _generate_next_value_(name, start, last, values):
+ pass
+ self.BusyGNV = BusyGNV
+ #
+ self.is_flag = False
+ self.names = ['first', 'second', 'third']
+ if issubclass(MainEnum, StrEnum):
+ self.values = self.names
+ elif MainEnum._member_type_ is str:
+ self.values = ['1', '2', '3']
+ elif issubclass(self.enum_type, Flag):
+ self.values = [1, 2, 4]
+ self.is_flag = True
+ self.dupe2 = MainEnum(5)
+ else:
+ self.values = self.values or [1, 2, 3]
+ #
+ if not getattr(self, 'source_values', False):
+ self.source_values = self.values
+ elif self.__class__.__name__[-8:] == 'Function':
@enum.property
def first(self):
return '%s is first!' % self.name
- class MainEnum(BaseEnum):
+ BaseEnum = self.enum_type('BaseEnum', {'first':first})
+ #
first = auto()
second = auto()
third = auto()
@@ -248,63 +319,60 @@ class _EnumTests:
dupe = 3
else:
dupe = third
- self.MainEnum = MainEnum
- #
- class NewStrEnum(self.enum_type):
+ self.MainEnum = MainEnum = BaseEnum('MainEnum', dict(first=first, second=second, third=third, dupe=dupe))
+ #
def __str__(self):
return self.name.upper()
first = auto()
- self.NewStrEnum = NewStrEnum
- #
- class NewFormatEnum(self.enum_type):
+ self.NewStrEnum = self.enum_type('NewStrEnum', (('first',first),('__str__',__str__)))
+ #
def __format__(self, spec):
return self.name.upper()
first = auto()
- self.NewFormatEnum = NewFormatEnum
- #
- class NewStrFormatEnum(self.enum_type):
+ self.NewFormatEnum = self.enum_type('NewFormatEnum', [('first',first),('__format__',__format__)])
+ #
def __str__(self):
return self.name.title()
def __format__(self, spec):
return ''.join(reversed(self.name))
first = auto()
- self.NewStrFormatEnum = NewStrFormatEnum
- #
- class NewBaseEnum(self.enum_type):
+ self.NewStrFormatEnum = self.enum_type('NewStrFormatEnum', dict(first=first, __format__=__format__, __str__=__str__))
+ #
def __str__(self):
return self.name.title()
def __format__(self, spec):
return ''.join(reversed(self.name))
- class NewSubEnum(NewBaseEnum):
- first = auto()
- self.NewSubEnum = NewSubEnum
- #
- class LazyGNV(self.enum_type):
+ NewBaseEnum = self.enum_type('NewBaseEnum', dict(__format__=__format__, __str__=__str__))
+ class NewSubEnum(NewBaseEnum):
+ first = auto()
+ self.NewSubEnum = NewBaseEnum('NewSubEnum', 'first')
+ #
def _generate_next_value_(name, start, last, values):
pass
- self.LazyGNV = LazyGNV
- #
- class BusyGNV(self.enum_type):
+ self.LazyGNV = self.enum_type('LazyGNV', {'_generate_next_value_':_generate_next_value_})
+ #
@staticmethod
def _generate_next_value_(name, start, last, values):
pass
- self.BusyGNV = BusyGNV
- #
- self.is_flag = False
- self.names = ['first', 'second', 'third']
- if issubclass(MainEnum, StrEnum):
- self.values = self.names
- elif MainEnum._member_type_ is str:
- self.values = ['1', '2', '3']
- elif issubclass(self.enum_type, Flag):
- self.values = [1, 2, 4]
- self.is_flag = True
- self.dupe2 = MainEnum(5)
+ self.BusyGNV = self.enum_type('BusyGNV', {'_generate_next_value_':_generate_next_value_})
+ #
+ self.is_flag = False
+ self.names = ['first', 'second', 'third']
+ if issubclass(MainEnum, StrEnum):
+ self.values = self.names
+ elif MainEnum._member_type_ is str:
+ self.values = ['1', '2', '3']
+ elif issubclass(self.enum_type, Flag):
+ self.values = [1, 2, 4]
+ self.is_flag = True
+ self.dupe2 = MainEnum(5)
+ else:
+ self.values = self.values or [1, 2, 3]
+ #
+ if not getattr(self, 'source_values', False):
+ self.source_values = self.values
else:
- self.values = self.values or [1, 2, 3]
- #
- if not getattr(self, 'source_values', False):
- self.source_values = self.values
+ raise ValueError('unknown enum style: %r' % self.__class__.__name__)
def assertFormatIsValue(self, spec, member):
self.assertEqual(spec.format(member), spec.format(member.value))
@@ -332,6 +400,17 @@ class _EnumTests:
with self.assertRaises(AttributeError):
del Season.SPRING.name
+ def test_bad_new_super(self):
+ with self.assertRaisesRegex(
+ TypeError,
+ 'has no members defined',
+ ):
+ class BadSuper(self.enum_type):
+ def __new__(cls, value):
+ obj = super().__new__(cls, value)
+ return obj
+ failed = 1
+
def test_basics(self):
TE = self.MainEnum
if self.is_flag:
@@ -387,7 +466,7 @@ class _EnumTests:
MainEnum = self.MainEnum
self.assertIn(MainEnum.first, MainEnum)
self.assertTrue(self.values[0] in MainEnum)
- if type(self) is not TestStrEnum:
+ if type(self) not in (TestStrEnumClass, TestStrEnumFunction):
self.assertFalse('first' in MainEnum)
val = MainEnum.dupe
self.assertIn(val, MainEnum)
@@ -909,15 +988,23 @@ class _FlagTests:
self.assertTrue(~OpenXYZ(0), (X|Y|Z))
-class TestPlainEnum(_EnumTests, _PlainOutputTests, unittest.TestCase):
+class TestPlainEnumClass(_EnumTests, _PlainOutputTests, unittest.TestCase):
+ enum_type = Enum
+
+
+class TestPlainEnumFunction(_EnumTests, _PlainOutputTests, unittest.TestCase):
enum_type = Enum
-class TestPlainFlag(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase):
+class TestPlainFlagClass(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase):
enum_type = Flag
-class TestIntEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+class TestPlainFlagFunction(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase):
+ enum_type = Flag
+
+
+class TestIntEnumClass(_EnumTests, _MinimalOutputTests, unittest.TestCase):
enum_type = IntEnum
#
def test_shadowed_attr(self):
@@ -929,7 +1016,17 @@ class TestIntEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
self.assertIs(Number.numerator.divisor, Number.divisor)
-class TestStrEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+class TestIntEnumFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ enum_type = IntEnum
+ #
+ def test_shadowed_attr(self):
+ Number = IntEnum('Number', ('divisor', 'numerator'))
+ #
+ self.assertEqual(Number.divisor.numerator, 1)
+ self.assertIs(Number.numerator.divisor, Number.divisor)
+
+
+class TestStrEnumClass(_EnumTests, _MinimalOutputTests, unittest.TestCase):
enum_type = StrEnum
#
def test_shadowed_attr(self):
@@ -942,64 +1039,141 @@ class TestStrEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
self.assertIs(Book.title.author, Book.author)
-class TestIntFlag(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase):
+class TestStrEnumFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ enum_type = StrEnum
+ #
+ def test_shadowed_attr(self):
+ Book = StrEnum('Book', ('author', 'title'))
+ #
+ self.assertEqual(Book.author.title(), 'Author')
+ self.assertEqual(Book.title.title(), 'Title')
+ self.assertIs(Book.title.author, Book.author)
+
+
+class TestIntFlagClass(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase):
+ enum_type = IntFlag
+
+
+class TestIntFlagFunction(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase):
enum_type = IntFlag
-class TestMixedInt(_EnumTests, _MixedOutputTests, unittest.TestCase):
+class TestMixedIntClass(_EnumTests, _MixedOutputTests, unittest.TestCase):
class enum_type(int, Enum): pass
-class TestMixedStr(_EnumTests, _MixedOutputTests, unittest.TestCase):
+class TestMixedIntFunction(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ enum_type = Enum('enum_type', type=int)
+
+
+class TestMixedStrClass(_EnumTests, _MixedOutputTests, unittest.TestCase):
class enum_type(str, Enum): pass
-class TestMixedIntFlag(_EnumTests, _MixedOutputTests, _FlagTests, unittest.TestCase):
+class TestMixedStrFunction(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ enum_type = Enum('enum_type', type=str)
+
+
+class TestMixedIntFlagClass(_EnumTests, _MixedOutputTests, _FlagTests, unittest.TestCase):
class enum_type(int, Flag): pass
-class TestMixedDate(_EnumTests, _MixedOutputTests, unittest.TestCase):
+class TestMixedIntFlagFunction(_EnumTests, _MixedOutputTests, _FlagTests, unittest.TestCase):
+ enum_type = Flag('enum_type', type=int)
+
+class TestMixedDateClass(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ #
values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)]
source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
-
+ #
class enum_type(date, Enum):
+ @staticmethod
def _generate_next_value_(name, start, count, last_values):
values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
return values[count]
-class TestMinimalDate(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+class TestMixedDateFunction(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ #
+ values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)]
+ source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
+ #
+ # staticmethod decorator will be added by EnumType if not present
+ def _generate_next_value_(name, start, count, last_values):
+ values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
+ return values[count]
+ #
+ enum_type = Enum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=date)
+
+class TestMinimalDateClass(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ #
values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)]
source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
-
+ #
class enum_type(date, ReprEnum):
+ # staticmethod decorator will be added by EnumType if absent
def _generate_next_value_(name, start, count, last_values):
values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
return values[count]
-class TestMixedFloat(_EnumTests, _MixedOutputTests, unittest.TestCase):
+class TestMinimalDateFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ #
+ values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)]
+ source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
+ #
+ @staticmethod
+ def _generate_next_value_(name, start, count, last_values):
+ values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
+ return values[count]
+ #
+ enum_type = ReprEnum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=date)
- values = [1.1, 2.2, 3.3]
+class TestMixedFloatClass(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ #
+ values = [1.1, 2.2, 3.3]
+ #
class enum_type(float, Enum):
def _generate_next_value_(name, start, count, last_values):
values = [1.1, 2.2, 3.3]
return values[count]
-class TestMinimalFloat(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+class TestMixedFloatFunction(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ #
+ values = [1.1, 2.2, 3.3]
+ #
+ def _generate_next_value_(name, start, count, last_values):
+ values = [1.1, 2.2, 3.3]
+ return values[count]
+ #
+ enum_type = Enum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=float)
- values = [4.4, 5.5, 6.6]
+class TestMinimalFloatClass(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ #
+ values = [4.4, 5.5, 6.6]
+ #
class enum_type(float, ReprEnum):
def _generate_next_value_(name, start, count, last_values):
values = [4.4, 5.5, 6.6]
return values[count]
+class TestMinimalFloatFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ #
+ values = [4.4, 5.5, 6.6]
+ #
+ def _generate_next_value_(name, start, count, last_values):
+ values = [4.4, 5.5, 6.6]
+ return values[count]
+ #
+ enum_type = ReprEnum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=float)
+
+
class TestSpecial(unittest.TestCase):
"""
various operations that are not attributable to every possible enum