summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2016-08-20 14:19:31 (GMT)
committerEthan Furman <ethan@stoneleaf.us>2016-08-20 14:19:31 (GMT)
commite8e61277ff31d7b5ae87ca21808c4a6fbdab4954 (patch)
treef44a362609960627ade0521c9b8de0c1dfbc2693
parent3e458755782e8f63dd9eb29cb134c2b65657d17f (diff)
downloadcpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.zip
cpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.tar.gz
cpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.tar.bz2
issue26981: add _order_ compatibility shim to enum.Enum
-rw-r--r--Doc/library/enum.rst21
-rw-r--r--Lib/enum.py17
-rw-r--r--Lib/test/test_enum.py62
-rw-r--r--Misc/NEWS3
4 files changed, 100 insertions, 3 deletions
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 2111d1c..827bab0 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -257,7 +257,7 @@ members are not integers (but see `IntEnum`_ below)::
>>> Color.red < Color.blue
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- TypeError: unorderable types: Color() < Color()
+ TypeError: '<' not supported between instances of 'Color' and 'Color'
Equality comparisons are defined though::
@@ -776,3 +776,22 @@ appropriately.
If you wish to change how :class:`Enum` members are looked up you should either
write a helper function or a :func:`classmethod` for the :class:`Enum`
subclass.
+
+To help keep Python 2 / Python 3 code in sync a user-specified :attr:`_order_`,
+if provided, will be checked to ensure the actual order of the enumeration
+matches::
+
+ >>> class Color(Enum):
+ ... _order_ = 'red green blue'
+ ... red = 1
+ ... blue = 3
+ ... green = 2
+ ...
+ Traceback (most recent call last):
+ ...
+ TypeError: member order does not match _order_
+
+.. note::
+
+ In Python 2 code the :attr:`_order_` attribute is necessary as definition
+ order is lost during class creation.
diff --git a/Lib/enum.py b/Lib/enum.py
index 99db9e6..e7889a8 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -64,9 +64,11 @@ class _EnumDict(dict):
"""
if _is_sunder(key):
- raise ValueError('_names_ are reserved for future Enum use')
+ if key not in ('_order_', ):
+ raise ValueError('_names_ are reserved for future Enum use')
elif _is_dunder(key):
- pass
+ if key == '__order__':
+ key = '_order_'
elif key in self._member_names:
# descriptor overwriting an enum?
raise TypeError('Attempted to reuse key: %r' % key)
@@ -106,6 +108,9 @@ class EnumMeta(type):
for name in classdict._member_names:
del classdict[name]
+ # adjust the sunders
+ _order_ = classdict.pop('_order_', None)
+
# check for illegal enum names (any others?)
invalid_names = set(members) & {'mro', }
if invalid_names:
@@ -210,6 +215,14 @@ class EnumMeta(type):
if save_new:
enum_class.__new_member__ = __new__
enum_class.__new__ = Enum.__new__
+
+ # py3 support for definition order (helps keep py2/py3 code in sync)
+ if _order_ is not None:
+ if isinstance(_order_, str):
+ _order_ = _order_.replace(',', ' ').split()
+ if _order_ != enum_class._member_names_:
+ raise TypeError('member order does not match _order_')
+
return enum_class
def __bool__(self):
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 564c0e9..2d4519e 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -1571,6 +1571,68 @@ class TestEnum(unittest.TestCase):
self.assertEqual(LabelledList(1), LabelledList.unprocessed)
+class TestOrder(unittest.TestCase):
+
+ def test_same_members(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_same_members_with_aliases(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_same_members_wrong_order(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ blue = 3
+ green = 2
+
+ def test_order_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_order_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_enum_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+
+ def test_enum_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+ verde = green
+
+
class TestUnique(unittest.TestCase):
def test_unique_clean(self):
diff --git a/Misc/NEWS b/Misc/NEWS
index 292519f..6c871cc 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -137,6 +137,9 @@ Library
- Issue #26800: Undocumented support of general bytes-like objects
as paths in os functions is now deprecated.
+- Issue #26981: Add _order_ compatibility ship to enum.Enum for
+ Python 2/3 code bases.
+
- Issue #27661: Added tzinfo keyword argument to datetime.combine.
- In the curses module, raise an error if window.getstr() or window.instr() is