From c7a15701a5cc6533e67cd1ae58809a4b048c35b2 Mon Sep 17 00:00:00 2001 From: Clemens Wolff Date: Sun, 25 Nov 2018 22:11:23 -0500 Subject: [PATCH] Make singleton decorator explicit --- opwen_email_server/integration/azure.py | 11 ++++---- opwen_email_server/utils/collections.py | 6 ++++ opwen_email_server/utils/log.py | 8 +++--- .../utils/test_collections.py | 28 +++++++++++++++++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/opwen_email_server/integration/azure.py b/opwen_email_server/integration/azure.py index 70b54abe..127be50a 100644 --- a/opwen_email_server/integration/azure.py +++ b/opwen_email_server/integration/azure.py @@ -8,9 +8,10 @@ from opwen_email_server.services.storage import AzureObjectStorage from opwen_email_server.services.storage import AzureObjectsStorage from opwen_email_server.services.storage import AzureTextStorage +from opwen_email_server.utils.collections import singleton -@lru_cache(maxsize=1) +@singleton def get_auth() -> AzureAuth: return AzureAuth( storage=AzureTextStorage( @@ -20,7 +21,7 @@ def get_auth() -> AzureAuth: provider=config.STORAGE_PROVIDER)) -@lru_cache(maxsize=1) +@singleton def get_client_storage() -> AzureObjectsStorage: return AzureObjectsStorage( file_storage=AzureFileStorage( @@ -30,7 +31,7 @@ def get_client_storage() -> AzureObjectsStorage: provider=config.STORAGE_PROVIDER)) -@lru_cache(maxsize=1) +@singleton def get_raw_email_storage() -> AzureTextStorage: return AzureTextStorage( account=config.BLOBS_ACCOUNT, @@ -39,13 +40,13 @@ def get_raw_email_storage() -> AzureTextStorage: provider=config.STORAGE_PROVIDER) -@lru_cache(maxsize=1) +@singleton def get_email_sender() -> SendgridEmailSender: return SendgridEmailSender( key=config.SENDGRID_KEY) -@lru_cache(maxsize=1) +@singleton def get_email_storage() -> AzureObjectStorage: return AzureObjectStorage( text_storage=AzureTextStorage( diff --git a/opwen_email_server/utils/collections.py b/opwen_email_server/utils/collections.py index 9a4ed92c..1a26bae1 100644 --- a/opwen_email_server/utils/collections.py +++ b/opwen_email_server/utils/collections.py @@ -1,4 +1,6 @@ +from functools import lru_cache from itertools import islice +from typing import Callable from typing import Iterable from typing import Optional from typing import TypeVar @@ -18,3 +20,7 @@ def chunks(iterable: Iterable[T], chunk_size: int) -> Iterable[Iterable[T]]: if not chunk: return yield chunk + + +def singleton(func: Callable) -> Callable: + return lru_cache(maxsize=1)(func) diff --git a/opwen_email_server/utils/log.py b/opwen_email_server/utils/log.py index 61b12abe..98c6bf74 100644 --- a/opwen_email_server/utils/log.py +++ b/opwen_email_server/utils/log.py @@ -1,4 +1,3 @@ -from functools import lru_cache from logging import Formatter from logging import Handler from logging import Logger @@ -17,16 +16,17 @@ from opwen_email_server.constants.logging import STDERR from opwen_email_server.constants.logging import TELEMETRY_QUEUE_ITEMS from opwen_email_server.constants.logging import TELEMETRY_QUEUE_SECONDS +from opwen_email_server.utils.collections import singleton -@lru_cache(maxsize=1) +@singleton def _get_log_handlers() -> Iterable[Handler]: stderr = StreamHandler() stderr.setFormatter(Formatter(STDERR)) return [stderr] -@lru_cache(maxsize=1) +@singleton def _get_logger() -> Logger: log = getLogger() for handler in _get_log_handlers(): @@ -35,7 +35,7 @@ def _get_logger() -> Logger: return log -@lru_cache(maxsize=1) +@singleton def _get_telemetry_client() -> Optional[TelemetryClient]: if not APPINSIGHTS_KEY: return None diff --git a/tests/opwen_email_server/utils/test_collections.py b/tests/opwen_email_server/utils/test_collections.py index 5469e74b..33a839b7 100644 --- a/tests/opwen_email_server/utils/test_collections.py +++ b/tests/opwen_email_server/utils/test_collections.py @@ -1,3 +1,4 @@ +from collections import Counter from unittest import TestCase from opwen_email_server.utils import collections @@ -29,3 +30,30 @@ def test_creates_nonfull_chunks(self): chunks = collections.chunks([1, 2, 3, 4], 3) self.assertEqual(list(chunks), [(1, 2, 3), (4, )]) + + +class SingletonTests(TestCase): + def test_creates_object_only_once(self): + value1 = self.function1() + value2 = self.function1() + value3 = self.function2() + value4 = self.function2() + + self.assertIs(value1, value2) + self.assertIs(value3, value4) + self.assertIsNot(value1, value3) + self.assertEqual(self.call_counts['function1'], 1) + self.assertEqual(self.call_counts['function2'], 1) + + def setUp(self): + self.call_counts = Counter() + + @collections.singleton + def function1(self): + self.call_counts['function1'] += 1 + return 'some-value' + + @collections.singleton + def function2(self): + self.call_counts['function2'] += 1 + return 'some-other-value'