'In Python, how do you annotate a function that accepts a fixed argument as well as any number of other arguments?

In Python, from my understanding, the ellipsis lets you annotate a function that has any number of arguments (documentation found here). Here's an example of what I'd like to do:

from typing import Callable, Any

def foo(first: str, *rest: Any):
    print(rest)
    return first

def call(f: Callable[[str, ...], str]):
    f("Hello", 1, None, True)

print(call(foo))

Python (or, at least, Pylance) doesn't like the ellipsis ("..." not allowed in this context): Sceenshot of Pylance hint

I've tried to use Python 3.10's ParamSpecs, but the documentation on them (including PEP 612) seems to say they're used for other purposes, and I can't tell what those purposes are. Here's what I've tried:

from typing import Any, Callable, Concatenate, ParamSpec

P = ParamSpec("P")

def foo(first: str, *rest: Any):
    print(rest)
    return first

def call(f: Callable[Concatenate[str, P], str]):
    f("Hello", 1, None, True)

print(call(foo))

Python (or, at least, Pylance) seems to reflect that they aren't meant to be used this way: Screenshot of Pylance hint

How do I annotate a function like this, that knows the type of one or more of its arguments and accepts any number of other arguments anyway?



Solution 1:[1]

You can type arbitrary function signatures using __call__ on a Protocol

class Foo(Protocol):
    def __call__(self, first: str, *rest: Any) -> str:
        ...

def call(f: Foo):
    f("Hello", 1, None, True)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1