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

Makes Enum members implicitly final, refs #5599 #10852

Merged
merged 6 commits into from
Nov 10, 2021
Merged

Makes Enum members implicitly final, refs #5599 #10852

merged 6 commits into from
Nov 10, 2021

Conversation

sobolevn
Copy link
Member

Makes Enum members implicitly final

My problem was that this code was typechecking:

from enum import Enum

class A(str, Enum):
    some = 'some'
    other = 'other'

A.some = 'new'

While in runtime it was failing with:

Traceback (most recent call last):
  File "ex.py", line 12, in <module>
    A.some = 'new'
  File "/Users/sobolev/.pyenv/versions/3.8.9/lib/python3.8/enum.py", line 427, in __setattr__
    raise AttributeError('Cannot reassign members.')
AttributeError: Cannot reassign members.

This change allows to catch this error by making all Enum members implicitly Final.

I also had to modify Enum plugin, since it was not ready to work with Literal[True] and Literal[False].

Closes #5599

Notes

I would love to hear your feedback! 👍

@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

alerta (https://github.com/alerta/alerta.git)
alerta/models/enums.py:159: error: Cannot override writable attribute "value" with a final one

This one is interesting:

class A(str, Enum):
    value = 'value'

Produces:

error: Cannot override writable attribute "value" with a final one

@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

Is this fail related?

__________________________ testTypedPkgSimple_python2 __________________________

[gw1] linux -- Python 3.7.1 /home/travis/build/python/mypy/.tox/py/bin/python

data: /home/travis/build/python/mypy/test-data/unit/pep561.test:85:

/home/travis/build/python/mypy/mypy/test/testpep561.py:43: in run_case

    test_pep561(test_case)

/home/travis/build/python/mypy/mypy/test/testpep561.py:116: in test_pep561

    with virtualenv(python) as venv:

/opt/python/3.7.1/lib/python3.7/contextlib.py:112: in __enter__

    return next(self.gen)

/home/travis/build/python/mypy/mypy/test/testpep561.py:63: in virtualenv

    raise Exception("Failed to create venv. Do you have virtualenv installed?\n" + err)

E   Exception: Failed to create venv. Do you have virtualenv installed?

E   Already using interpreter /usr/bin/python2

E   New python executable in /tmp/tmppdnp09_w/bin/python2

E   Also creating executable in /tmp/tmppdnp09_w/bin/python

E   Command /tmp/tmppdnp09_w/bin/python2 -m pip config list had error code 1

E   Installing setuptools, pip, wheel...

E   

E     Complete output from command /tmp/tmppdnp09_w/bin/python2 - setuptools pip wheel:

E     Traceback (most recent call last):

E     File "<stdin>", line 15, in <module>

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv_support/pip-21.1.3-py3-none-any.whl/pip/__init__.py", line 1, in <module>

E   ImportError: No module named typing

E   ----------------------------------------

E   ...Installing setuptools, pip, wheel...done.

E   Running virtualenv with interpreter /opt/pyenv/shims/python2

E   Traceback (most recent call last):

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 2644, in <module>

E       main()

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 869, in main

E       symlink=options.symlink,

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 1188, in create_environment

E       install_wheel(to_install, py_executable, search_dirs, download=download)

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 1028, in install_wheel

E       _install_wheel_with_search_dir(download, project_names, py_executable, search_dirs)

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 1125, in _install_wheel_with_search_dir

E       call_subprocess(cmd, show_stdout=False, extra_env=env, stdin=script)

E     File "/home/travis/build/python/mypy/.tox/py/lib/python3.7/site-packages/virtualenv.py", line 962, in call_subprocess

E       raise OSError("Command {} failed with error code {}".format(cmd_desc, proc.returncode))

E   OSError: Command /tmp/tmppdnp09_w/bin/python2 - setuptools pip wheel failed with error code 1

It does not seem to be.

@hauntsaninja
Copy link
Collaborator

It's unrelated, @pranavrajpal fixed this in #10853

@sobolevn
Copy link
Member Author

Rebased, now it should be fine.

@github-actions

This comment has been minimized.

@bluetech
Copy link
Contributor

I was just intending to implement this myself, but luckily I checked before and saw there's already a PR for it :)

My reason for wanting this is not so much marking the values immutable, but to have A.some.value typed as Literal['some'] instead of str, which is something I found to be useful several times already. Currently it works by doing

class A(Enum):
    some: Final = 'some'
    other = 'other'

reveal_type(A.some.value)  # Literal['some']
reveal_type(A.other.value)  # str

but no reason for it to not be implicit.

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, enum members are clearly not mutable. This looks good to me overall, but I have question about one change.

proper_type is not None and is_equivalent(proper_type, underlying_type)
for proper_type in proper_types)
if all_equivalent_types:
return make_simplified_union(cast(Sequence[Type], proper_types))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unclear to me when the above new code is needed. Is there a test case that depends on this? If not, could you add a test case.

In any case, it would be good to have a comment here.

Copy link
Member Author

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated code with more comments 👍

@github-actions

This comment has been minimized.

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a nice improvement. Lgtm, but wouldn't mind if Jukka took another look.

mypy/plugins/enums.py Outdated Show resolved Hide resolved
mypy/plugins/enums.py Outdated Show resolved Hide resolved
@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

sobolevn commented Nov 7, 2021

Rebased! 🚜

@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

sobolevn commented Nov 7, 2021

Merge conflicts 😮‍💨

Should be fine now!

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2021

Diff from mypy_primer, showing the effect of this PR on open source code:

prefect (https://github.com/PrefectHQ/prefect.git)
+ src/prefect/core/task.py:45: error: Cannot override writable attribute "value" with a final one

alerta (https://github.com/alerta/alerta.git)
+ alerta/models/enums.py:159: error: Cannot override writable attribute "value" with a final one

@97littleleaf11
Copy link
Collaborator

lgtm, I really like this feature! Also thanks for your detailed comments.

@97littleleaf11 97littleleaf11 merged commit fdcda96 into python:master Nov 10, 2021
@sobolevn
Copy link
Member Author

Awesome, thanks, everyone! 🎉

@joey-laminar
Copy link
Contributor

I was just intending to implement this myself, but luckily I checked before and saw there's already a PR for it :)

My reason for wanting this is not so much marking the values immutable, but to have A.some.value typed as Literal['some'] instead of str, which is something I found to be useful several times already. Currently it works by doing

class A(Enum):
    some: Final = 'some'
    other = 'other'

reveal_type(A.some.value)  # Literal['some']
reveal_type(A.other.value)  # str

but no reason for it to not be implicit.

It looks like this still doesn't work in latest mypy for strings (but does work for int/bool). Any idea why?

tushar-deepsource pushed a commit to DeepSourceCorp/mypy that referenced this pull request Jan 20, 2022
refs python#5599

This change allows to catch this error by making all Enum members 
implicitly Final.

Also modifies Enum plugin, since it was not ready to work 
with `Literal[True]` and `Literal[False]`.

Co-authored-by: Shantanu <[email protected]>
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.

Make enum attributes implicitly final
6 participants