Skip to content

Commit fb1e950

Browse files
gh-91456: [Enum] Deprecate default auto() behavior with mixed value types (GH-91457)
When used with plain Enum, auto() returns the last numeric value assigned, skipping any incompatible member values (such as strings); starting in 3.13 the default auto() for plain Enums will require all the values to be of compatible types, and will return a new value that is 1 higher than any existing value. Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
1 parent 7c439dc commit fb1e950

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

Doc/library/enum.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ Utilities and Decorators
761761
``_generate_next_value_`` can be overridden to customize the values used by
762762
*auto*.
763763

764+
.. note:: in 3.13 the default ``"generate_next_value_`` will always return
765+
the highest member value incremented by 1, and will fail if any
766+
member is an incompatible type.
767+
764768
.. decorator:: property
765769

766770
A decorator similar to the built-in *property*, but specifically for

Lib/enum.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,21 +1205,39 @@ def __new__(cls, value):
12051205
def __init__(self, *args, **kwds):
12061206
pass
12071207

1208-
def _generate_next_value_(name, start, count, last_values):
1208+
def _generate_next_value_(name, start, count, last_value):
12091209
"""
12101210
Generate the next value when not given.
12111211
12121212
name: the name of the member
12131213
start: the initial start value or None
12141214
count: the number of existing members
1215-
last_value: the last value assigned or None
1215+
last_value: the list of values assigned
12161216
"""
1217-
for last_value in reversed(last_values):
1218-
try:
1219-
return last_value + 1
1220-
except TypeError:
1221-
pass
1222-
else:
1217+
if not last_value:
1218+
return start
1219+
try:
1220+
last = last_value[-1]
1221+
last_value.sort()
1222+
if last == last_value[-1]:
1223+
# no difference between old and new methods
1224+
return last + 1
1225+
else:
1226+
# trigger old method (with warning)
1227+
raise TypeError
1228+
except TypeError:
1229+
import warnings
1230+
warnings.warn(
1231+
"In 3.13 the default `auto()`/`_generate_next_value_` will require all values to be sortable and support adding +1\n"
1232+
"and the value returned will be the largest value in the enum incremented by 1",
1233+
DeprecationWarning,
1234+
stacklevel=3,
1235+
)
1236+
for v in last_value:
1237+
try:
1238+
return v + 1
1239+
except TypeError:
1240+
pass
12231241
return start
12241242

12251243
@classmethod

Lib/test/test_enum.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3953,23 +3953,54 @@ class Color(AutoNameEnum):
39533953
self.assertEqual(Color.blue.value, 'blue')
39543954
self.assertEqual(Color.green.value, 'green')
39553955

3956-
def test_auto_garbage(self):
3957-
class Color(Enum):
3958-
red = 'red'
3959-
blue = auto()
3956+
@unittest.skipIf(
3957+
python_version >= (3, 13),
3958+
'mixed types with auto() no longer supported',
3959+
)
3960+
def test_auto_garbage_ok(self):
3961+
with self.assertWarnsRegex(DeprecationWarning, 'will require all values to be sortable'):
3962+
class Color(Enum):
3963+
red = 'red'
3964+
blue = auto()
39603965
self.assertEqual(Color.blue.value, 1)
39613966

3962-
def test_auto_garbage_corrected(self):
3963-
class Color(Enum):
3964-
red = 'red'
3965-
blue = 2
3966-
green = auto()
3967+
@unittest.skipIf(
3968+
python_version >= (3, 13),
3969+
'mixed types with auto() no longer supported',
3970+
)
3971+
def test_auto_garbage_corrected_ok(self):
3972+
with self.assertWarnsRegex(DeprecationWarning, 'will require all values to be sortable'):
3973+
class Color(Enum):
3974+
red = 'red'
3975+
blue = 2
3976+
green = auto()
39673977

39683978
self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
39693979
self.assertEqual(Color.red.value, 'red')
39703980
self.assertEqual(Color.blue.value, 2)
39713981
self.assertEqual(Color.green.value, 3)
39723982

3983+
@unittest.skipIf(
3984+
python_version < (3, 13),
3985+
'mixed types with auto() will raise in 3.13',
3986+
)
3987+
def test_auto_garbage_fail(self):
3988+
with self.assertRaisesRegex(TypeError, 'will require all values to be sortable'):
3989+
class Color(Enum):
3990+
red = 'red'
3991+
blue = auto()
3992+
3993+
@unittest.skipIf(
3994+
python_version < (3, 13),
3995+
'mixed types with auto() will raise in 3.13',
3996+
)
3997+
def test_auto_garbage_corrected_fail(self):
3998+
with self.assertRaisesRegex(TypeError, 'will require all values to be sortable'):
3999+
class Color(Enum):
4000+
red = 'red'
4001+
blue = 2
4002+
green = auto()
4003+
39734004
def test_auto_order(self):
39744005
with self.assertRaises(TypeError):
39754006
class Color(Enum):
@@ -3991,6 +4022,22 @@ def _generate_next_value_(name, start, count, last):
39914022
self.assertEqual(Color.red.value, 'pathological case')
39924023
self.assertEqual(Color.blue.value, 'blue')
39934024

4025+
@unittest.skipIf(
4026+
python_version < (3, 13),
4027+
'auto() will return highest value + 1 in 3.13',
4028+
)
4029+
def test_auto_with_aliases(self):
4030+
class Color(Enum):
4031+
red = auto()
4032+
blue = auto()
4033+
oxford = blue
4034+
crimson = red
4035+
green = auto()
4036+
self.assertIs(Color.crimson, Color.red)
4037+
self.assertIs(Color.oxford, Color.blue)
4038+
self.assertIsNot(Color.green, Color.red)
4039+
self.assertIsNot(Color.green, Color.blue)
4040+
39944041
def test_duplicate_auto(self):
39954042
class Dupes(Enum):
39964043
first = primero = auto()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deprecate current default auto() behavior: In 3.13 the default will be for
2+
for auto() to always return the largest member value incremented by
3+
1, and to raise if incompatible value types are used.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy