-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[ty] Detect enums if metaclass is a subtype of EnumType/EnumMeta #19481
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
|
I'm surprised the spec includes this language. It's somewhat imprecise. It's true that any class that uses >>> from enum import EnumMeta
>>> class CustomEnumType(EnumMeta): ...
...
>>> class CustomEnumBase(metaclass=CustomEnumType): ...
...
>>> class Color(CustomEnumBase):
... RED = 1
... GREEN = 2
... BLUE = 3
...
>>> list(Color)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
>>> Color.__members__
mappingproxy({'RED': <Color.RED: 1>, 'GREEN': <Color.GREEN: 2>, 'BLUE': <Color.BLUE: 3>})
>>> Color.RED
<Color.RED: 1>
>>> Color(1)
<Color.RED: 1>
>>> Color["RED"]
<Color.RED: 1> but they will also be unlike enum classes in many ways because of the fact that >>> Color.RED.name
Traceback (most recent call last):
File "<python-input-14>", line 1, in <module>
Color.RED.name
AttributeError: 'Color' object has no attribute 'name'
>>> Color.RED.value
Traceback (most recent call last):
File "<python-input-15>", line 1, in <module>
Color.RED.value
AttributeError: 'Color' object has no attribute 'value' |
Ok, but do you think we are modeling things wrong here? We do not pretend that |
I'm not sure... I can't immediately find any other behaviour differences between these "enum-like" classes and "proper |
I agree with Alex that a class that uses the Pyright's logic looks for a metaclass that derives from I wrote the enum chapter in the typing spec, so the quote above comes from me. We could update the spec to eliminate the mention of EnumMeta and EnumType. I did some spelunking to see who requested that pyright support |
FWIW, I found this class in the wild, which does not appear to have Edit: Oh, that class doesn't actually have a metaclass that derives from |
yeah, that project looks like it's doing a lot of metaprogramming, and I think it's reasonable to declare it out of scope for ty (look at these customized class This session was run after cloning https://github.com/phasehq/console locally, >>> from ee.billing.graphene.types import PlanTypeEnum
>>> PlanTypeEnum.__mro__
(<PlanTypeEnum meta=<EnumOptions name='PlanTypeEnum'>>, <Enum meta=None>, <class 'graphene.types.unmountedtype.UnmountedType'>, <class 'graphene.utils.orderedtype.OrderedType'>, <BaseType meta=None>, <SubclassWithMeta meta=None>, <class 'object'>)
>>> type(PlanTypeEnum)
<class 'graphene.types.enum.EnumMeta'>
>>> _.__mro__
(<class 'graphene.types.enum.EnumMeta'>, <class 'graphene.utils.subclass_with_meta.SubclassWithMeta_Meta'>, <class 'type'>, <class 'object'>) My inclination would be to adjust the wording of the spec here, as @erictraut suggests, so that we do not have to provide support for enum-like classes that do not inherit from |
(If we receive an actual request from a user asking for us to support this pattern, then that's a different question, of course. But until we get to that point, I'm inclined to stick to uses of the |
As a final note here before I close this:
|
This PR already exists and IMO does no harm. I think we should merge it and offer best-effort support that roughly matches what other type checkers offer and what the spec says, rather than letting the perfect be the enemy of the good and insisting that if we can't precisely model every detail of these "platypus" types correctly, we shouldn't support them at all. |
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.
This looks good to me; the implementation is extremely simple and easy enough to modify later. When in doubt, I think we should prefer compatibility with the behavior of other type checkers.
Merging this for now. If someone has additional comments, let me know and I can adapt/revert. |
* main: (28 commits) [ty] highlight the argument in `static_assert` error messages (astral-sh#19426) [ty] Infer single-valuedness for enums based on `int`/`str` (astral-sh#19510) [ty] Restructure submodule query around `File` dependency [ty] Make `Module` a Salsa ingredient [ty] Reachability analysis for `isinstance(…)` branches (astral-sh#19503) [ty] Normalize single-member enums to their instance type (astral-sh#19502) [ty] Invert `ty_ide` and `ty_project` dependency (astral-sh#19501) [ty] Implement mock language server for testing (astral-sh#19391) [ty] Detect enums if metaclass is a subtype of EnumType/EnumMeta (astral-sh#19481) [ty] perform type narrowing for places marked `global` too (astral-sh#19381) [ty] Use `ThinVec` for sub segments in `PlaceExpr` (astral-sh#19470) [ty] Splat variadic arguments into parameter list (astral-sh#18996) [`flake8-pyi`] Skip fix if all `Union` members are `None` (`PYI016`) (astral-sh#19416) Skip notebook with errors in ecosystem check (astral-sh#19491) [ty] Consistent use of American english (in rules) (astral-sh#19488) [ty] Support iterating over enums (astral-sh#19486) Fix panic for illegal `Literal[…]` annotations with inner subscript expressions (astral-sh#19489) Move fix suggestion to subdiagnostic (astral-sh#19464) [ty] Implement non-stdlib stub mapping for classes and functions (astral-sh#19471) [ty] Disallow illegal uses of `ClassVar` (astral-sh#19483) ... # Conflicts: # crates/ty_ide/src/goto.rs
* main: [ty] Fix narrowing and reachability of class patterns with arguments (#19512) [ty] Implemented partial support for "find references" language server feature. (#19475) [`flake8-use-pathlib`] Add autofix for `PTH101`, `PTH104`, `PTH105`, `PTH121` (#19404) [`perflint`] Parenthesize generator expressions (`PERF401`) (#19325) [`pep8-naming`] Fix `N802` false positives for `CGIHTTPRequestHandler` and `SimpleHTTPRequestHandler` (#19432) [`pylint`] Handle empty comments after line continuation (`PLR2044`) (#19405) Move concise diagnostic rendering to `ruff_db` (#19398) [ty] highlight the argument in `static_assert` error messages (#19426) [ty] Infer single-valuedness for enums based on `int`/`str` (#19510) [ty] Restructure submodule query around `File` dependency [ty] Make `Module` a Salsa ingredient [ty] Reachability analysis for `isinstance(…)` branches (#19503) [ty] Normalize single-member enums to their instance type (#19502) [ty] Invert `ty_ide` and `ty_project` dependency (#19501) [ty] Implement mock language server for testing (#19391) [ty] Detect enums if metaclass is a subtype of EnumType/EnumMeta (#19481) [ty] perform type narrowing for places marked `global` too (#19381)
) ## Summary This PR implements the following section from the [typing spec on enums](https://typing.python.org/en/latest/spec/enums.html#enum-definition): > Enum classes can also be defined using a subclass of `enum.Enum` **or any class that uses `enum.EnumType` (or a subclass thereof) as a metaclass**. Note that `enum.EnumType` was named `enum.EnumMeta` prior to Python 3.11. part of astral-sh/ty#183 ## Test Plan New Markdown tests
Summary
This PR implements the following section from the typing spec on enums:
part of astral-sh/ty#183
Test Plan
New Markdown tests