-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
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
gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ #108704
Conversation
🤖 New build scheduled with the buildbot fleet by @ethanfurman for commit 561dac5 🤖 If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again. |
🤖 New build scheduled with the buildbot fleet by @ethanfurman for commit 0435142 🤖 If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again. |
Thanks @ethanfurman for the PR 🌮🎉.. I'm working now to backport this PR to: 3.11, 3.12. |
…custom __new__ (pythonGH-108704) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b) Co-authored-by: Ethan Furman <[email protected]>
Sorry, @ethanfurman, I could not cleanly backport this to |
GH-108733 is a backport of this pull request to the 3.12 branch. |
|
… custom __new__ (GH-108704) (#108733) gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ (GH-108704) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b) Co-authored-by: Ethan Furman <[email protected]>
…custom __new__ (pythonGH-108704) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b)
GH-108739 is a backport of this pull request to the 3.11 branch. |
… custom __new__ (GH-108704) (GH-108739) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b)
# still not found -- verify that members exist, in-case somebody got here mistakenly | ||
# (such as via super when trying to override __new__) | ||
if not cls._member_map_: | ||
raise TypeError("%r has no members defined" % cls) | ||
# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to move _member_map_
check after _missing_
hook?
I have a derived class has no members and hook _missing_
, it raise exception here.
@ethanfurman
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, no. I tried to do that, but then the bug wasn't fixed.
What does your missing()
hook do?
If you have no other alternative, you'll need to subclass EnumType
and override the __call__
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the quick reply. here is an example:
from enum import Enum
class DynamicEnum(Enum):
@classmethod
def _missing_(cls, value):
temp = object.__new__(cls)
temp._name_ = f"{cls.__name__}_{value}"
temp._value_ = value
return temp
dynamic_enum = DynamicEnum(3)
print(dynamic_enum)
print(dynamic_enum.value)
which prints
DynamicEnum.DynamicEnum_3
3
before this change.
As my understanding, hook _missing_
handle the missing input, a dynamic value in the above case.
so I raise the question: hook _missing_
before empty check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll chime in here as well. I have the same question and got hit by this change as well. We have a setup where we do like:
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
Previously, Result(0) returned CommonResult.OK. Now throws. It was a nifty pattern to use, and it sort of breaks the combination of using the _missing_
and subclasses of Enum.
📚 Documentation preview 📚: https://cpython-previews--108704.org.readthedocs.build/