Skip to content

Enum: using _missing_ on Enums without any elements doesn't work since 3.11.6 #111103

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
mikrodust-henrikp opened this issue Oct 20, 2023 · 2 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@mikrodust-henrikp
Copy link

mikrodust-henrikp commented Oct 20, 2023

Bug report

Bug description:

It seems the pull request (#108704) that tried to handle erroneous calls to Enum.__new__ introduced some other behavior as well. In a few projects that I'm involved with we use this pattern:

from enum import Enum

class Result(Enum):
    @classmethod
    def _missing_(cls, value):
        v = None
        for subclass in cls.__subclasses__():
            try:
                v = subclass(value)
            except:
                pass

        return v


class CommonResult(Result):
    OK = 0
    ERR_FOO = -0x1
    ERR_BAR = -0x2

where calling Result(0) would return CommonResult.OK. Now in 3.11.6 and onwards this throws a TypeError. Was this intentional? It's a nice pattern to have and there are to me no obvious ways to replace it with something as elegant, so I hope that it can be solved some other way (maybe by checking if the class actually overrides __new__ and if not, then allowing the call to _missing_?)

CPython versions tested on:

3.11, 3.12

Operating systems tested on:

Linux, Windows

@mikrodust-henrikp mikrodust-henrikp added the type-bug An unexpected behavior, bug, or error label Oct 20, 2023
@AlexWaygood AlexWaygood added the stdlib Python modules in the Lib dir label Oct 20, 2023
@mikrodust-henrikp
Copy link
Author

mikrodust-henrikp commented Oct 20, 2023

Found a workaround which is not horrible. I still think that it would be nice for a few other use cases if the _missing_ pattern worked, but if anyone else needs it, here's a solution that replicates what at least I did:

from enum import Enum, EnumMeta

class Fudgeulator(EnumMeta):
    def __call__(cls, value, *args, **kwargs):
        for subclass in cls.__subclasses__():
            try:
                return subclass(value)
            except Exception:
                pass
        
        return super().__call__(value, *args, **kwargs)

class Result(Enum, metaclass=Fudgeulator):
    pass

class Foo(Result):
    pass

class CommonResult(Foo):
    OK = 0
    ERR_FOO = -0x1
    ERR_BAR = -0x2

class AnotherResult(Result):
    ERR_QUUX = -0x4
    ERR_BAAA = -0x5


print(Result(-0x4))
print(CommonResult.OK)

@ethanfurman
Copy link
Member

@mikrodust-henrikp: Definitely an intriguing use-case. Your subclass of EnumType is the correct solution (and hopefully not onerous).

@ethanfurman ethanfurman closed this as not planned Won't fix, can't repro, duplicate, stale Oct 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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