Description
What is the issue?
The base _generate_next_value_(name, start, count, last_values)
in class Enum
is not sufficiently rigorous as a default implementation for assigning values with auto()
.
Why is it problematic?
For a user employing auto()
to assign values for their enums, the expectation is that the function will automatically assign unique values to new enums without the user needing to specify those values manually. However, when used following the creation of an alias to a pre-existing enum, the value generated by auto()
is not guaranteed to be unique.
Given the following code:
from enum import Enum, auto
class Example(Enum):
A = auto()
B = auto()
C = A
D = auto()
the expectation is that A, B, D
have been assigned unique values whereas C
is an alias of A
. A printout of each of the Example
enum's values proves that not to be the case though:
>>> print(Example.A)
Example.A
>>> print(Example.B)
Example.B
>>> print(Example.C)
Example.A
>>> print(Example.D) ### UNEXPECTED!
Example.B
effectively rendering D
as an alias to B
rather than its own separate value.
Suggested Cause
Upon closer inspection, the reason seems to be that the base _generate_next_value_
which auto()
relies on is incrementing the "last value that was assigned" rather than the "last NEW value that was assigned"
Lines 1171 to 1186 in a8abb76
Current Workaround
At the moment, to workaround this issue, the user can:
- Ensure that all aliases are only declared after any and all enums assigned with
auto()
- Implement a replacement
_generate_next_value_(name, start, count, last_values)
function as described in the docs
This issue only affects code that combines the use of auto()
with aliases. Although straightforward workarounds do exist (overloading _generate_next_value_
is well described in the docs), it seems unintuitive and unfriendly as the default behavior of auto()
. Even simply sorting last_values
before incrementing may be a sufficient solution to this issue, e.g.
def _generate_next_value_(name, start, count, last_values):
# for last_value in reversed(last_values):
for last_value in sorted(last_values, reverse=True):
try:
return last_value + 1
except TypeError:
pass
else:
return start