python / python/typing

Callable with variadic fixed arguments that doesn't mean *args

Open
#1,301 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

topic: other
Dominant language
Python
Stars
1.8k
Forks
302
Avg merge
23h
Merged PRs (30d)
8

Description

Suppose I have a type of function called CommandHandler, that is a function that receives a command as its first parameter but then could have other parameters.
Examples

class Command:
    pass

class RegisterUser(Command):
    pass

class DeleteUser(Command):
    pass

def register_user(command: RegisterUser, db : Db) -> None:
    ...

def delete_user(command: DeleteUser, dispatch: EventDispatcher, cache: Cache) -> None:
    ...

mappings : dict[Command, CommandHandler] = {
    RegisterUser: register_user,
    DeleteUser: delete_user
}

What would be the definition of CommandHandler?

Ideally it would be:

C= TypeVar("C", bound=Command)
CommandHandler = Callable[[C, ...], None] 

But this syntax is not possible.

I've found in the documentation that Concatenate could receive ... at the end,

Concatenate is currently only valid when used as the first argument to a Callable. The last parameter to Concatenate must be a ParamSpec or ellipsis (...).

so it could be:

C= TypeVar("C", bound=Command)
CommandHandler = Callable[Concatenate[C, ...], None] 

But this returns an error from Pylance:

image

One could say Callback Protocol could solve this like:

C= TypeVar("C", bound=Command)
class CommandHandler(Protocol):
    def __call__(self, __command: C, *__args: Any) -> None: ...

But it only receives functions that has an implicit variadic argument, it doesn't work with variadic forms of the function.

So far the only way I could solve this is through the Union of many Callables, but it doesn't look like the best solution.

At the end CommandHandler can only be defined as:

C= TypeVar("C", bound=Command)
CommandHandler = Union[
    Callable[[C, Any], None],
    Callable[[C, Any, Any], None],
    Callable[[C, Any, Any, Any], None],
    Callable[[C, Any, Any, Any, Any], None],
    Callable[[C, Any, Any, Any, Any, Any], None],
    Callable[[C, Any, Any, Any, Any, Any, Any], None],
    Callable[[C, Any, Any, Any, Any, Any, Any, Any], None],
    Callable[
        [C, Any, Any, Any, Any, Any, Any, Any, Any], None
    ],
]

Related:
https://github.com/python/cpython/issues/88954
https://stackoverflow.com/questions/57658879/python-type-hint-for-callable-with-variable-number-of-str-same-type-arguments

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Callable and Concatenate documentation described in the issue, then review related issue 88954 and the linked Stack Overflow discussion. Compare the requested CommandHandler forms with the current typing rules and determine whether a supported design can express them; done should include an agreed specification and corresponding documentation or implementation plan.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.