class A:
    def test(self, value: int, *args: str, **kwargs: int) -> str:
        pass 
class B(A)
    def test(self, value, *args, **kwargs):
        # do some stuff
        super().test(value)
Is there a way to tell mypy that the subclass's test has identical typing as the parent class?
I ask this because some the typing of some methods I'm inheriting require a lot of imports. One example would be requests.Session.get. If I'm just writing a wrapper that manipulates the headers before sending everything to the proper function, how can I tell mypy to just consider the annotations of the parent class for a specific function?
As pointed out by @SUTerliakov in the comments, there is currently no directly supported way to annotate a child method with the same annotations as the overridden method of the parent class.
However, with the introduction of typing.ParamSpec in Python 3.10, a viable workaround would be to define a wrapper function that uses the parameter specifications of the parent function object to annotate an inner wrapper function that actually calls the parent function after doing "some stuff" with the arguments, and returns the inner wrapper function to make it an actual method of the child class:
from typing import TypeVar, ParamSpec
from collections.abc import Callable
T = TypeVar('T')
P = ParamSpec('P')
class A:
    def test(self, value: int, *args: str, **kwargs: int) -> str:
        print('base method with:', self, value, args, kwargs)
        return ' '.join(map(str, (value, args, kwargs)))
    
class B(A):
    @staticmethod
    def inherit(f: Callable[P, T]) -> Callable[P, T]:
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
            print('do something in wrapper with:', args, kwargs)
            return f(*args, **kwargs)
        return wrapper
    test = inherit(A.test)
print(B().test(1, 'foo', 'bar', a=2))
Demo of the code passing mypy: https://mypy-play.net/?mypy=latest&python=3.11&gist=828b3ba31c5c4979ff46ae2b2213dbc4
Demo of the code running on repl.it: https://replit.com/@blhsing/ThriftyZealousCustomer
Demo of type hints in PyCharm:

If you don't want to define B.test under an if not typing.TYPE_CHECKING block, it's easy enough to make a no-op decorator factory to take over any signature you need:
import collections.abc as cx
import typing as t
P = t.ParamSpec("P")
R = t.TypeVar("R")
def withSignatureFrom(
    f: cx.Callable[P, R]
) -> cx.Callable[[cx.Callable[..., object]], cx.Callable[P, R]]:
    return lambda _: _  # type: ignore[return-value]
class A:
    def test(self, value: int, *args: str, **kwargs: int) -> str:
        return ""
class B(A):
    @withSignatureFrom(A.test)
    def test(self, value, *args, **kwargs):
        # do some stuff
        return super().test(value)
>>> B().test("")  # mypy: Argument 1 to "test" of "B" has incompatible type "str"; expected "int" [arg-type]
This only handles instance methods though - you'll need to define other @typing.overloads  for @withSignatureFrom if you want to handle @staticmethods and @classmethods.
If you're running mypy in --strict mode, you'll have to also decide what to do with untyped definitions; e.g. by setting the configuration disallow_untyped_defs = False.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With