16

Imagine I have a set of functions like below. foo has a lot of arguments of various types, and bar passes all its arguments to that other function. Is there any way to make mypy understand that bar has the same type as foo without explicitly copying the whole argument list?

def foo(a: int, b: float, c: str, d: bool, *e: str, f: str = "a", g: str = "b") -> str:
    ...

def bar(*args, **kwargs):
    val = foo(*args, **kwargs)
    ...
    return val
Kyuuhachi
  • 651
  • 6
  • 15
  • This is covered in [PEP-612](https://www.python.org/dev/peps/pep-0612/) (`ParamSpec`). Support for mypy is still outstanding (see [mypy#8645](https://github.com/python/mypy/issues/8645)). – Danielle Madeley Dec 24 '20 at 00:55
  • @DanielleMadeley [#8645](https://github.com/python/mypy/issues/8645) as been closed. I couldn't figure out how to apply this to the use case in this question. Can you explain? – Feuermurmel Jun 28 '23 at 12:30
  • I could have sworn there was an example in the PEP. It's not that different to the accepted answer really. You need to make yourself an identity decorator that goes `Callable[P, T] -> Callable[P, T]` where `P = ParamSpec('P'); T = TypeVar('T')`. See https://stackoverflow.com/questions/71968447/python-typing-copy-kwargs-from-one-function-to-another has an example answer. – Danielle Madeley Jun 29 '23 at 23:39

1 Answers1

12

There's been a lot of discussion about adding this feature here. For the straightforward case of passing all arguments you can use the recipe from this comment:

F = TypeVar('F', bound=Callable[..., Any])

class copy_signature(Generic[F]):
    def __init__(self, target: F) -> None: ...
    def __call__(self, wrapped: Callable[..., Any]) -> F: ...

def f(x: bool, *extra: int) -> str: ...

@copy_signature(f)
def test(*args, **kwargs):
    return f(*args, **kwargs)

reveal_type(test)  # Revealed type is 'def (x: bool, *extra: int) -> str'
Alex Hall
  • 34,833
  • 5
  • 57
  • 89
  • 1
    This works, with a few drawbacks: `--strict` still reports that the parameters are untyped, and the return type of `__init__` is `None`, so copying that doesn't quite work. – Kyuuhachi Jan 13 '20 at 14:15
  • If you could add a comment about that to the issue, that'd be great. – Alex Hall Jan 13 '20 at 14:18