Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type-hinting Callable with positional/keyword only arguments without typing.Protocol

I'm wondering if it is possible to type-hint a Callable with positional- / keyword-only argument without using typing.Protocol.

For example in this snippet - how would I type-hint x correctly so that add is assignable to it:

from typing import Callable

def add(arg: int, /, *, other: str) -> str:
    return f"{arg}{other}"

x: Callable[[int, str], str] = add
# Type Error:
# `(arg: int, /, *, other: str) -> str` is not assignable to `(int, str) -> str`
like image 379
Clue Avatar asked Dec 06 '25 03:12

Clue


1 Answers

In short no. A combination is (currently) not possible, as keyword only parameters are not possible with Callable, as it describes positional-only parameters - you need a Protocol for more specific typing. To quote the specs:

Parameters specified using Callable are assumed to be positional-only. The Callable form provides no way to specify keyword-only parameters, variadic parameters, or default argument values. For these use cases, see the section on Callback protocols.


A bit more on assignability; a function with standard parameters (keyword or positional) can be assigned to a function with any parameter type.
The reverse, if you have a function that is keyword-only / positional-only it can only be assigned to a matching type, i.e. in your case you need a Protocol that reflects these parameter types exactly.

from typing import Callable, Protocol

class Standard(Protocol):
    def __call__(self, a: int) -> None: ...

class KwOnly(Protocol):
    def __call__(self, *, a: int) -> None: ...

class PosOnly(Protocol):
    def __call__(self, a: int, /) -> None: ...

CallableType = Callable[[int], None]

def func(standard: Standard, kw_only: KwOnly, callable: CallableType, pos_only: PosOnly):
    # Standard assignable to all
    f1a: KwOnly = standard  # OK
    f1b: CallableType = standard # OK
    f1c: PosOnly = standard # OK

    # Keyword-Only assignable to keyword-only
    f2a: Standard = kw_only  # error
    f2b: CallableType = kw_only  # error
    f2c: PosOnly = kw_only # error

    # CallableType and PosOnly are equivalent; only assignable to position-only/Callable
    f3a: Standard = callable  # error
    f3b: KwOnly = callable # error
    f3c: PosOnly = callable # OK - as equivalent

    f4a: Standard = pos_only  # error
    f4b: KwOnly = pos_only # error
    f4c: CallableType = pos_only # OK - as equivalent

I am not aware that there are any plans to change extend Callable in its behavior, e.g. accept keyword-only via TypedDicts.

like image 124
Daraan Avatar answered Dec 08 '25 17:12

Daraan