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

Support overriding dunder attributes in Enum subclass #12138

Merged
merged 1 commit into from Mar 22, 2022

Conversation

flaeppe
Copy link
Contributor

@flaeppe flaeppe commented Feb 7, 2022

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 suite

Extra 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 raising AttributeErrors, but my changes needs a bit more polishing, so I left it for a later PR. Invalids there will raise during runtime anyways.

@sobolevn sobolevn self-requested a review February 7, 2022 07:40
Copy link
Member

@sobolevn sobolevn left a 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
Copy link
Member

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?

Copy link
Contributor Author

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__'):
Copy link
Member

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__

Copy link
Member

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?

Copy link
Contributor Author

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.

test-data/unit/check-enum.test Show resolved Hide resolved
test-data/unit/check-enum.test Show resolved Hide resolved
@github-actions

This comment has been minimized.

from enum import Enum

class WritingMembers(Enum):
__members__: typing.Dict[Enum, Enum] = {} # E: Cannot declare read-only attribute "__members__"
Copy link
Member

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.

Copy link
Contributor Author

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 of member_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

Copy link
Member

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'?

Copy link
Contributor Author

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!

@github-actions

This comment has been minimized.

Copy link
Member

@sobolevn sobolevn left a 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 🙂

@github-actions

This comment has been minimized.

@JukkaL
Copy link
Collaborator

JukkaL commented Mar 14, 2022

Sorry for the slow response, can you fix the merge conflicts so that I can do another review pass and get this merged?

@flaeppe
Copy link
Contributor Author

flaeppe commented Mar 14, 2022

No worries, rebased and squashed just now

@github-actions
Copy link
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@flaeppe
Copy link
Contributor Author

flaeppe commented Mar 17, 2022

@JukkaL friendly ping in case you missed that I bumped the branch

@JukkaL JukkaL merged commit 9551b93 into python:master Mar 22, 2022
JukkaL pushed a commit that referenced this pull request Mar 22, 2022
Allows any dunder (`__name__`) attributes except `__members__` (due to it being 
read-only) to be overridden in enum subclasses.

Fixes #12132.
@flaeppe flaeppe deleted the fix/issue-12132 branch March 22, 2022 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Overriding non-enumeration attributes in Enum subclass fails
3 participants