-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Passing enum directly as iterable loses type specificity #3210
Comments
Confirmed, and this is not entirely limited to Enum. It seems to have to do with an iterable metaclass. Here's a simpler example: from typing import *
T = TypeVar('T', bound='E')
class EMeta(type, Iterable['E']):
def __iter__(self: Type[T]) -> Iterator[T]: ...
class E(metaclass=EMeta):
pass
class C(E):
pass
reveal_type(list(c for c in C)) # list[C]
reveal_type(list(C)) # list[E] @elazarg Do you have an idea? (IIRC you added metaclass support?) |
I think There was a brief discussion about this in some other issue/PR; part of the the problem is the absence of syntax for declaring the actual intention; so protocols may help, but I did not yet experiment with the combination of protocols, self types and metaclasses though. |
Why would the context for I don't think there's special-casing for @ilevkivskyi This smells familiar -- does it ring a bell? |
Something is picking the metaclass, looking at the declaration: The way I understand it, the absence of special casing is the source of the issue here. The type |
Ah, I think you're right and now I understand the issue. I have no solution alas. |
It also has nothing to do with metaclasses: from typing import TypeVar
T = TypeVar('T', bound='A')
class A(Iterable['A']):
def __iter__(self: T) -> Iterator[T]: ...
class B(A): pass
reveal_type(list(b for b in B())) # list[B]
reveal_type(list(B())) # list[A] |
Protocols solve this problem if I remove the explicit In principle my PR plays nicely with self types and metaclasses, mainly I just reuse |
Well, I think this kind of things worth separate issues and PRs. Maybe you can whitelist |
Or maybe an "infer" parameter will do this whitelisting. |
@elazarg Yes, we could play with this. The general plan is to make a "smooth" transition. Initially, only mypy protocols PR will be (hopefully) merged, but typeshed classes will stay nominal, then we could gradually replace nominal classes with corresponding protocols, then we could maybe even switch to structural first for some classes. |
@elazarg please go ahead and create the separate issues you see necessary. |
Seems like it's getting worse with protocols (regression?): $ cat tmp.py
from enum import Enum
class Colors(Enum):
RED = 0
BLUE = 1
reveal_type(frozenset(c for c in Colors)) # line 7
reveal_type(frozenset(Colors)) # line 8
$ mypy tmp.py
tmp.py:7: error: Revealed type is 'builtins.frozenset[Any]'
tmp.py:7: error: Iterable expected
tmp.py:7: error: "Colors" has no attribute "__iter__"
tmp.py:8: error: Revealed type is 'builtins.frozenset[<uninhabited>]'
tmp.py:8: error: Argument 1 to "frozenset" has incompatible type "Colors"; expected Iterable[<uninhabited>] @ilevkivsky is this the constraints problem you talked about? |
@elazarg Strange, I just installed from master and I just get this: tst23.py:7: error: Revealed type is 'builtins.frozenset[tst23.Colors*]'
tst23.py:8: error: Revealed type is 'builtins.frozenset[enum.Enum*]' (which is the same as before) |
It's likely there's a problem on my side (I don't know what it is, but I'm using a new machine) - sorry about that. |
My bad indeed. Thanks @ilevkivskyi.
|
It looks like if you pass an Enum class directly to a generic that wants an iterable (eg
list
,frozenset
) it will forget which type of Enum you have. However if you make a no-op iterable out of it first, the enum type is preserved:The last line here should have type
frozenset[Colors*]
rather thanfrozenset[Enum*]
The text was updated successfully, but these errors were encountered: