Open mvaled opened 2 years ago
I don't think the sample above (or the one in your gist) should generate a type error. There's a valid solution for TypeVar M
for both Carrier
calls at the bottom of the sample. In the first of the two calls, the M
is Foo
. In the second M
is Foo | Bar
or object
.
If you want the second call to Carrier
to produce an error, you would need to define M
as a constrained TypeVar rather than a bound TypeVar.
M = TypeVar("M", "Foo", "Bar")
That's probably correct. My first intuition was to look for something like Instance[T]
(for some T
bounded to a meta-class). This is the (pseudo) code I was asking about in the gitter thread:
import enum
from dataclasses import dataclass
from typing import TypeVar, Generic, Instance
M = TypeVar('M', bound='Meta')
class Meta(enum.EnumMeta):
pass
class Foo(enum.Enum, metaclass=Meta):
item1 = "1"
item2 = "2"
class Bar(enum.Enum, metaclass=Meta):
item1 = "1"
item2 = "2"
@dataclass
class Carrier(Generic[M]):
meta: M
instance: Instance[M]
Carrier(Foo, Foo.item1)
Carrier(Foo, Bar.item1) # should fail
The difference here is that M
is bound to the metaclass Meta
but then I would like to refer to some instance of M
.
Yeah, there's no such thing as Instance
in the type system today. (It's generally not needed because you can specify Type[M]
to specify that you're talking about the instantiable class rather than an instance of that class.) And as you probably know, bound
works only parent/child class relationships, not with metaclasses.
Does my suggestion to use a constrained TypeVar meet your needs?
The actual code is a little bit more complicated; there are methods in the common MyEnum
base with a signature similar to:
@classmethod
def convert(cls: Type[M], ...) -> M:
...
And have M
being constrained to the subclasses doesn't work. I will have to look at it a little more closely.
Bug Report
Trying to type-check generic types of a bound
enum.Enum
(with a custom metaclass) only catches type errors when the actual code fails to run; but doesn't detect the issues when the code runs.To Reproduce
The code in this gist properly detects the type error while calling
Carrier(Foo, Bar.item1)
:However that code fails to run with
In order for the code to run we have to swap the positions of
enum.Enum
andMyEnum
in bothFoo
andBar
(see the second gist); but then mypy fails to catch the type-error.Your Environment
mypy.ini
(and other config files): noneLinux pavla 5.15.12-1-MANJARO #1 SMP PREEMPT Wed Dec 29 18:08:07 UTC 2021 x86_64 GNU/Linux
Previous discussion in gitter: https://gitter.im/python/typing?at=61dab43cf5a3947800f73b71