Typing kwargs with typing.Unpack

PEP-0692 gave us a way to typehint **kwargs using a typing.TypedDict but this requires you to:

  1. Write a TypedDict that’s matches the signature of the function we want to pass the kwargs to
  2. Keep it in sync if said function gains or loses a kwarg in future

Is there no way to “automatically” copy the function signature?

Essentially, I’m looking for something like this:

from typing import Unpack

from httpx import Client, Response


def my_wrapper(foo: str, **kwargs: Unpack[Client]) -> Response:

    with Client(**kwargs) as session:
        response = session.get(...)
    
    return response

httpx.Client() takes lots of arguments, it’s not gonna be fun to write all of that in a TypedDict

Client kwargs
    def __init__(
        self,
        *,
        auth: AuthTypes | None = None,
        params: QueryParamTypes | None = None,
        headers: HeaderTypes | None = None,
        cookies: CookieTypes | None = None,
        verify: VerifyTypes = True,
        cert: CertTypes | None = None,
        http1: bool = True,
        http2: bool = False,
        proxy: ProxyTypes | None = None,
        proxies: ProxiesTypes | None = None,
        mounts: None | (typing.Mapping[str, BaseTransport | None]) = None,
        timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
        follow_redirects: bool = False,
        limits: Limits = DEFAULT_LIMITS,
        max_redirects: int = DEFAULT_MAX_REDIRECTS,
        event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None,
        base_url: URLTypes = "",
        transport: BaseTransport | None = None,
        app: typing.Callable[..., typing.Any] | None = None,
        trust_env: bool = True,
        default_encoding: str | typing.Callable[[bytes], str] = "utf-8",
    ) -> None:

Here’s me trying to get it close with a TypedDict

TypedDict
from typing import TYPE_CHECKING, Any, Callable, Mapping, TypedDict

if TYPE_CHECKING:
    from httpx import BaseTransport, Limits
    from httpx._client import EventHook
    from httpx._types import (
        AuthTypes,
        CertTypes,
        CookieTypes,
        HeaderTypes,
        ProxiesTypes,
        ProxyTypes,
        QueryParamTypes,
        TimeoutTypes,
        URLTypes,
        VerifyTypes,
    )

# Note, I can't even match it 1:1 because TypedDicts don't support
# right hand-side values used to indicate the default
class HTTPXClientKwargs(TypedDict):
    auth: AuthTypes | None
    params: QueryParamTypes | None
    headers: HeaderTypes | None
    cookies: CookieTypes | None
    verify: VerifyTypes
    cert: CertTypes | None
    http1: bool
    http2: bool
    proxy: ProxyTypes | None
    proxies: ProxiesTypes | None
    mounts: None | (Mapping[str, BaseTransport | None])
    timeout: TimeoutTypes
    follow_redirects: bool
    limits: Limits
    max_redirects: int
    event_hooks: None | (Mapping[str, list[EventHook]])
    base_url: URLTypes
    transport: BaseTransport | None
    app: Callable[..., Any] | None
    trust_env: bool
    default_encoding: str | Callable[[bytes], str]

Issues:

  • Super verbose
  • Duping code
  • Can’t match it 1:1 because typedDicts don’t support right hand side values used to indicate the default in function signature

Perhaps taking them from the parameters attribute of the output of inspect.signature.