This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: enum: _generate_next_value_ is not called if its definition occurs after calls to auto()
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.10
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: ethan.furman Nosy List: Ethan Onstott, Jonathan Hsu, ankeshsaha, barry, docs@python, edd07, eli.bendersky, ethan.furman, hongweipeng, miss-islington, thatiparthy
Priority: normal Keywords: easy, patch

Created on 2020-03-20 11:25 by edd07, last changed 2022-04-11 14:59 by admin. This issue is now closed.

File name Uploaded Description Edit
Issue40025.PNG ankeshsaha, 2020-03-30 15:23 Screenshot of the code and output
Pull Requests
URL Status Linked Edit
PR 19098 merged Ethan Onstott, 2020-03-21 05:15
PR 19762 merged miss-islington, 2020-04-28 17:22
PR 19763 merged miss-islington, 2020-04-28 17:22
PR 19904 closed hongweipeng, 2020-05-04 16:37
PR 22284 merged ethan.furman, 2020-09-16 18:20
PR 22285 merged ethan.furman, 2020-09-16 18:33
PR 22287 merged miss-islington, 2020-09-17 00:26
PR 22286 merged miss-islington, 2020-09-17 00:26
Messages (13)
msg364665 - (view) Author: Luis E. (edd07) Date: 2020-03-20 11:25
I ran into this issue when attempting to add a custom _generate_next_value_ method to an existing Enum. Adding the method definition to the bottom of the class causes it to not be called at all:

from enum import Enum, auto

class E(Enum):
	A = auto()
	B = auto()
	def _generate_next_value_(name, *args):
		return name

E.B.value  # Returns 2, E._generate_next_value_ is not called

class F(Enum):
	def _generate_next_value_(name, *args):
		return name
	A = auto()
	B = auto()
F.B.value  # Returns 'B', as intended

I do not believe that the order of method/attribute definition should affect the behavior of the class, or at least it should be mentioned in the documentation.
msg364707 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-03-20 19:09
Immediate solution is to raise an exception if `_generate_next_value_` is defined after members.

Possible future solution is to save all member definitions until after class is defined.

The exception-raising solution would require a check in `_EnumDict` where `_generate_next_value_` is saved -- if any members already exist, raise.
msg364776 - (view) Author: Jonathan Hsu (Jonathan Hsu) * Date: 2020-03-21 21:45
While the current behavior may be initially unexpected, it does match the way that python normally behaves when defining class variables. For example, the following class will throw an exception because the function number_two() is called before it is defined:

class Numbers:
    one = 1
    two = number_two()

    def number_two():
        return 2

# NameError: name 'number_two' is not defined

However, this version is fine:

class Numbers:
    one = 1

    def number_two():
        return 2

    two = number_two()
msg364784 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-03-22 05:20
Jonathan Hsu, you are correct -- and "don't do that" was my initial response; but Enum takes many pains to make sure the user doesn't shoot themselves in the foot, so raising a TypeError is appropriate instead of silently doing the wrong thing.
msg364967 - (view) Author: Jonathan Hsu (Jonathan Hsu) * Date: 2020-03-25 00:55
Thank you for the explanation.
msg365321 - (view) Author: Ankesh Saha (ankeshsaha) * Date: 2020-03-30 15:23

I have ran the code with with _generate_next_value_ method at the bottom of the class and didn't run into any exceptions. Please refer my python file. Please let me know if I am missing something.

from enum import Enum, auto

class E(Enum):
	A = auto()
	B = auto()
	def _generate_next_value_(name, *args):
		return name

for l in (E):
msg367538 - (view) Author: Ethan Onstott (Ethan Onstott) * Date: 2020-04-28 15:51
Ankesh, that is the expected behavior as no patch has been merged yet.
msg367547 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-04-28 17:21
New changeset d9a43e20facdf4ad10186f820601c6580e1baa80 by Ethan Onstott in branch 'master':
bpo-40025: Require _generate_next_value_ to be defined before members (GH-19098)
msg370127 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-05-27 20:12
New changeset b5ecbf02e4dbdea6d1c9a6d7189137f76e70c073 by Miss Islington (bot) in branch '3.8':
bpo-40025: Require _generate_next_value_ to be defined before members(GH-19763)
msg371324 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-06-11 21:48
New changeset ebd44003c9e206755e5e28716242ed8941495a62 by Miss Islington (bot) in branch '3.7':
bpo-40025: Require _generate_next_value_ to be defined before members (GH-19762)
msg372002 - (view) Author: Srinivas Reddy Thatiparthy(శ్రీనివాస్ రెడ్డి తాటిపర్తి) (thatiparthy) * Date: 2020-06-21 16:28
We can close this?
msg372010 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-06-21 18:16
Not yet.  I want to investigate the idea Ankesh Saha had some more.
msg377032 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2020-09-17 00:32
There was an effort to make it so `_generate_next_value_` could be defined last and still work correctly -- unfortunately, it could not handle the more common case of using `auto()` with the default `_generate_next_value_`:

  class I(Enum):
      first = auto()
      second = first + 2    # this line would fail

Closing the ticket.  Thank you everyone!
Date User Action Args
2022-04-11 14:59:28adminsetgithub: 84206
2020-09-17 00:32:48ethan.furmansetstatus: open -> closed
resolution: fixed
messages: + msg377032

stage: patch review -> resolved
2020-09-17 00:26:45miss-islingtonsetpull_requests: + pull_request21339
2020-09-17 00:26:27miss-islingtonsetpull_requests: + pull_request21338
2020-09-16 18:33:31ethan.furmansetpull_requests: + pull_request21337
2020-09-16 18:20:03ethan.furmansetpull_requests: + pull_request21336
2020-06-21 18:16:32ethan.furmansetmessages: + msg372010
versions: + Python 3.10, - Python 3.7, Python 3.8, Python 3.9
2020-06-21 16:28:03thatiparthysetnosy: + thatiparthy
messages: + msg372002
2020-06-11 21:48:55ethan.furmansetmessages: + msg371324
2020-05-27 20:12:15ethan.furmansetmessages: + msg370127
2020-05-04 16:37:26hongweipengsetnosy: + hongweipeng
pull_requests: + pull_request19219
2020-04-28 17:22:53miss-islingtonsetpull_requests: + pull_request19084
2020-04-28 17:22:44miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request19083
2020-04-28 17:21:03ethan.furmansetmessages: + msg367547
2020-04-28 15:51:20Ethan Onstottsetmessages: + msg367538
2020-03-30 15:23:41ankeshsahasetfiles: + Issue40025.PNG
nosy: + ankeshsaha
messages: + msg365321

2020-03-25 00:55:55Jonathan Hsusetmessages: + msg364967
2020-03-22 05:20:26ethan.furmansetmessages: + msg364784
2020-03-21 21:45:08Jonathan Hsusetnosy: + Jonathan Hsu
messages: + msg364776
2020-03-21 05:15:28Ethan Onstottsetkeywords: + patch
nosy: + Ethan Onstott

pull_requests: + pull_request18458
stage: needs patch -> patch review
2020-03-20 19:09:21ethan.furmansetversions: + Python 3.8, Python 3.9
messages: + msg364707

assignee: docs@python -> ethan.furman
keywords: + easy
stage: needs patch
2020-03-20 13:35:17xtreaksetnosy: + barry, eli.bendersky, ethan.furman
2020-03-20 11:27:27edd07setcomponents: - Documentation
2020-03-20 11:25:03edd07create