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

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

Closed
bmerry opened this issue Apr 8, 2022 · 3 comments · Fixed by #14554
Closed

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

bmerry opened this issue Apr 8, 2022 · 3 comments · Fixed by #14554

Comments

@bmerry
Copy link
Contributor

bmerry commented Apr 8, 2022

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
@bmerry bmerry added the bug mypy got something wrong label Apr 8, 2022
@AlexWaygood
Copy link
Member

AlexWaygood commented Apr 9, 2022

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)

@AlexWaygood
Copy link
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>]"

@bmerry
Copy link
Contributor Author

bmerry commented Apr 11, 2022

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

hauntsaninja pushed a commit that referenced this issue Jan 29, 2023
Fixes #12553

This looks quite niche, but also it was mentioned recently couple times
for a real-life use case: enum classes, and implementation looks simple.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants