Skip to content

IntEnum __format__ behavior can't be overridden through __str__ #81660

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

Closed
thatneat mannequin opened this issue Jul 1, 2019 · 9 comments
Closed

IntEnum __format__ behavior can't be overridden through __str__ #81660

thatneat mannequin opened this issue Jul 1, 2019 · 9 comments
Assignees
Labels
3.8 (EOL) end of life 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@thatneat
Copy link
Mannequin

thatneat mannequin commented Jul 1, 2019

BPO 37479
Nosy @ericvsmith, @ethanfurman, @thatneat
PRs
  • bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__ #14545
  • [3.8] bpo-37479: Enum - use correct __format__ (GH-14545) #22227
  • [3.7] bpo-37479: Enum - user correct __format__ (GH-14545) #22228
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/ethanfurman'
    closed_at = <Date 2020-09-13.20:51:07.553>
    created_at = <Date 2019-07-01.23:24:11.546>
    labels = ['3.8', 'type-bug', 'library', '3.9']
    title = "IntEnum __format__ behavior can't be overridden through __str__"
    updated_at = <Date 2020-09-13.20:51:07.551>
    user = 'https://github.com/thatneat'

    bugs.python.org fields:

    activity = <Date 2020-09-13.20:51:07.551>
    actor = 'ethan.furman'
    assignee = 'ethan.furman'
    closed = True
    closed_date = <Date 2020-09-13.20:51:07.553>
    closer = 'ethan.furman'
    components = ['Library (Lib)']
    creation = <Date 2019-07-01.23:24:11.546>
    creator = 'jason.curtis'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 37479
    keywords = ['patch']
    message_count = 7.0
    messages = ['347089', '347090', '347094', '347096', '347291', '376848', '376851']
    nosy_count = 3.0
    nosy_names = ['eric.smith', 'ethan.furman', 'jason.curtis']
    pr_nums = ['14545', '22227', '22228']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue37479'
    versions = ['Python 3.8', 'Python 3.9']

    @thatneat
    Copy link
    Mannequin Author

    thatneat mannequin commented Jul 1, 2019

    Combining int and Enum, as with enum.IntEnum results in a class where __str__ cannot be effectively overridden. For example:

    from enum import IntEnum
    
    class myIntEnum(IntEnum):
        x = 1
        
        def __str__(self):
            return 'aaaaAAAa'
        
    f'{myIntEnum.x}, {str(myIntEnum.x)}'

    Expected output:
    'aaaaAAAa, aaaaAAAa'

    Actual output:
    '1, aaaaAAAa'

    Overriding __str__ in this way works as expected if the inherited classes are int or Enum individually. However, it does not work when inheriting (int, Enum) or when inheriting (intEnum).

    Presumably this is a side effect of Enum's mixin behavior documented at https://docs.python.org/3/library/enum.html#others and it is possibly related to https://bugs.python.org/issue18264 .

    @thatneat thatneat mannequin added stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Jul 1, 2019
    @thatneat
    Copy link
    Mannequin Author

    thatneat mannequin commented Jul 1, 2019

    I mistyped - __str__ can be overridden effectively, but __format__ has to be overridden instead or overridden also to get the desired f-string behavior.

    @thatneat
    Copy link
    Mannequin Author

    thatneat mannequin commented Jul 1, 2019

    related cPython code:

    if self._member_type_ is object:

    If we're in a mixed-in class but the class has overridden __str__(), we should probably use str(self) instead of self._value_.

    @ericvsmith
    Copy link
    Member

    Note that this isn't really related to f-strings, except that they use the __format__ protocol, as does str.__format__.

    >>> format(myIntEnum.x)
    '1'

    @ethanfurman ethanfurman self-assigned this Jul 2, 2019
    @thatneat thatneat mannequin changed the title IntEnum f-string behavior can't be overridden IntEnum __format__ behavior can't be overridden through __str__ Jul 2, 2019
    @ethanfurman
    Copy link
    Member

    New changeset 2f19e82 by Ethan Furman (thatneat) in branch 'master':
    bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__ (GH-14545)
    2f19e82

    @ethanfurman
    Copy link
    Member

    New changeset 38c8d39 by Ethan Furman in branch '3.8':
    [3.8] bpo-37479: Enum - use correct __format__ (GH-14545)
    38c8d39

    @ethanfurman
    Copy link
    Member

    Thank you, Jason!

    @ethanfurman ethanfurman added 3.8 (EOL) end of life 3.9 only security fixes labels Sep 13, 2020
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @zinnjonas
    Copy link

    zinnjonas commented Nov 23, 2022

    Was this change really intended?

    The change will make projects break in their behavior when changed from 3.7 to a newer version.

    from enum import IntEnum
    
    class Test(IntEnum):
         A = 0
         B = 0
    
    t = Test.A
    print(f"0x{t:X} {str(t)}")

    is valid python code and also compatible with python 3.7.
    But!!!

    from enum import IntEnum
    
    class Test(IntEnum):
         A = 0
         B = 0
        
         def __str__(self):
                 return self.name.lower()
    
    t = Test.A
    print(f"0x{t:X} {str(t)}")

    is valid in python 3.7 but due to the change not valid in python 3.8+ anymore.
    Since "str" can't be represented by "{:X}".
    Since isinstance(t, int) is true in both scenarios, it is to be expected that f"0x{t:X}" will hold. it is an "int" and not a "str"

    The str function exists in both cases, the override of the second case shall not alter the behavior of the class !

    @ethanfurman
    Copy link
    Member

    @zinnjonas This has been corrected in 3.11.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.8 (EOL) end of life 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants