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

Multiple enum mixins not allowed even when they have the same datatype #88522

Closed
JEphron mannequin opened this issue Jun 9, 2021 · 11 comments
Closed

Multiple enum mixins not allowed even when they have the same datatype #88522

JEphron mannequin opened this issue Jun 9, 2021 · 11 comments
Assignees
Labels
3.9 only security fixes 3.10 only security fixes 3.11 only security fixes type-bug An unexpected behavior, bug, or error

Comments

@JEphron
Copy link
Mannequin

JEphron mannequin commented Jun 9, 2021

BPO 44356
Nosy @ethanfurman, @miss-islington, @JEphron
PRs
  • bpo-44356: [Enum] allow multiple data-type mixins if they are all the same #26649
  • [3.9] bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649) #26652
  • [3.10] bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649) #26653
  • [3.8] bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649) #26654
  • 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 2021-06-11.08:19:34.541>
    created_at = <Date 2021-06-09.04:20:29.919>
    labels = ['type-bug', '3.9', '3.10', '3.11']
    title = 'Multiple enum mixins not allowed even when they have the same datatype'
    updated_at = <Date 2021-06-11.08:27:36.541>
    user = 'https://github.com/JEphron'

    bugs.python.org fields:

    activity = <Date 2021-06-11.08:27:36.541>
    actor = 'JEphron'
    assignee = 'ethan.furman'
    closed = True
    closed_date = <Date 2021-06-11.08:19:34.541>
    closer = 'ethan.furman'
    components = []
    creation = <Date 2021-06-09.04:20:29.919>
    creator = 'JEphron'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 44356
    keywords = ['patch']
    message_count = 11.0
    messages = ['395378', '395380', '395433', '395446', '395543', '395570', '395579', '395580', '395610', '395611', '395614']
    nosy_count = 3.0
    nosy_names = ['ethan.furman', 'miss-islington', 'JEphron']
    pr_nums = ['26649', '26652', '26653', '26654']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue44356'
    versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']

    @JEphron
    Copy link
    Mannequin Author

    JEphron mannequin commented Jun 9, 2021

    Prior to 3.8 it was possible to create "abstract" enums (without members) and mix them together. To motivate with an example, perhaps we're modeling an API and want to be robust in the face of inconsistent casing

    class CaseInsensitiveStrEnum(str, Enum):
        @classmethod
        def _missing_(cls, value):
            for member in cls._member_map_.values():
                if member._value_.lower() == value.lower():
                    return member
            return super()._missing_(value)

    and perhaps we also want to be robust in response to extensibility

    class LenientStrEnum(str, Enum):
        @classmethod
        def _missing_(cls, value):
            logger.warning(
                f"[{cls.__name__}] encountered an unknown value!\n"
                f"Luckily I'm a LenientStrEnum, so I won't crash just yet.\n"
                f"You might want to add a new case though.\n"
                f"Value was: '{value}'"
            )
            return UnexpectedStr(value)

    but we also want to model some known good set of values, so mixing together the abstract enums we'd get something like

    class JobStatus(CaseInsensitiveStrEnum, LenientStrEnum):
        ACTIVE = "active"
        PENDING = "pending"
        TERMINATED = "terminated"

    However, due to the resolution of https://bugs.python.org/issue39587 this no longer works, instead producing:

    TypeError: 'JobStatus': too many data types: [<class 'str'>, <class 'str'>]

    The relevant change is

    ethanfurman@bff01f3

    I believe that if we made data_types a set rather than a list then the example would become valid once again.

    @JEphron JEphron mannequin added 3.8 only security fixes 3.9 only security fixes type-bug An unexpected behavior, bug, or error labels Jun 9, 2021
    @ethanfurman ethanfurman added 3.10 only security fixes 3.11 only security fixes labels Jun 9, 2021
    @ethanfurman ethanfurman self-assigned this Jun 9, 2021
    @ethanfurman ethanfurman added 3.10 only security fixes 3.11 only security fixes labels Jun 9, 2021
    @ethanfurman ethanfurman self-assigned this Jun 9, 2021
    @ethanfurman
    Copy link
    Member

    Excellent bug report.

    But what is an UnexpectedString() ?

    @JEphron
    Copy link
    Mannequin Author

    JEphron mannequin commented Jun 9, 2021

    But what is an UnexpectedString()

    Sorry, that’s maybe less relevant to the example. It’s just a subclass of string with some marker to make it detectable later on, similar to schemes that taint user input to prevent sql injection or whatever

    On Jun 8, 2021, at 23:20, Ethan Furman report@bugs.python.org wrote:

    
    Ethan Furman ethan@stoneleaf.us added the comment:

    Excellent bug report.

    But what is an UnexpectedString() ?

    ----------


    Python tracker <report@bugs.python.org>
    <https://bugs.python.org/issue44356\>


    @JEphron
    Copy link
    Mannequin Author

    JEphron mannequin commented Jun 9, 2021

    Oh, on further investigation I see that the example wouldn't have worked on 3.8 anyway, due to bpo-34536 adding some checks to the type returned by _missing_, so maybe I'm pushing Enum too far in that case.

    @ethanfurman
    Copy link
    Member

    Since I like puzzles, here is a working LenientStrEnum:

        class LenientStrEnum(str, Enum):
            #
            def __init__(self, *args):
                self._valid = True
            #
            @classmethod
            def _missing_(cls, value):
                logger.warning(
                    f"[{cls.__name__}] encountered an unknown value!\n"
                    f"Luckily I'm a LenientStrEnum, so I won't crash just yet.\n"
                    f"You might want to add a new case though.\n"
                    f"Value was: '{value}'"
                    )
                unknown = cls._member_type_.__new__(cls, value)
                unknown._valid = False
                unknown._name_ = value.upper()
                unknown._value_ = value
                cls._member_map_[value] = unknown
                return unknown
            #
            @property
            def valid(self):
                return self._valid

    _member_map_ is not guaranteed, but is unlikely to change.

    @ethanfurman ethanfurman changed the title Abstract enum mixins not allowed Multiple enum mixins not allowed even when they have the same datatype Jun 10, 2021
    @ethanfurman ethanfurman changed the title Abstract enum mixins not allowed Multiple enum mixins not allowed even when they have the same datatype Jun 10, 2021
    @ethanfurman
    Copy link
    Member

    New changeset 8a4f085 by Ethan Furman in branch 'main':
    bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649)
    8a4f085

    @ethanfurman
    Copy link
    Member

    New changeset 0128601 by Miss Islington (bot) in branch '3.10':
    bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649) (GH-26653)
    0128601

    @ethanfurman
    Copy link
    Member

    New changeset 304ec53 by Miss Islington (bot) in branch '3.9':
    bpo-44356: [Enum] allow multiple data-type mixins if they are all the same (GH-26649) (GH-26652)
    304ec53

    @JEphron
    Copy link
    Mannequin Author

    JEphron mannequin commented Jun 11, 2021

    On 10.06.2021 15:33, Ethan Furman wrote:

    Since I like puzzles, here is a working LenientStrEnum:
    ...

    Oh indeed, that's really cool!

    @JEphron JEphron mannequin changed the title Multiple enum mixins not allowed even when they have the same datatype Abstract enum mixins not allowed Jun 11, 2021
    @JEphron JEphron mannequin changed the title Multiple enum mixins not allowed even when they have the same datatype Abstract enum mixins not allowed Jun 11, 2021
    @ethanfurman
    Copy link
    Member

    Glad you like it!

    Please don't change the title back. :-)

    @ethanfurman ethanfurman removed the 3.8 only security fixes label Jun 11, 2021
    @ethanfurman ethanfurman changed the title Abstract enum mixins not allowed Multiple enum mixins not allowed even when they have the same datatype Jun 11, 2021
    @ethanfurman ethanfurman removed the 3.8 only security fixes label Jun 11, 2021
    @ethanfurman ethanfurman changed the title Abstract enum mixins not allowed Multiple enum mixins not allowed even when they have the same datatype Jun 11, 2021
    @JEphron
    Copy link
    Mannequin Author

    JEphron mannequin commented Jun 11, 2021

    Oops, still getting used to the Python mailing list format. Learned
    something new! Thanks for all your awesome work on Python.

    On 11.06.2021 08:19, Ethan Furman wrote:

    Ethan Furman <ethan@stoneleaf.us> added the comment:

    Glad you like it!

    Please don't change the title back. :-)

    ----------
    resolution: -> fixed
    stage: patch review -> resolved
    status: open -> closed
    title: Abstract enum mixins not allowed -> Multiple enum mixins not allowed even when they have the same datatype
    versions: -Python 3.8


    Python tracker <report@bugs.python.org>
    <https://bugs.python.org/issue44356\>


    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.9 only security fixes 3.10 only security fixes 3.11 only security fixes type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    1 participant