Description
Consider the following code from my hypothetical ORM that has three different ways of building an object
from typing import TypeVar, Type
T = TypeVar('T')
class ModelMeta(type):
def build(cls: Type[T]) -> T:
return cls()
def build_other(cls):
return cls()
class Model(metaclass=ModelMeta):
@classmethod
def new(cls: Type[T]) -> T:
return cls()
I'm having trouble defining build
in a way that type checks. In this example, I get an error on build
about how Type[T]
is not a supertype of the class ModelMeta
.
build_other
passes the type checker, but usages don't seem to catch anything (implicit Any perhaps?)
class Dog(Model):
def __init__(self):
print("Built Dog")
class Cat(Model):
def __init__(self):
print("Built Cat")
c: Cat = Dog.new() # type check fails
c2: Cat = Dog.build() # type check fails
c3: Cat = Dog.build_other() # passes
Considering class methods work, it feels like metaclass methods should equally be able to "unwrap" the instance type for a class, but there might be subtleties I'm missing here.
My wish list here would be:
- have the inverse of
Type
(something likeInstance
) so I could write something likedef build(cls: T) -> Instance[T]
. This would be (I think) a bit clearer. - Have an intersection type, with which I could declare something like
def build(cls: (Type[T] & ModelMeta)) -> T
. This would help with a lot of other problems as well. This seems like it would be a useful shortcut to writing subclasses - Have a magic hook in
subtypes.is_subtype
that would let me write a custom subtype check in some types
Full context: I'm writing some stubs for Django's ORM, and was hitting issues around the type of Model.objects.create
. So in my case I end up needing a "class property" (hence doing things through the metaclass), which is why the simple new()
call strategy hasn't been working for me.
I think this is related to #3438, but I'm having a hard time identifying the link.