Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.11.1 Regression: namedtuple Enum values are cast to tuple #100098

Closed
Zannick opened this issue Dec 8, 2022 · 2 comments
Closed

3.11.1 Regression: namedtuple Enum values are cast to tuple #100098

Zannick opened this issue Dec 8, 2022 · 2 comments
Assignees
Labels
type-bug An unexpected behavior, bug, or error

Comments

@Zannick
Copy link

Zannick commented Dec 8, 2022

Bug report

Between 3.11.0 and 3.11.1, Enums whose values are namedtuple objects have their values converted to tuple, which drops the field names we expect to be able to use, causing AttributeErrors. Test cases below create a namedtuple and an enum whose values are instances of that tuple. In the 3.11.1 case, referencing the enum value like NTEnum.NONE.value produces a tuple and not a namedtuple. In both cases, copy.copy preserves the namedtuple type.

It is not clear whether any item in the changelog or release notes references this change, nor could I quickly tell whether this was related to changes to address #93910.

Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
>>> from enum import Enum
>>> from collections import namedtuple
>>> TTuple = namedtuple('TTuple', 'id a blist')
>>> class NTEnum(Enum):
...     NONE = TTuple(0, 0, [])
...     A = TTuple(1, 2, [4])
...     B = TTuple(2, 4, [0, 1, 2])
... 
...     
>>> NTEnum.NONE
<NTEnum.NONE: TTuple(id=0, a=0, blist=[])>
>>> NTEnum.NONE.value
TTuple(id=0, a=0, blist=[])
>>> [x.value for x in NTEnum]
[TTuple(id=0, a=0, blist=[]), TTuple(id=1, a=2, blist=[4]), TTuple(id=2, a=4, blist=[0, 1, 2])]
>>> import copy
>>> x = TTuple(0, 1, [7])
>>> x
TTuple(id=0, a=1, blist=[7])
>>> copy.copy(x)
TTuple(id=0, a=1, blist=[7])
>>> copy.deepcopy(x)
TTuple(id=0, a=1, blist=[7])
>>> NTEnum.NONE.value.blist
[]
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
>>> from enum import Enum
>>> from collections import namedtuple
>>> TTuple = namedtuple('TTuple', 'id a blist')
>>> class NTEnum(Enum):
...     NONE = TTuple(0, 0, [])
...     A = TTuple(1, 2, [4])
...     B = TTuple(2, 4, [0, 1, 2])
... 
...     
>>> NTEnum.NONE
<NTEnum.NONE: (0, 0, [])>
>>> NTEnum.NONE.value
(0, 0, [])
>>> [x.value for x in NTEnum]
[(0, 0, []), (1, 2, [4]), (2, 4, [0, 1, 2])]
>>> import copy
>>> x = TTuple(0, 1, [7])
>>> x
TTuple(id=0, a=1, blist=[7])
>>> copy.copy(x)
TTuple(id=0, a=1, blist=[7])
>>> copy.deepcopy(x)
TTuple(id=0, a=1, blist=[7])
>>> NTEnum.NONE.value.blist
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    NTEnum.NONE.value.blist
AttributeError: 'tuple' object has no attribute 'blist'

Your environment

  • CPython versions tested on: 3.11.0, 3.11.1
  • Operating system and architecture: win64 (amd64)
  • 3.11.0 additionally tested on linux
  • 3.11.0, 3.11.1 tested in IDLE

Linked PRs

@Zannick Zannick added the type-bug An unexpected behavior, bug, or error label Dec 8, 2022
@ethanfurman ethanfurman self-assigned this Dec 8, 2022
ethanfurman added a commit that referenced this issue Dec 8, 2022
…H-100099)

When checking for auto() instances, only top-level usage is supported,
which means either alone or as part of a regular tuple. Other
containers, such as lists, dicts, or namedtuples, will not have auto()
transformed into a value.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Dec 8, 2022
…uto (pythonGH-100099)

When checking for auto() instances, only top-level usage is supported,
which means either alone or as part of a regular tuple. Other
containers, such as lists, dicts, or namedtuples, will not have auto()
transformed into a value.
(cherry picked from commit ded02ca)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
miss-islington added a commit that referenced this issue Dec 8, 2022
…H-100099)

When checking for auto() instances, only top-level usage is supported,
which means either alone or as part of a regular tuple. Other
containers, such as lists, dicts, or namedtuples, will not have auto()
transformed into a value.
(cherry picked from commit ded02ca)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
@ethanfurman
Copy link
Member

ethanfurman commented Dec 8, 2022

Thank you for the bug report. The fix will be in 3.11.2. If you need to use 3.11.1 you can inherit directly from TTuple:

>>> from enum import Enum
>>> from collections import namedtuple
>>> TTuple = namedtuple('TTuple', 'id a blist')
>>> class NTEnum(TTuple, Enum):
...     NONE = TTuple(0, 0, [])
...     A = TTuple(1, 2, [4])
...     B = TTuple(2, 4, [0, 1, 2])
... 
>>> NTEnum.A
<NTEnum.A: TTuple(id=1, a=2, blist=[4])>
>>> 
>>> NTEnum.A.id
1
>>> NTEnum.B.blist
[0, 1, 2]

On the up-side, the .value attribute is now a TTuple, and you can directly access the id, a, and blist attributes on the enum mebers; on the (possible) down-side, the enum member is directly comparable to tuples.

@tzaffi
Copy link

tzaffi commented Dec 16, 2022

FYI, you can achieve equivalent functionality on python 3.11.1 for typing.NamedTuple using dataclasses as follows:

>>> from enum import Enum
>>> from dataclasses import dataclass
>>> 
>>> @dataclass
... class TTuple:
...     id: int
...     a: int
...     blist: list[int]
... 
>>> 
>>> class NTEnum(Enum):
...     NONE = TTuple(0, 0, [])
...     A = TTuple(1, 2, [4])
...     B = TTuple(2, 4, [0, 1, 2])
... 
>>> NTEnum.A
<NTEnum.A: TTuple(id=1, a=2, blist=[4])>
>>> NTEnum.A.value.id
1
>>> NTEnum.B.value.blist
[0, 1, 2]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants