diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2016-08-20 14:19:31 (GMT) |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2016-08-20 14:19:31 (GMT) |
commit | e8e61277ff31d7b5ae87ca21808c4a6fbdab4954 (patch) | |
tree | f44a362609960627ade0521c9b8de0c1dfbc2693 | |
parent | 3e458755782e8f63dd9eb29cb134c2b65657d17f (diff) | |
download | cpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.zip cpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.tar.gz cpython-e8e61277ff31d7b5ae87ca21808c4a6fbdab4954.tar.bz2 |
issue26981: add _order_ compatibility shim to enum.Enum
-rw-r--r-- | Doc/library/enum.rst | 21 | ||||
-rw-r--r-- | Lib/enum.py | 17 | ||||
-rw-r--r-- | Lib/test/test_enum.py | 62 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
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): @@ -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 |