Skip to content

Instantly share code, notes, and snippets.

@catwell
Last active February 28, 2025 08:38
Show Gist options
  • Save catwell/ca31fcd5e13f61bd6142d363b18df833 to your computer and use it in GitHub Desktop.
Save catwell/ca31fcd5e13f61bd6142d363b18df833 to your computer and use it in GitHub Desktop.
ParamSpec + Concatenate + higher-order function wrapper
# See https://julien.danjou.info/p/the-hidden-cost-of-badly-typed-python
# Simple version (hardcoded types)
from collections.abc import Callable
from typing import Any, Concatenate, ParamSpec
P = ParamSpec("P")
def send_request(client: "HttpClient", method: str = "GET", timeout: int = 5) -> str:
return f"Request sent to {client.url} with method {method} and timeout {timeout}s"
class HttpClient:
def __init__(self, url: str):
self.url = url
def _make_request(f: Callable[Concatenate[HttpClient, P], str]) -> Callable[Concatenate[str, P], str]:
def g(url: str, *args: Any, **kwargs: Any) -> str:
return f(HttpClient(url), *args, **kwargs)
return g
make_request = _make_request(send_request)
# See https://julien.danjou.info/p/the-hidden-cost-of-badly-typed-python
# Generic version
from collections.abc import Callable
from typing import Any, Concatenate
def send_request(client: "HttpClient", method: str = "GET", timeout: int = 5) -> str:
return f"Request sent to {client.url} with method {method} and timeout {timeout}s"
class HttpClient:
def __init__(self, url: str):
self.url = url
def _replace_first_arg[**P, Tin, Tout, Tres](
wrapped: Callable[Concatenate[Tout, P], Tres],
project: Callable[[Tin], Tout],
) -> Callable[Concatenate[Tin, P], Tres]:
def wrapper(a: Tin, *args: Any, **kwargs: Any) -> Tres:
return wrapped(project(a), *args, **kwargs)
return wrapper
make_request = _replace_first_arg(send_request, HttpClient)
@catwell
Copy link
Author

catwell commented Feb 28, 2025

It works with both pyright and mypy:

Screenshot From 2025-02-28 09-38-34

Screenshot From 2025-02-28 09-37-32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment