Hi all! I’m very green with the advanced use of the typing
module, so please bear with me.
I’ve seen a lot of discussion around generics, ParamSpec
, and how to copy signatures of functions to other functions, like those discussed here and here. However, it seems these solutions involve explicitly passing in the function to a decorator, or to create a TypeDict
, which I don’t think will work for my use case. Maybe my use case is not a good one and should be handled in another way (I’m all ears), but let me explain what I’m trying to accomplish.
I have a class which inherits from pydantic
’s BaseModel
from pydantic import BaseModel
class MyClass(BaseModel):
field1: int
field2: str
foo = MyClass(field1=..., field2=...)
When I go to type the last line, the type hinter (I’m using VS Code with the built-in pyright
type checker), it correctly provides field1
and field2
when I go to create the class. I know this is some magic done in pydantic
by replacing signatures (maybe only for runtime?) and with the use of the @typing.dataclass_transform
decorator on BaseModel
or its parent metaclass. I haven’t looked into the details of how the decorator works.
I am looking to build a mixin class which adds a “alternate constructor” for these classes. Example:
from pydantic import BaseModel
class MyMixin:
@classmethod
def alt_construct(cls, *args, **kwargs):
...
return cls(*args, **kwargs)
class MyClass(MyMixin, BaseModel):
field1: int
field2: str
foo = MyClass.alt_constructor(...)
Right now, if I type the last line, the signature for alt_constructor
is just *args: Any, **kwargs: Any
. I am seeking a way that I can type hint these mixin class methods with the signature of the __init__
or __call__
functions of the class new class inheriting the mixin.
I came across this comment which sounds like they’re trying to doing what I want to achieve but within the same class.
I have tried to use a decorator approach, but that requires a syntax like:
def source_func(a: int, b: str) -> float: ...
@copy_params_from(source_func)
def target_func(*args, **kwargs): ...
The source_func
where the parameters are being copied from has to be in scope. In the mixin, they are not in scope where the decorated function is written.
I have tried to write a Generic
class with a ParamSpec
, but so far none of them have resulted in the correct type hinting - all of them have been empty type hints (e.g. (...)
) or just (*args: Any, **kwargs: Any)
straight from the signature of target_func
. The closest I have gotten is a solution using typing.cast
, but this required the alternate constructor to be in the same class and have an explicitly defined __init__
function. Even then, it copied the self
parameter in addition to the others.
Is there a way that I can type hint alt_constructor
from my example MyMixin
class in a generic way that the static type hint readers can understand, and which changes depending on which class it’s mixed into?