objectif
Préserver la signature des fonctions décorées avec ParamSpec.
code minimal
from typing import TypeVar, ParamSpec, Callable
from functools import wraps
P = ParamSpec("P")
R = TypeVar("R")
def log_calls(fn: Callable[P, R]) -> Callable[P, R]:
@wraps(fn)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
return fn(*args, **kwargs)
return wrapper
@log_calls
def add(x:int, y:int) -> int: return x+y
print(add(1,2) == 3) # attendu: True
utilisation
from typing import ParamSpec, TypeVar, Callable
from functools import wraps
P = ParamSpec("P"); R = TypeVar("R")
def ident(fn: Callable[P, R]) -> Callable[P, R]:
@wraps(fn)
def w(*a: P.args, **k: P.kwargs) -> R: return fn(*a, **k)
return w
@ident
def f(a:int, b:str="x") -> str: return b* a
print(f(2) == "xx" and f.__name__ == "f")
variante(s) utile(s)
from typing import Concatenate, Callable, ParamSpec, TypeVar
P = ParamSpec("P"); R = TypeVar("R")
def add_ctx(fn: Callable[Concatenate[str, P], R]) -> Callable[Concatenate[str, P], R]:
def w(ctx: str, *a: P.args, **k: P.kwargs) -> R: return fn(ctx, *a, **k)
return w
@add_ctx
def greet(ctx: str, name: str) -> str: return f"{ctx}:{name}"
print(greet("hi","bob") == "hi:bob")
notes
- ParamSpec préserve args/kwargs des fonctions décorées.
- Utilisez wraps pour garder nom/docstring/signature.