Skip to content

Partial Type Specifications For CallableΒ #696

Closed
@rmorshea

Description

@rmorshea

The Feature Request

It would be awesome if it were possible to create a partial type spec for Callable. For example, I might want to be able to specify that the first argument of a function must be an integer, but that any other parameters are allowed. This could be accomplished with the following extension to PEP-484 which would allow ... to be included in the argument list to Callable if it followed any extended callable types. As a result we could write a type spec FirstArgIsInt which would match all the following functions and any number of other arbitrary functions so long as their first argument is an integer:

FirstArgIsInt = Callable[[Arg(int), ...], int]

f1: FirstArgIsInt
def f1(x: int) -> int: ...

f2: FirstArgIsInt
def f2(x: int, y: int) -> int: ...

f3: FirstArgIsInt
def f3(x: int, *args, **kwargs) -> int: ...

A Real World Use Case

Partial type specifications for Callable are useful if parameters are being passed on to a function from a decorator as in request handlers for many web frameworks like Django's view funtions or Sanic's routes:

def route_1(request: Request) -> Response: ...
def route_2(request: Request, username: str) -> Response: ...
def route_3(request: Request, comment_id: int, content: str) -> Response: ...
# adinfinitum...

Imagine that you wanted to write an authenticated decorator which ensures a user who accesses any particular route is logged in. What would be the type of RouteHandler if not Callable[..., Response]:

RouteHandler = ?

def authenticated(r: RouteHandler) -> RouteHandler: ...

It would be Callable[[Arg(Request), ...], Any], or some other expression of the same idea.

Current Work Arounds

  1. Callable[..., Response], however this provides no precision for the function parameters.
  2. Use Callable[[Arg(Request), VarArg(), KwArg()], Response] however this is problematic since, these route handlers don't actually need to be able to accept arbitrary arguments.
  3. Use Protocol with overloads, however there are cases (e.g. decorators for route handlers as above) where it's not feasible to enumerate all possible route handler implementations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions