From 5be13fb066cd311c8e2cddfd6fd66c1cb1607f5f Mon Sep 17 00:00:00 2001 From: Steve Howell Date: Mon, 30 Oct 2017 16:31:47 -0700 Subject: [PATCH] Add cachify decorator. --- zerver/decorator.py | 16 +++++++- zerver/tests/test_decorators.py | 66 ++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/zerver/decorator.py b/zerver/decorator.py index eb090315a1..f11ebdfcfd 100644 --- a/zerver/decorator.py +++ b/zerver/decorator.py @@ -30,7 +30,7 @@ import logging from io import BytesIO from six.moves import zip, urllib -from typing import Union, Any, Callable, Sequence, Dict, Optional, TypeVar, Text, cast +from typing import Union, Any, Callable, Sequence, Dict, Optional, TypeVar, Text, Tuple, cast from zerver.lib.str_utils import force_bytes from zerver.lib.logging_util import create_logger @@ -74,6 +74,20 @@ def asynchronous(method): wrapper.csrf_exempt = True # type: ignore # https://github.com/JukkaL/mypy/issues/1170 return wrapper +def cachify(method): + # type: (Callable) -> Callable + dct = {} # type: Dict[Tuple, Any] + + def cache_wrapper(*args): + # type: (*Any) -> Any + tup = tuple(args) + if tup in dct: + return dct[tup] + result = method(*args) + dct[tup] = result + return result + return cache_wrapper + def update_user_activity(request, user_profile, query): # type: (HttpRequest, UserProfile, Optional[str]) -> None # update_active_status also pushes to rabbitmq, and it seems diff --git a/zerver/tests/test_decorators.py b/zerver/tests/test_decorators.py index e490f15bf0..060ce94e38 100644 --- a/zerver/tests/test_decorators.py +++ b/zerver/tests/test_decorators.py @@ -29,7 +29,7 @@ from zerver.lib.request import \ RequestVariableConversionError from zerver.decorator import ( api_key_only_webhook_view, - authenticate_notify, + authenticate_notify, cachify, get_client_name, internal_notify_view, is_local_addr, rate_limit, validate_api_key, logged_in_and_active, return_success_on_head_request @@ -1255,6 +1255,70 @@ class RestAPITest(ZulipTestCase): self.assertEqual(result.status_code, 302) self.assertTrue(result["Location"].endswith("/login/?next=/json/users")) +class CacheTestCase(ZulipTestCase): + def test_cachify_basics(self): + # type: () -> None + + @cachify + def add(w, x, y, z): + # type: (Any, Any, Any, Any) -> Any + return w + x + y + z + + for i in range(2): + self.assertEqual(add(1, 2, 4, 8), 15) + self.assertEqual(add('a', 'b', 'c', 'd'), 'abcd') + + def test_cachify_is_per_call(self): + # type: () -> None + + def test_greetings(greeting): + # type: (Text) -> Tuple[List[Text], List[Text]] + + result_log = [] # type: List[Text] + work_log = [] # type: List[Text] + + @cachify + def greet(first_name, last_name): + # type: (Text, Text) -> Text + msg = '%s %s %s' % (greeting, first_name, last_name) + work_log.append(msg) + return msg + + result_log.append(greet('alice', 'smith')) + result_log.append(greet('bob', 'barker')) + result_log.append(greet('alice', 'smith')) + result_log.append(greet('cal', 'johnson')) + + return (work_log, result_log) + + work_log, result_log = test_greetings('hello') + self.assertEqual(work_log, [ + 'hello alice smith', + 'hello bob barker', + 'hello cal johnson', + ]) + + self.assertEqual(result_log, [ + 'hello alice smith', + 'hello bob barker', + 'hello alice smith', + 'hello cal johnson', + ]) + + work_log, result_log = test_greetings('goodbye') + self.assertEqual(work_log, [ + 'goodbye alice smith', + 'goodbye bob barker', + 'goodbye cal johnson', + ]) + + self.assertEqual(result_log, [ + 'goodbye alice smith', + 'goodbye bob barker', + 'goodbye alice smith', + 'goodbye cal johnson', + ]) + class TestUserAgentParsing(ZulipTestCase): def test_user_agent_parsing(self): # type: () -> None