changeset: 102805:10830e1b2966 user: Ethan Furman date: Sat Aug 20 07:19:31 2016 -0700 files: Doc/library/enum.rst Lib/enum.py Lib/test/test_enum.py Misc/NEWS description: issue26981: add _order_ compatibility shim to enum.Enum diff -r 90bb32c9921c -r 10830e1b2966 Doc/library/enum.rst --- a/Doc/library/enum.rst Sat Aug 20 08:27:06 2016 +0000 +++ b/Doc/library/enum.rst Sat Aug 20 07:19:31 2016 -0700 @@ -257,7 +257,7 @@ >>> Color.red < Color.blue Traceback (most recent call last): File "", line 1, in - TypeError: unorderable types: Color() < Color() + TypeError: '<' not supported between instances of 'Color' and 'Color' Equality comparisons are defined though:: @@ -776,3 +776,22 @@ 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 -r 90bb32c9921c -r 10830e1b2966 Lib/enum.py --- a/Lib/enum.py Sat Aug 20 08:27:06 2016 +0000 +++ b/Lib/enum.py Sat Aug 20 07:19:31 2016 -0700 @@ -64,9 +64,11 @@ """ 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 @@ 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 @@ 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 -r 90bb32c9921c -r 10830e1b2966 Lib/test/test_enum.py --- a/Lib/test/test_enum.py Sat Aug 20 08:27:06 2016 +0000 +++ b/Lib/test/test_enum.py Sat Aug 20 07:19:31 2016 -0700 @@ -1571,6 +1571,68 @@ 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 -r 90bb32c9921c -r 10830e1b2966 Misc/NEWS --- a/Misc/NEWS Sat Aug 20 08:27:06 2016 +0000 +++ b/Misc/NEWS Sat Aug 20 07:19:31 2016 -0700 @@ -137,6 +137,9 @@ - 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