Skip to content

Commit 7d2f88e

Browse files
[3.12] gh-114071: [Enum] update docs and code for tuples/subclasses (GH-114871) (GH-114993)
Update documentation with `__new__` and `__init__` entries. Support use of `auto()` in tuple subclasses on member assignment lines. Previously, auto() was only supported on the member definition line either solo or as part of a tuple: RED = auto() BLUE = auto(), 'azul' However, since Python itself supports using tuple subclasses where tuples are expected, e.g.: from collections import namedtuple T = namedtuple('T', 'first second third') def test(one, two, three): print(one, two, three) test(*T(4, 5, 6)) GH- 4 5 6 it made sense to also support tuple subclasses in enum definitions. (cherry picked from commit ff7588b) Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
1 parent dc01c84 commit 7d2f88e

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

Doc/library/enum.rst

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,17 @@ Data Types
323323
>>> PowersOfThree.SECOND.value
324324
9
325325

326+
.. method:: Enum.__init__(self, \*args, \**kwds)
327+
328+
By default, does nothing. If multiple values are given in the member
329+
assignment, those values become separate arguments to ``__init__``; e.g.
330+
331+
>>> from enum import Enum
332+
>>> class Weekday(Enum):
333+
... MONDAY = 1, 'Mon'
334+
335+
``Weekday.__init__()`` would be called as ``Weekday.__init__(self, 1, 'Mon')``
336+
326337
.. method:: Enum.__init_subclass__(cls, \**kwds)
327338

328339
A *classmethod* that is used to further configure subsequent subclasses.
@@ -350,6 +361,18 @@ Data Types
350361
>>> Build('deBUG')
351362
<Build.DEBUG: 'debug'>
352363

364+
.. method:: Enum.__new__(cls, \*args, \**kwds)
365+
366+
By default, doesn't exist. If specified, either in the enum class
367+
definition or in a mixin class (such as ``int``), all values given
368+
in the member assignment will be passed; e.g.
369+
370+
>>> from enum import Enum
371+
>>> class MyIntEnum(Enum):
372+
... SEVENTEEN = '1a', 16
373+
374+
results in the call ``int('1a', 16)`` and a value of ``17`` for the member.
375+
353376
.. method:: Enum.__repr__(self)
354377

355378
Returns the string used for *repr()* calls. By default, returns the
@@ -463,9 +486,9 @@ Data Types
463486

464487
.. class:: Flag
465488

466-
*Flag* members support the bitwise operators ``&`` (*AND*), ``|`` (*OR*),
467-
``^`` (*XOR*), and ``~`` (*INVERT*); the results of those operators are members
468-
of the enumeration.
489+
``Flag`` is the same as :class:`Enum`, but its members support the bitwise
490+
operators ``&`` (*AND*), ``|`` (*OR*), ``^`` (*XOR*), and ``~`` (*INVERT*);
491+
the results of those operators are members of the enumeration.
469492

470493
.. method:: __contains__(self, value)
471494

Lib/enum.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,11 @@ def __setitem__(self, key, value):
457457
if isinstance(value, auto):
458458
single = True
459459
value = (value, )
460-
if type(value) is tuple and any(isinstance(v, auto) for v in value):
460+
if isinstance(value, tuple) and any(isinstance(v, auto) for v in value):
461461
# insist on an actual tuple, no subclasses, in keeping with only supporting
462462
# top-level auto() usage (not contained in any other data structure)
463463
auto_valued = []
464+
t = type(value)
464465
for v in value:
465466
if isinstance(v, auto):
466467
non_auto_store = False
@@ -475,7 +476,12 @@ def __setitem__(self, key, value):
475476
if single:
476477
value = auto_valued[0]
477478
else:
478-
value = tuple(auto_valued)
479+
try:
480+
# accepts iterable as multiple arguments?
481+
value = t(auto_valued)
482+
except TypeError:
483+
# then pass them in singlely
484+
value = t(*auto_valued)
479485
self._member_names[key] = None
480486
if non_auto_store:
481487
self._last_values.append(value)

Lib/test/test_enum.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,40 @@ class SomeTuple(tuple, Enum):
23082308
globals()['SomeTuple'] = SomeTuple
23092309
test_pickle_dump_load(self.assertIs, SomeTuple.first)
23102310

2311+
def test_tuple_subclass_with_auto_1(self):
2312+
from collections import namedtuple
2313+
T = namedtuple('T', 'index desc')
2314+
class SomeEnum(T, Enum):
2315+
__qualname__ = 'SomeEnum' # needed for pickle protocol 4
2316+
first = auto(), 'for the money'
2317+
second = auto(), 'for the show'
2318+
third = auto(), 'for the music'
2319+
self.assertIs(type(SomeEnum.first), SomeEnum)
2320+
self.assertEqual(SomeEnum.third.value, (3, 'for the music'))
2321+
self.assertIsInstance(SomeEnum.third.value, T)
2322+
self.assertEqual(SomeEnum.first.index, 1)
2323+
self.assertEqual(SomeEnum.second.desc, 'for the show')
2324+
globals()['SomeEnum'] = SomeEnum
2325+
globals()['T'] = T
2326+
test_pickle_dump_load(self.assertIs, SomeEnum.first)
2327+
2328+
def test_tuple_subclass_with_auto_2(self):
2329+
from collections import namedtuple
2330+
T = namedtuple('T', 'index desc')
2331+
class SomeEnum(Enum):
2332+
__qualname__ = 'SomeEnum' # needed for pickle protocol 4
2333+
first = T(auto(), 'for the money')
2334+
second = T(auto(), 'for the show')
2335+
third = T(auto(), 'for the music')
2336+
self.assertIs(type(SomeEnum.first), SomeEnum)
2337+
self.assertEqual(SomeEnum.third.value, (3, 'for the music'))
2338+
self.assertIsInstance(SomeEnum.third.value, T)
2339+
self.assertEqual(SomeEnum.first.value.index, 1)
2340+
self.assertEqual(SomeEnum.second.value.desc, 'for the show')
2341+
globals()['SomeEnum'] = SomeEnum
2342+
globals()['T'] = T
2343+
test_pickle_dump_load(self.assertIs, SomeEnum.first)
2344+
23112345
def test_duplicate_values_give_unique_enum_items(self):
23122346
class AutoNumber(Enum):
23132347
first = ()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support tuple subclasses using auto() for enum member value.

0 commit comments

Comments
 (0)