-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Support overriding dunder attributes in Enum subclass #12138
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
Conversation
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.
Thank you! 👏
mypy/checker.py
Outdated
if (isinstance(sym.node, Var) and sym.node.has_explicit_value and | ||
sym.node.name == '__members__'): | ||
self.fail( | ||
'Cannot declare read-only attribute "__members__"', sym.node |
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.
Can you please move this string to message_registry.py
?
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.
Done, had a bit of a hard time coming up with a reasonable name for the variable. Let me know if you got any better than what I could come up with
if defn.info.fullname not in ENUM_BASES: | ||
for sym in defn.info.names.values(): | ||
if (isinstance(sym.node, Var) and sym.node.has_explicit_value and | ||
sym.node.name == '__members__'): |
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.
Let's add a comment: why don't we allow __members__
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.
So, to clarify:
>>> import enum
>>> class E(enum.Enum):
... __members__ = (1, 2)
...
>>> class D(E):
... __members__ = (3, 4)
...
>>> D.__members__
mappingproxy({})
It is not a runtime error. But, it does not make sense, right?
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.
Yeah, exactly. __members__
will always be overwritten. So it doesn't make sense to try to declare any value to it.
This comment has been minimized.
This comment has been minimized.
test-data/unit/check-enum.test
Outdated
from enum import Enum | ||
|
||
class WritingMembers(Enum): | ||
__members__: typing.Dict[Enum, Enum] = {} # E: Cannot declare read-only attribute "__members__" |
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 think we can improve this error message. __members__
is not read only, it more like "internal"?..
I would be quite lost with the current one.
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.
Yeah, agreed, I tried to generalise what's said in the documentation a bit: https://docs.python.org/3/library/enum.html#supported-dunder-names
__members__
is a read-only ordered mapping ofmember_name:member
items. It is only available on the class.
Inlining it here just for reference
What do you think about Cannot assign to internal attribute "__members__"
? Not completely sure if "assign" is most appropriate though
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.
Maybe exposing the internal implementation would be a good idea in this case?
Like 'Assigned "__members__" will be overriden by "Enum" internally'
?
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.
That reads good IMO, lets stick with that!
This comment has been minimized.
This comment has been minimized.
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 like it! Thank you!
I am going to wait for someone else to merge this, since I am new here and might miss something 🙂
This comment has been minimized.
This comment has been minimized.
Sorry for the slow response, can you fix the merge conflicts so that I can do another review pass and get this merged? |
de303eb
to
360fd99
Compare
No worries, rebased and squashed just now |
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉 |
@JukkaL friendly ping in case you missed that I bumped the branch |
Allows any dunder (`__name__`) attributes except `__members__` (due to it being read-only) to be overridden in enum subclasses. Fixes #12132.
Description
Allows any dunder(
__name__
) attributes except__members__
(due to it being read-only) to be overridden in enum subclasses.Fixes #12132
Test Plan
Added tests to the
check-enum
suiteExtra notes
I'm quite new to mypy's code. So please help out with stuff I might've missed or so. I'm also not sure if I've placed the
__members__
fail correctly or if it's better placed in the analyser.I also tried to fix sunder (
_name_
) attributes raisingAttributeError
s, but my changes needs a bit more polishing, so I left it for a later PR. Invalids there will raise during runtime anyways.