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

Enum-like user classes #4311

Closed
orenbenkiki opened this issue Dec 3, 2017 · 13 comments
Closed

Enum-like user classes #4311

orenbenkiki opened this issue Dec 3, 2017 · 13 comments

Comments

@orenbenkiki
Copy link

orenbenkiki commented Dec 3, 2017

Feature request: Allow annotating a class as enum-like for mypy type checking purposes.

I have classes dealing with a value-like (immutable) type.
One of the classes in that family is an enum-like class, whose values are of the new immutable type.
I tried to implement this by deriving from the standard library Enum, but it has pretty strong opinions about the implementation which do not match the way my value type works (it is too different from a plain integer).

No worries, I just implemented my own MyEnumBase base class (and MyEnumMeta for it), loosely inspired by Enum.
It works fine.

The problem is convincing mypy to type check it.
Specifically, if I write:

class EnumExample(MyEnumBase):
    FOO = MyAuto()

Then mypy will not recognize that EnumExample.FOO has the type EnumExample.
I can work around this by the following horrible hack:

class IsEnumBase:
    """Check whether a given class is an enumerated base class."""
    def __eq__(self, class_name: Any) -> bool:
        return class_name == 'enum.Enum' or class_name == 'my.module.MyEnumBase'

mypy.nodes.ENUM_BASECLASS = IsEnumBase()

Which mostly works, but, shudder (is there a better way to achieve the same effect?)

It would be nice if I could annotate MyEnumBase somehow to let mypy it is an enum base class.
For example, if the single mypy.nodes.ENUM_BASECLASS was replaced by a set of class names, so that it were possible to insert additional class names into it (and provide a command line flag to mypy for doing that).

@ethanhs
Copy link
Collaborator

ethanhs commented Dec 3, 2017

Have you tried inheriting from EnumMeta? I believe that was designed to be more customizable.

@ilevkivskyi
Copy link
Member

Can this be achieved by descriptors? mypy already supports __get__ and __set__.

@orenbenkiki
Copy link
Author

orenbenkiki commented Dec 3, 2017

@ethanhs: I did try deriving from EnumMeta, but that was using Python 3.6.0. Perhaps it was modified since but I'm still supporting 3.6.0. Even if I could use it, from looking at the code in mypy.nodes, it seems it explicitly checks whether the base class name is enum.Enum rather than checking whether the base class uses EnumMeta as a metaclass, so I doubt that would solve the type checking problem.

@ilevkivskyi: Not sure what you mean. My problem is in convincing mypy that ExampleEnum.FOO has the actual type it has at run-time (that is, ExampleEnum) rather than the type mypy thinks it has (that is, MyAuto). Since I explicitly initialize the class member, I don't think __set__ and __get__ can come into play here.

@ilevkivskyi
Copy link
Member

@orenbenkiki

Not sure what you mean

You can make MyAuto a descriptor (in stubs).

@orenbenkiki
Copy link
Author

Ah, I see. This would require writing MyAuto[MyEnumClass] at the very least - and even then, I think mypy won't be smart enough to understand __get__ returns a MyEnumClass. Also, MyAuto is just one option - in other cases I initialize with an explicit value of some type (similar to explicitly initializing a standard enum value to an integer). In these cases, mypy thinks the property has the initializer's type instead of the enum's class type.

@elazarg
Copy link
Contributor

elazarg commented Dec 3, 2017

I think it is a reasonable feature request to make instances of EnumMeta have enum-like types.

@ethanhs
Copy link
Collaborator

ethanhs commented Dec 3, 2017

I feel that the underlying issue @orenbenkiki is hitting is #1020, since MyAuto returns a different type than it is. Although I'm not sure this is something that we should support.

Also, I'm not certain that we should support classes with EnumMeta as a metaclass, as it may not follow the assumptions we have made about Enum

@elazarg
Copy link
Contributor

elazarg commented Dec 4, 2017

I wasn't talking about inheriting classes but classes that use it as metaclass; this is a declaration of intent just like inheriting Enum.

@ethanhs
Copy link
Collaborator

ethanhs commented Dec 4, 2017

Ah, I mispoke, I mean classes that use it as a metaclass. Anyway, I don't see why we can't try that.

@elazarg
Copy link
Contributor

elazarg commented Dec 4, 2017

I have implemented it - it's pretty easy. But it falsifies the assumption on typeshed that only Enum derivatives instantiate EnumClass, so typeshed should be fixed first.

elazarg added a commit to elazarg/typeshed that referenced this issue Dec 4, 2017
gvanrossum pushed a commit to python/typeshed that referenced this issue Dec 4, 2017
@ethanhs
Copy link
Collaborator

ethanhs commented Dec 17, 2017

@orenbenkiki I am going to close this, as the original ask is unlikely to happen, and EnumMeta is now checked properly. Feel free to leave a message if this is not satisfactory.

@ethanhs ethanhs closed this as completed Dec 17, 2017
@elazarg
Copy link
Contributor

elazarg commented Dec 17, 2017

@ethanhs What about #4319?

@ethanhs
Copy link
Collaborator

ethanhs commented Dec 17, 2017

Oh, I thought that had been merged! My mistake.

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

No branches or pull requests

5 participants