mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Enums: Teach mypy that descriptors are not converted to become enum members

Open AlexWaygood opened this issue 3 years ago • 9 comments

Fixes #12494.

Description

At runtime, most objects in an enum class statement are automatically converted by the enum module into members of the enumeration. However, if an object has a __get__, __set__ or __delete__ method, special-casing by the enum module means that the object is not converted into an enum member. Mypy is currently aware of this special-casing, leading it to falsely assume that certain enum classes are unsubclassable, when, in fact, they are.

This PR teaches mypy about descriptor special-casing in enums.

Test Plan

  • Two test cases have been added.
  • One test case has been extended.
  • One test case has been altered slightly -- it was in fact buggy. (It shouldn't have been passing.)

AlexWaygood avatar Apr 25 '22 03:04 AlexWaygood

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

github-actions[bot] avatar Apr 25 '22 03:04 github-actions[bot]

Ah, this patch fixes some false positives where mypy complains about enums-with-no-members being unsubclassable, but there are still some issues:

from enum import Enum

class Foo(Enum):
    x = classmethod(lambda cls: 42)

reveal_type(Foo.x)  # note: Revealed type is "Literal[test.Foo.x]?"
b = Foo.x()  # error: "Foo" not callable

AlexWaygood avatar Apr 26 '22 01:04 AlexWaygood

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

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/pq/_enums.py:202: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.960+dev.07988fccbcb97b054cde780b2463387193d8a0ec
+ psycopg/psycopg/pq/_enums.py:202: : note: use --pdb to drop into pdb
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 180, in build
+     result = _build(
+   File "/build.py", line 256, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2733, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3081, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3173, in process_stale_scc
+     mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors)
+   File "/semanal_main.py", line 78, in semantic_analysis_for_scc
+     process_top_levels(graph, scc, patches)
+   File "/semanal_main.py", line 199, in process_top_levels
+     deferred, incomplete, progress = semantic_analyze_target(next_id, state,
+   File "/semanal_main.py", line 326, in semantic_analyze_target
+     analyzer.refresh_partial(refresh_node,
+   File "/semanal.py", line 414, in refresh_partial
+     self.refresh_top_level(node)
+   File "/semanal.py", line 425, in refresh_top_level
+     self.accept(d)
+   File "/semanal.py", line 5352, in accept
+     node.accept(self)
+   File "/nodes.py", line 1028, in accept
+     return visitor.visit_class_def(self)
+   File "/semanal.py", line 1121, in visit_class_def
+     self.analyze_class(defn)
+   File "/semanal.py", line 1201, in analyze_class
+     self.analyze_class_body_common(defn)
+   File "/semanal.py", line 1209, in analyze_class_body_common
+     defn.defs.accept(self)
+   File "/nodes.py", line 1099, in accept
+     return visitor.visit_block(self)
+   File "/semanal.py", line 3630, in visit_block
+     self.accept(s)
+   File "/semanal.py", line 5352, in accept
+     node.accept(self)
+   File "/nodes.py", line 1165, in accept
+     return visitor.visit_assignment_stmt(self)
+   File "/semanal.py", line 2100, in visit_assignment_stmt
+     self.store_final_status(s)
+   File "/semanal.py", line 2495, in store_final_status
+     info = self.named_type(s.rvalue.callee.fullname).type
+   File "/semanal.py", line 4725, in named_type
+     assert isinstance(node, TypeInfo)
+ AssertionError: 

github-actions[bot] avatar Apr 26 '22 05:04 github-actions[bot]

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

github-actions[bot] avatar Apr 26 '22 05:04 github-actions[bot]

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

github-actions[bot] avatar Apr 26 '22 06:04 github-actions[bot]

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

github-actions[bot] avatar Apr 26 '22 18:04 github-actions[bot]

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

github-actions[bot] avatar Apr 26 '22 21:04 github-actions[bot]

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

github-actions[bot] avatar Apr 26 '22 21:04 github-actions[bot]

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

github-actions[bot] avatar Jul 27 '22 14:07 github-actions[bot]

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

github-actions[bot] avatar Aug 15 '22 09:08 github-actions[bot]

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

github-actions[bot] avatar Aug 19 '22 11:08 github-actions[bot]

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

github-actions[bot] avatar Aug 19 '22 12:08 github-actions[bot]

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

github-actions[bot] avatar Sep 28 '22 17:09 github-actions[bot]

I doubt I'll get to this any time soon

AlexWaygood avatar Jan 22 '23 18:01 AlexWaygood