From 1fb9ce763ccba27411e0444f6f848f2e9a3e241d Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 12 Dec 2022 12:29:56 +0000 Subject: [PATCH 1/5] Add a cached helper to the module API --- synapse/module_api/__init__.py | 36 ++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index 96a661177abd..f3f39acee60b 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -25,7 +25,7 @@ Optional, Tuple, TypeVar, - Union, + Union, Collection, ) import attr @@ -126,7 +126,7 @@ ) from synapse.util import Clock from synapse.util.async_helpers import maybe_awaitable -from synapse.util.caches.descriptors import CachedFunction, cached +from synapse.util.caches.descriptors import CachedFunction, cached as _cached from synapse.util.frozenutils import freeze if TYPE_CHECKING: @@ -185,6 +185,38 @@ class UserIpAndAgent: last_seen: int +def cached( + *, + max_entries: int = 1000, + num_args: Optional[int] = None, + uncached_args: Optional[Collection[str]] = None, +): + """Returns a decorator that caches the value of the decorated function for a given + set of arguments. This decorator behaves similarly to functools.lru_cache. + + Example: + + @cached() + def foo('a', 'b'): + ... + + Args: + max_entries: The maximum number of entries in the cache. If the cache is full + and a new entry is added, the least recently accessed entry will be evicted + from the cache. + num_args: The number of positional arguments (excluding `self`) to use as cache + keys. Defaults to all named args of the function. + uncached_args: A list of argument names to not use as the cache key. (`self` is + always ignored.) Cannot be used with num_args. + + """ + return _cached( + max_entries=max_entries, + num_args=num_args, + uncached_args=uncached_args, + ) + + class ModuleApi: """A proxy object that gets passed to various plugin modules so they can register new users etc if necessary. From af960e6e7575a7d2aeb7660fa805cb51725b72c9 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 12 Dec 2022 12:34:57 +0000 Subject: [PATCH 2/5] Changelog --- changelog.d/14663.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/14663.feature diff --git a/changelog.d/14663.feature b/changelog.d/14663.feature new file mode 100644 index 000000000000..b03f3ee54e33 --- /dev/null +++ b/changelog.d/14663.feature @@ -0,0 +1 @@ +Add a `cached` function to `synapse.module_api` that returns a decorator to cache return values of functions. From 6014bd77d21711af9380a3f653072a69867bf1a6 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 12 Dec 2022 12:37:17 +0000 Subject: [PATCH 3/5] Add version number --- synapse/module_api/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index f3f39acee60b..4f9617914ad4 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -200,6 +200,8 @@ def cached( def foo('a', 'b'): ... + Added in Synapse v1.74.0. + Args: max_entries: The maximum number of entries in the cache. If the cache is full and a new entry is added, the least recently accessed entry will be evicted From d4e304afd45e804403ce193a1eaf6841d0252e81 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 12 Dec 2022 12:38:55 +0000 Subject: [PATCH 4/5] Improve docstring --- synapse/module_api/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index 4f9617914ad4..aacf71823ebc 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -191,8 +191,8 @@ def cached( num_args: Optional[int] = None, uncached_args: Optional[Collection[str]] = None, ): - """Returns a decorator that caches the value of the decorated function for a given - set of arguments. This decorator behaves similarly to functools.lru_cache. + """Returns a decorator that applies a memoizing cache around the function. This + decorator behaves similarly to functools.lru_cache. Example: @@ -211,6 +211,8 @@ def foo('a', 'b'): uncached_args: A list of argument names to not use as the cache key. (`self` is always ignored.) Cannot be used with num_args. + Returns: + A decorator that applies a memoizing cache around the function. """ return _cached( max_entries=max_entries, From 7c23e3bd12dbb938f69f4a5fcbd9544d1480e817 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 12 Dec 2022 12:54:56 +0000 Subject: [PATCH 5/5] Lint --- synapse/module_api/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index aacf71823ebc..683289d9ca59 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -18,6 +18,7 @@ TYPE_CHECKING, Any, Callable, + Collection, Dict, Generator, Iterable, @@ -25,7 +26,7 @@ Optional, Tuple, TypeVar, - Union, Collection, + Union, ) import attr @@ -136,6 +137,7 @@ T = TypeVar("T") P = ParamSpec("P") +F = TypeVar("F", bound=Callable[..., Any]) """ This package defines the 'stable' API which can be used by extension modules which @@ -190,7 +192,7 @@ def cached( max_entries: int = 1000, num_args: Optional[int] = None, uncached_args: Optional[Collection[str]] = None, -): +) -> Callable[[F], CachedFunction[F]]: """Returns a decorator that applies a memoizing cache around the function. This decorator behaves similarly to functools.lru_cache.