summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2020-09-22 00:23:13 (GMT)
committerGitHub <noreply@github.com>2020-09-22 00:23:13 (GMT)
commit0063ff4e583505e69473caa978e476ea4c559b83 (patch)
tree637c86f3b68e8d63678fb666c5cbf4e3326783c6 /Lib
parent68526fe258da8c01196fd7cf48e8e5f1280bf8fd (diff)
downloadcpython-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.py32
-rw-r--r--Lib/test/test_enum.py54
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):