Add singleton implementation + tests for all singleton types

This commit is contained in:
Roman Mogylatov 2020-12-15 18:25:32 -05:00
parent f07f3e4943
commit 6020c6caf4
5 changed files with 5546 additions and 4567 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -141,6 +141,7 @@ cdef class FactoryAggregate(Provider):
# Singleton providers
cdef class BaseSingleton(Provider):
cdef Factory __instantiator
cdef bint __async
cdef class Singleton(BaseSingleton):

View File

@ -1941,6 +1941,7 @@ cdef class BaseSingleton(Provider):
self.__class__, self.__class__.provided_type))
self.__instantiator = Factory(provides, *args, **kwargs)
self.__async = False
super(BaseSingleton, self).__init__()
@ -2128,10 +2129,30 @@ cdef class Singleton(BaseSingleton):
cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance."""
if self.__storage is None:
self.__storage = __factory_call(self.__instantiator,
args, kwargs)
instance = __factory_call(self.__instantiator, args, kwargs)
if __isawaitable(instance):
future_result = asyncio.Future()
instance = asyncio.ensure_future(instance)
instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
self.__storage = future_result
return future_result
self.__storage = instance
if self.__async:
result = asyncio.Future()
result.set_result(self.__storage)
return result
return self.__storage
def _async_init_instance(self, future_result, result):
instance = result.result()
self.__storage = instance
self.__async = True
future_result.set_result(instance)
cdef class DelegatedSingleton(Singleton):
"""Delegated singleton is injected "as is".
@ -3488,8 +3509,20 @@ def merge_dicts(dict1, dict2):
return result
def isawaitable(obj):
"""Check if object is a coroutine function.
Return False for any object in Python 3.4 or below.
"""
try:
return inspect.isawaitable(obj)
except AttributeError:
return False
def iscoroutinefunction(obj):
"""Check if object is a coroutine function.
Return False for any object in Python 3.4 or below.
"""
try:
@ -3500,6 +3533,7 @@ def iscoroutinefunction(obj):
def isasyncgenfunction(obj):
"""Check if object is an asynchronous generator function.
Return False for any object in Python 3.4 or below.
"""
try:

View File

@ -218,3 +218,154 @@ class FactoryTests(AsyncTestCase):
self.assertIs(service2.client.resource2, RESOURCE2)
self.assertIsNot(service1.client, service2.client)
class SingletonTests(AsyncTestCase):
def test_injections(self):
class ContainerWithSingletons(containers.DeclarativeContainer):
resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
client = providers.Singleton(
Client,
resource1=resource1,
resource2=resource2,
)
service = providers.Singleton(
Service,
client=client,
)
container = ContainerWithSingletons()
client1 = self._run(container.client())
client2 = self._run(container.client())
self.assertIsInstance(client1, Client)
self.assertIs(client1.resource1, RESOURCE1)
self.assertIs(client1.resource2, RESOURCE2)
self.assertIsInstance(client2, Client)
self.assertIs(client2.resource1, RESOURCE1)
self.assertIs(client2.resource2, RESOURCE2)
service1 = self._run(container.service())
service2 = self._run(container.service())
self.assertIsInstance(service1, Service)
self.assertIsInstance(service1.client, Client)
self.assertIs(service1.client.resource1, RESOURCE1)
self.assertIs(service1.client.resource2, RESOURCE2)
self.assertIsInstance(service2, Service)
self.assertIsInstance(service2.client, Client)
self.assertIs(service2.client.resource1, RESOURCE1)
self.assertIs(service2.client.resource2, RESOURCE2)
self.assertIs(service1, service2)
self.assertIs(service1.client, service2.client)
self.assertIs(service1.client, client1)
self.assertIs(service2.client, client2)
self.assertIs(client1, client2)
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.Singleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)
class DelegatedSingletonTests(AsyncTestCase):
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.DelegatedSingleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)
class ThreadSafeSingletonTests(AsyncTestCase):
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.ThreadSafeSingleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)
class DelegatedThreadSafeSingletonTests(AsyncTestCase):
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.DelegatedThreadSafeSingleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)
class ThreadLocalSingletonTests(AsyncTestCase):
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.ThreadLocalSingleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)
class DelegatedThreadLocalSingletonTests(AsyncTestCase):
def test_async_mode(self):
instance = object()
async def create_instance():
return instance
provider = providers.DelegatedThreadLocalSingleton(create_instance)
instance1 = self._run(provider())
instance2 = self._run(provider())
self.assertIs(instance1, instance2)
self.assertIs(instance, instance)