Skip to content

len(Enum) error #2891

Closed
Closed

Description

@MeGotsThis

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

Activity

  1. ilevkivskyi commented on Feb 22, 2017

    @ilevkivskyi
    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?

  2. elazarg commented on Feb 22, 2017

    @elazarg
    Contributor

    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
  3. JukkaL commented on Feb 24, 2017

    @JukkaL
    Collaborator

    __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.

  4. MeGotsThis commented on Mar 1, 2017

    @MeGotsThis
    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")
    
  5. MeGotsThis commented on Mar 2, 2017

    @MeGotsThis
    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]
    
  6. gvanrossum commented on Apr 21, 2017

    @gvanrossum
    Member

    Fixed by #1136.

  7. ilevkivskyi commented on Apr 21, 2017

    @ilevkivskyi
    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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions