Skip to content

iter doesn't work on TypeVar with bound of Enum #12553

Closed

Description

@bmerry

Bug Report

A function that takes an argument of Type[_E] where _E = TypeVar("_E", bound=Enum) does not accept calling iter on that argument. Calling iter on a concrete enum class is accepted, which is why I think this isn't purely a typeshed issue. Using the class in a loop or calling the __iter__ member is also accepted.

To Reproduce

Run mypy on the following:

#!/usr/bin/env python3

import socket
from enum import Enum
from typing import Iterator, Type, TypeVar

_E = TypeVar("_E", bound=Enum)

def enum_iter(cls: Type[_E]):
    return iter(cls)

for value in enum_iter(socket.SocketKind):
    print(value)

Expected Behavior

Expected no mypy errors.

Actual Behavior

mypy_enum_iter.py:10: error: No overload variant of "iter" matches argument type "Type[_E]"
mypy_enum_iter.py:10: note: Possible overload variants:
mypy_enum_iter.py:10: note:     def [_SupportsNextT <: SupportsNext[Any]] iter(_SupportsIter[_SupportsNextT]) -> _SupportsNextT
mypy_enum_iter.py:10: note:     def [_T] iter(Callable[[], Optional[_T]], None) -> Iterator[_T]
mypy_enum_iter.py:10: note:     def [_T] iter(Callable[[], _T], object) -> Iterator[_T]
Found 1 error in 1 file (checked 1 source file)

What's even weirder is that adding an Iterator[_E] return annotation to enum_iter makes it pass.

Your Environment

  • Mypy version used: 0.942
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.8.10
  • Operating system and version: Ubuntu 20.04

Activity

  1. AlexWaygood commented on Apr 9, 2022

    @AlexWaygood
    Member

    I think this is a repro that doesn't involve enums, revealing that the issue is a general one with mypy's understanding of metaclasses rather than an issue specific to the enum plugin:

    from typing import Iterator, TypeVar
    
    M = TypeVar("M")
    
    class Meta(type):
        def __iter__(self: type[M]) -> Iterator[M]: ...
    
    class Foo(metaclass=Meta): ...
    
    F = TypeVar("F", bound=Foo)
    
    def foo_iter(cls: type[F]):
        return iter(cls)

    Mypy output:

    main.py:13: error: No overload variant of "iter" matches argument type "Type[F]"
    main.py:13: note: Possible overload variants:
    main.py:13: note:     def [_SupportsNextT <: SupportsNext[Any]] iter(__iterable, _SupportsIter[_SupportsNextT]) -> _SupportsNextT
    main.py:13: note:     def [_T] iter(__function, Callable[[], Optional[_T]], None) -> Iterator[_T]
    main.py:13: note:     def [_T] iter(__function, Callable[[], _T], object) -> Iterator[_T]
    Found 2 errors in 1 file (checked 1 source file)
    
  2. AlexWaygood commented on Apr 10, 2022

    @AlexWaygood
    Member

    It probably doesn't even have anything to do with TypeVars. I can reproduce a similar error with this snippet:

    from typing import Iterator, TypeVar
    
    M = TypeVar("M")
    
    class Meta(type):
        def __iter__(self: type[M]) -> Iterator[M]: ...
    
    class Foo(metaclass=Meta): ...
    
    def foo_iter(cls: type[Foo]):
        return iter(cls)  # error: Argument 1 to "iter" has incompatible type "Type[Foo]"; expected "_SupportsIter[<nothing>]"
  3. bmerry commented on Apr 11, 2022

    @bmerry
    ContributorAuthor

    I guess a good question is whether subclasses of Foo are expected to have a metaclass that is a subclass (in the Liskov substitution sense) of Meta. If not, then the error would be legitimate since cls could be an instance of a subclass of Foo whose metaclass doesn't provide __iter__.

  4. added a commit that references this issue on Jan 29, 2023
    8af3af3
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