diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2020-09-22 00:23:13 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-22 00:23:13 (GMT) |
commit | 0063ff4e583505e69473caa978e476ea4c559b83 (patch) | |
tree | 637c86f3b68e8d63678fb666c5cbf4e3326783c6 /Lib | |
parent | 68526fe258da8c01196fd7cf48e8e5f1280bf8fd (diff) | |
download | cpython-0063ff4e583505e69473caa978e476ea4c559b83.zip cpython-0063ff4e583505e69473caa978e476ea4c559b83.tar.gz cpython-0063ff4e583505e69473caa978e476ea4c559b83.tar.bz2 |
bpo-41816: add `StrEnum` (GH-22337)
`StrEnum` ensures that its members were already strings, or intended to
be strings.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/enum.py | 32 | ||||
-rw-r--r-- | Lib/test/test_enum.py | 54 |
2 files changed, 64 insertions, 22 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index e8603a4..589b17f 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -4,7 +4,7 @@ from types import MappingProxyType, DynamicClassAttribute __all__ = [ 'EnumMeta', - 'Enum', 'IntEnum', 'Flag', 'IntFlag', + 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'auto', 'unique', ] @@ -688,7 +688,35 @@ class Enum(metaclass=EnumMeta): class IntEnum(int, Enum): - """Enum where members are also (and must be) ints""" + """ + Enum where members are also (and must be) ints + """ + + +class StrEnum(str, Enum): + """ + Enum where members are also (and must be) strings + """ + + def __new__(cls, *values): + if len(values) > 3: + raise TypeError('too many arguments for str(): %r' % (values, )) + if len(values) == 1: + # it must be a string + if not isinstance(values[0], str): + raise TypeError('%r is not a string' % (values[0], )) + if len(values) > 1: + # check that encoding argument is a string + if not isinstance(values[1], str): + raise TypeError('encoding must be a string, not %r' % (values[1], )) + if len(values) > 2: + # check that errors argument is a string + if not isinstance(values[2], str): + raise TypeError('errors must be a string, not %r' % (values[2], )) + value = str(*values) + member = str.__new__(cls, value) + member._value_ = value + return member def _reduce_ex_by_name(self, proto): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 3f39073..8e84d92 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -5,7 +5,7 @@ import sys import unittest import threading from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto +from enum import Enum, IntEnum, StrEnum, EnumMeta, Flag, IntFlag, unique, auto from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -48,14 +48,9 @@ except Exception as exc: FlagStooges = exc # for pickle test and subclass tests -try: - class StrEnum(str, Enum): - 'accepts only string values' - class Name(StrEnum): - BDFL = 'Guido van Rossum' - FLUFL = 'Barry Warsaw' -except Exception as exc: - Name = exc +class Name(StrEnum): + BDFL = 'Guido van Rossum' + FLUFL = 'Barry Warsaw' try: Question = Enum('Question', 'who what when where why', module=__name__) @@ -665,14 +660,13 @@ class TestEnum(unittest.TestCase): tau = 'Tau' self.assertTrue(phy.pi < phy.tau) - def test_strenum_inherited(self): - class StrEnum(str, Enum): - pass + def test_strenum_inherited_methods(self): class phy(StrEnum): pi = 'Pi' tau = 'Tau' self.assertTrue(phy.pi < phy.tau) - + self.assertEqual(phy.pi.upper(), 'PI') + self.assertEqual(phy.tau.count('a'), 1) def test_intenum(self): class WeekDay(IntEnum): @@ -2014,13 +2008,6 @@ class TestEnum(unittest.TestCase): self.assertTrue(issubclass(ReformedColor, int)) def test_multiple_inherited_mixin(self): - class StrEnum(str, Enum): - def __new__(cls, *args, **kwargs): - for a in args: - if not isinstance(a, str): - raise TypeError("Enumeration '%s' (%s) is not" - " a string" % (a, type(a).__name__)) - return str.__new__(cls, *args, **kwargs) @unique class Decision1(StrEnum): REVERT = "REVERT" @@ -2043,6 +2030,33 @@ class TestEnum(unittest.TestCase): local_ls = {} exec(code, global_ns, local_ls) + def test_strenum(self): + class GoodStrEnum(StrEnum): + one = '1' + two = '2' + three = b'3', 'ascii' + four = b'4', 'latin1', 'strict' + with self.assertRaisesRegex(TypeError, '1 is not a string'): + class FirstFailedStrEnum(StrEnum): + one = 1 + two = '2' + with self.assertRaisesRegex(TypeError, "2 is not a string"): + class SecondFailedStrEnum(StrEnum): + one = '1' + two = 2, + three = '3' + with self.assertRaisesRegex(TypeError, '2 is not a string'): + class ThirdFailedStrEnum(StrEnum): + one = '1' + two = 2 + with self.assertRaisesRegex(TypeError, 'encoding must be a string, not %r' % (sys.getdefaultencoding, )): + class ThirdFailedStrEnum(StrEnum): + one = '1' + two = b'2', sys.getdefaultencoding + with self.assertRaisesRegex(TypeError, 'errors must be a string, not 9'): + class ThirdFailedStrEnum(StrEnum): + one = '1' + two = b'2', 'ascii', 9 class TestOrder(unittest.TestCase): |