If this has been addressed elsewhere, please redirect me ![]()
From my understanding of PEP612, and the typing docs on generics, typing.Concatenate can be used to denote addition or removal of a parameter from the beginning of a signature. An example (slightly modified from the one in PEP612):
from typing import Callable, Concatenate, ParamSpec, reveal_type, Any
P = ParamSpec("P")
def bar(x: int, y: str) -> int: ...
# higher order func that removes the first parameter
def remove_first(x: Callable[Concatenate[Any, P], int]) -> Callable[P, bool]: ...
reveal_type(remove_first(bar)) # Revealed type is "def (y: str) -> bool"
I’m wondering why it wasn’t allowed to indicate removal of a paremeter at the end of the spec:
# higher order func that removes the last parameter
# ERR: Last type argument for "Concatenate" must be a ParamSpec or "...
def remove_last(x: Callable[Concatenate[P, Any], int]) -> Callable[P, bool]: ...
reveal_type(remove_last(bar)) # want this to be (x: int) -> bool
# but is actually: Revealed type is "def (*Never, **Never) -> bool
For context, I have a library that implements the observer pattern which will refrain from passing arguments that a callback cannot accept. I would have liked to do the following:
class Signal(Generic[P]):
# this decorator takes functions with the same Params
# or one arg removed from the end
def connect(
self,
callback: Callable[P, Any] | Callable[Concatenate[P, Any], Any],
) -> Callable: ...
# this signal emits up to 2 arguments, an int and string
signal: Signal[int, str] = Signal()
@signal.connect
def callback1(a: int, y: str): ... # fine
@signal.connect
def callback1(a: int): ... # ALSO fine
I suspect the answer has something to do with how this would be achieved. It’s true that I need to do signature inspection to make this work at runtime. But is that always going to be the case here? and is it never the case when removing parameters from the front of the signature? I guess I struggle a bit to see why the scenarios are so different that one was allowed and the other was not.
thanks for your insights!