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 # Singleton providers
cdef class BaseSingleton(Provider): cdef class BaseSingleton(Provider):
cdef Factory __instantiator cdef Factory __instantiator
cdef bint __async
cdef class Singleton(BaseSingleton): cdef class Singleton(BaseSingleton):

View File

@ -1941,6 +1941,7 @@ cdef class BaseSingleton(Provider):
self.__class__, self.__class__.provided_type)) self.__class__, self.__class__.provided_type))
self.__instantiator = Factory(provides, *args, **kwargs) self.__instantiator = Factory(provides, *args, **kwargs)
self.__async = False
super(BaseSingleton, self).__init__() super(BaseSingleton, self).__init__()
@ -2128,10 +2129,30 @@ cdef class Singleton(BaseSingleton):
cpdef object _provide(self, tuple args, dict kwargs): cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance.""" """Return single instance."""
if self.__storage is None: if self.__storage is None:
self.__storage = __factory_call(self.__instantiator, instance = __factory_call(self.__instantiator, args, kwargs)
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 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): cdef class DelegatedSingleton(Singleton):
"""Delegated singleton is injected "as is". """Delegated singleton is injected "as is".
@ -3488,8 +3509,20 @@ def merge_dicts(dict1, dict2):
return result 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): def iscoroutinefunction(obj):
"""Check if object is a coroutine function. """Check if object is a coroutine function.
Return False for any object in Python 3.4 or below. Return False for any object in Python 3.4 or below.
""" """
try: try:
@ -3500,6 +3533,7 @@ def iscoroutinefunction(obj):
def isasyncgenfunction(obj): def isasyncgenfunction(obj):
"""Check if object is an asynchronous generator function. """Check if object is an asynchronous generator function.
Return False for any object in Python 3.4 or below. Return False for any object in Python 3.4 or below.
""" """
try: try:

View File

@ -218,3 +218,154 @@ class FactoryTests(AsyncTestCase):
self.assertIs(service2.client.resource2, RESOURCE2) self.assertIs(service2.client.resource2, RESOURCE2)
self.assertIsNot(service1.client, service2.client) 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)