Concatenate adds parameters to the beginning of a ParamSpec.
1from typing import ParamSpec, TypeVar, Callable, Concatenate23P = ParamSpec("P")4R = TypeVar("R")56# Add Request parameter to function7def with_request(8 func: Callable[Concatenate[Request, P], R]9) -> Callable[P, R]:10 def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:11 request = get_current_request()12 return func(request, *args, **kwargs)13 return wrapper1415@with_request16def get_user(request: Request, user_id: int) -> User:17 return User.objects.get(id=user_id)1819# Request is injected, type checker knows!20user = get_user(42) # OK — (int) -> User2122# With logger23import logging2425def with_logger(26 func: Callable[Concatenate[logging.Logger, P], R]27) -> Callable[P, R]:28 def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:29 logger = logging.getLogger(func.__module__)30 return func(logger, *args, **kwargs)31 return wrapper3233@with_logger34def process(logger: logging.Logger, data: str) -> None:35 logger.info(f"Processing {data}")3637process("hello") # Logger is injected!
Benefits: