Search code examples
pythonpython-3.xmypytyping

mypy infer paramspec of function based on paramspec of first input


I'm creating a function which will act as a factory for a class, here's how it will look:

T = TypeVar("T")
def app_factory(app: Type[T], *args, **kwargs) -> T:
    ...
    return app(*args, **kwargs)

class App:
    def __init__(self, a: str, b: bool):
        self.a = a
        self.b = b

app_instance = app_factory(app=App, a="string", b=True)

Would it be possible to dynamically give app_factory the typehints a: str, b: bool based on the first input arg app? In other words, is there any way to dynamically set ParamSpec of a function to be equal to one of the function input callables?


Solution

  • Yes, this is doable because type-checker implementations should recognise type[T] as a subtype of Callable[P, T], where P is the parameters type of T's __new__ or __init__.

    from __future__ import annotations
    import typing as t
    
    if t.TYPE_CHECKING:
        import collections.abc as cx
        T = t.TypeVar("T")
        P = t.ParamSpec("P")
    
    def app_factory(app: cx.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
        return app(*args, **kwargs)
    
    class App:
        def __init__(self, a: str, b: bool) -> None:
            self.a = a
            self.b = b
    
    >>> app_factory(app=App, a="string", b=True)  # OK
    >>> app_factory(app=App, a="string", b=1) # mypy: Argument "b" to "app_factory" has incompatible type "int"; expected "bool" [arg-type]