Skip to content

len(Enum) error #2891

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

Closed
MeGotsThis opened this issue Feb 22, 2017 · 7 comments
Closed

len(Enum) error #2891

MeGotsThis opened this issue Feb 22, 2017 · 7 comments

Comments

@MeGotsThis
Copy link

Not sure if this is a mypy or typeshed problem.

import enum

class Letters(enum.Enum):
    A = 0
    B = 1

print(len(Letters))

Running mypy gives this error

enum-test.py:7: error: Argument 1 to "len" has incompatible type "Letters"; expected "Sized"

Running python on it gives this

2
@ilevkivskyi
Copy link
Member

This is probably related to #2305

Now that we have some support for metaclasses, both these should be not difficult to fix.
@elazarg what do you think?

@elazarg
Copy link
Contributor

elazarg commented Feb 22, 2017

Guido seems to be working on this here. I have also tried implementing it myself. len is easy; the problem is with declaring EnumMeta as an iterable, since it should be iterable over the concrete subclass, which is not declared yet. Selftype solves this nicely for the method definition, but not for the subclassing declaration. The closest I've got is

T = TypeVar('T', bound='Enum')

class EnumMeta(abc.ABCMeta, Iterable['Enum'], Sized):  # ABCMeta fixes an unrelated issue
    def __iter__(self: Type[T]) -> Iterator[T]: ...
    def __getitem__(self: Type[T], item: str) -> T: ...
    def __len__(self) -> int: ...

This gives us list(Letters) as List[Enum], which is not very helpful.

But in a way, metaclasses are generic in the concrete base class, so we should be writing something like

T = TypeVar('T', metaclass='EnumMeta')
class EnumMeta(Type[T], Iterable[T], Sized):  # Type[T] is a declaration like Generic[T]
    def __iter__(self: EnumMeta[T]) -> Iterator[T]: ...
    def __getitem__(self: EnumMeta[T], item: str) -> T: ...
    def __len__(self) -> int: ...

class Enum(metaclass=EnumMeta):
   ...

We can probably give some ad-hoc fix for protocols such as Iterable - possibly by inferring the generic parameter instead of defaulting to Any - but it would be nice to have the "right" solution somehow.

To summarize:

  • len and __getitem__ seems to be easy
  • We might need "inference" for protocols like Iterable[Subclass] since we can't express this today

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 24, 2017

__getitem__ already works (#2812). Making the iteration item type Any would be a reasonable first step to unblock the use case. It looks like inferring the item type would require some special casing in mypy.

@MeGotsThis
Copy link
Author

Also here are some other lines I just ran into:

print(Letters.A in Letters)

produces in mypy

enum-test.py:8: error: Unsupported right operand type for in ("Letters")

@MeGotsThis
Copy link
Author

Found another case

print(list(Letters))
print(list(reversed(Letters)))

produces in mypy 0.470

enum-test.py:9: error: No overload variant of "list" matches argument types [def (value: Any) -> enum-test.Letters]
enum-test.py:10: error: No overload variant of "reversed" matches argument types [def (value: Any) -> enum-test.Letters]

@gvanrossum
Copy link
Member

Fixed by #1136.

@ilevkivskyi
Copy link
Member

There is a small remainder:

No overload variant of "reversed" matches argument types [def (value: Any) -> tst22.Letters]

But it looks like a purely typeshed issue (EnumMeta should inherit from Reversible).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants