Use ParamSpec in a few places (#12667)

This commit is contained in:
David Robertson 2022-05-09 11:27:39 +01:00 committed by GitHub
parent c5969b346d
commit fa0eab9c8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 148 additions and 68 deletions

View file

@ -15,10 +15,10 @@
import logging
from functools import wraps
from types import TracebackType
from typing import Any, Callable, Optional, Type, TypeVar, cast
from typing import Awaitable, Callable, Optional, Type, TypeVar
from prometheus_client import Counter
from typing_extensions import Protocol
from typing_extensions import Concatenate, ParamSpec, Protocol
from synapse.logging.context import (
ContextResourceUsage,
@ -72,16 +72,21 @@ in_flight: InFlightGauge[_InFlightMetric] = InFlightGauge(
)
T = TypeVar("T", bound=Callable[..., Any])
P = ParamSpec("P")
R = TypeVar("R")
class HasClock(Protocol):
clock: Clock
def measure_func(name: Optional[str] = None) -> Callable[[T], T]:
"""
Used to decorate an async function with a `Measure` context manager.
def measure_func(
name: Optional[str] = None,
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]:
"""Decorate an async method with a `Measure` context manager.
The Measure is created using `self.clock`; it should only be used to decorate
methods in classes defining an instance-level `clock` attribute.
Usage:
@ -97,18 +102,24 @@ def measure_func(name: Optional[str] = None) -> Callable[[T], T]:
"""
def wrapper(func: T) -> T:
def wrapper(
func: Callable[Concatenate[HasClock, P], Awaitable[R]]
) -> Callable[P, Awaitable[R]]:
block_name = func.__name__ if name is None else name
@wraps(func)
async def measured_func(self: HasClock, *args: Any, **kwargs: Any) -> Any:
async def measured_func(self: HasClock, *args: P.args, **kwargs: P.kwargs) -> R:
with Measure(self.clock, block_name):
r = await func(self, *args, **kwargs)
return r
return cast(T, measured_func)
# There are some shenanigans here, because we're decorating a method but
# explicitly making use of the `self` parameter. The key thing here is that the
# return type within the return type for `measure_func` itself describes how the
# decorated function will be called.
return measured_func # type: ignore[return-value]
return wrapper
return wrapper # type: ignore[return-value]
class Measure: