diff --git a/dependency_injector/providers.py b/dependency_injector/providers.py index 00f9a024..f5a9055e 100644 --- a/dependency_injector/providers.py +++ b/dependency_injector/providers.py @@ -268,9 +268,15 @@ class Factory(Provider): class Singleton(Provider): - """Singleton provider. + """:py:class:`Singleton` provider returns same instance on every call. - Singleton provider will create instance once and return it on every call. + :py:class:`Singleton` provider creates instance once and return it on every + call. :py:class:`Singleton` uses :py:class:`Factory` for creation of + instance, so, please follow :py:class:`Factory` documentation to go inside + with injections syntax. + + :py:class:`Singleton` is thread-safe and could be used in multithreading + environment without any negative impact. """ __slots__ = ('instance', 'factory') @@ -278,25 +284,90 @@ class Singleton(Provider): def __init__(self, provides, *args, **kwargs): """Initializer.""" self.instance = None + """Read-only reference to singleton's instance. + + :type: object + """ + self.factory = Factory(provides, *args, **kwargs) + """Singleton's factory object. + + :type: :py:class:`Factory` + """ + super(Singleton, self).__init__() + @property + def provides(self): + """Class or other callable that provides object for creation. + + :type: type | callable + """ + return self.factory.provides + + @property + def args(self): + """Tuple of positional argument injections. + + :type: tuple[:py:class:`dependency_injector.injections.Arg`] + """ + return self.factory.args + + @property + def kwargs(self): + """Tuple of keyword argument injections. + + :type: tuple[:py:class:`dependency_injector.injections.KwArg`] + """ + return self.factory.kwargs + + @property + def attributes(self): + """Tuple of attribute injections. + + :type: tuple[:py:class:`dependency_injector.injections.Attribute`] + """ + return self.factory.attributes + + @property + def methods(self): + """Tuple of method injections. + + :type: tuple[:py:class:`dependency_injector.injections.Method`] + """ + return self.factory.methods + + @property + def injections(self): + """Read-only tuple of all injections. + + :rtype: tuple[:py:class:`dependency_injector.injections.Injection`] + """ + return self.factory.injections + + def reset(self): + """Reset cached instance, if any. + + :rtype: None + """ + self.instance = None + def _provide(self, *args, **kwargs): - """Return provided instance.""" + """Return provided instance. + + :param args: tuple of context positional arguments + :type args: tuple[object] + + :param kwargs: dictionary of context keyword arguments + :type kwargs: dict[str, object] + + :rtype: object + """ with GLOBAL_LOCK: if not self.instance: self.instance = self.factory(*args, **kwargs) return self.instance - def reset(self): - """Reset instance.""" - self.instance = None - - @property - def injections(self): - """Return tuple of all injections.""" - return self.factory.injections - class ExternalDependency(Provider): """External dependency provider. diff --git a/docs/images/providers/singleton.png b/docs/images/providers/singleton.png index 595d359f..3cc3f598 100644 Binary files a/docs/images/providers/singleton.png and b/docs/images/providers/singleton.png differ diff --git a/docs/images/providers/singleton_internals.png b/docs/images/providers/singleton_internals.png index f05fb902..a327f390 100644 Binary files a/docs/images/providers/singleton_internals.png and b/docs/images/providers/singleton_internals.png differ diff --git a/docs/providers/singleton.rst b/docs/providers/singleton.rst index c372e50d..70f575c7 100644 --- a/docs/providers/singleton.rst +++ b/docs/providers/singleton.rst @@ -1,8 +1,10 @@ Singleton providers ------------------- -``di.Singleton`` provider creates new instance of specified class on first call -and returns same instance on every next call. +.. module:: dependency_injector.providers + +:py:class:`Singleton` provider creates new instance of specified class on +first call and returns same instance on every next call. Example: @@ -16,9 +18,9 @@ Example: Singleton providers and injections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``di.Singleton`` providers use ``di.Factory`` providers for first creation of -specified class instance, so, all of the rules about injections are the same, -as for ``di.Factory`` providers. +:py:class:`Singleton` providers use :py:class:`Factory` providers for first +creation of specified class instance, so, all of the rules about injections +are the same, as for :py:class:`Factory` providers. .. image:: /images/providers/singleton_internals.png :width: 80% @@ -26,29 +28,29 @@ as for ``di.Factory`` providers. .. note:: - Due that ``di.Singleton`` provider creates specified class instance only on - the first call, all injections are done once, during the first call, also. - Every next call, while instance has been already created and memorized, no - injections are done, ``di.Singleton`` provider just returns memorized - earlier instance. + Due that :py:class:`Singleton` provider creates specified class instance + only on the first call, all injections are done once, during the first + call, also. Every next call, while instance has been already created + and memorized, no injections are done, :py:class:`Singleton` provider just + returns memorized earlier instance. This may cause some problems, for example, in case of trying to bind - ``di.Factory`` provider with ``di.Singleton`` provider (provided by - dependent ``di.Factory`` instance will be injected only once, during the - first call). Be aware that such behaviour was made with opened eyes and is - not a bug. + :py:class:`Factory` provider with :py:class:`Singleton` provider (provided + by dependent :py:class:`Factory` instance will be injected only once, + during the first call). Be aware that such behaviour was made with opened + eyes and is not a bug. - By the way, in such case, ``di.Delegate`` provider can be useful. It makes - possible to inject providers *as is*. Please check out full example in - *Providers delegation* section. + By the way, in such case, :py:class:`Delegate` provider can be useful. It + makes possible to inject providers *as is*. Please check out full example + in *Providers delegation* section. Singleton providers resetting ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Created and memorized by ``di.Singleton`` instance can be reset. Reset of -``di.Singleton``'s memorized instance is done by clearing reference to it. -Further lifecycle of memorized instance is out of ``di.Singleton`` provider's -control. +Created and memorized by :py:class:`Singleton` instance can be reset. Reset of +:py:class:`Singleton`'s memorized instance is done by clearing reference to +it. Further lifecycle of memorized instance is out of :py:class:`Singleton` +provider's control. Example: @@ -58,13 +60,13 @@ Example: Singleton providers delegation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``di.Singleton`` provider could be delegated to any other provider via any -kind of injection. Delegation of ``di.Singleton`` providers is the same as -``di.Factory`` providers delegation, please follow -*Factory providers delegation* section for example. +:py:class:`Singleton` provider could be delegated to any other provider via +any kind of injection. Delegation of :py:class:`Singleton` providers is the +same as :py:class:`Factory` providers delegation, please follow *Factory +providers delegation* section for example. -``di.Singleton`` delegate could be created obviously using -``di.Delegate(di.Singleton())`` or by calling ``di.Singleton.delegate()`` +:py:class:`Singleton` delegate could be created obviously using +``Delegate(Singleton(...))`` or by calling ``Singleton(...).delegate()`` method. Example: diff --git a/examples/providers/singleton.py b/examples/providers/singleton.py index 963d6c14..73b736ec 100644 --- a/examples/providers/singleton.py +++ b/examples/providers/singleton.py @@ -1,6 +1,6 @@ -"""`di.Singleton` providers example.""" +"""`Singleton` providers example.""" -import dependency_injector as di +from dependency_injector import providers class UserService(object): @@ -8,7 +8,7 @@ class UserService(object): # Singleton provider creates new instance of specified class on first call and # returns same instance on every next call. -users_service_provider = di.Singleton(UserService) +users_service_provider = providers.Singleton(UserService) # Retrieving several UserService objects: user_service1 = users_service_provider() diff --git a/examples/providers/singleton_delegation.py b/examples/providers/singleton_delegation.py index ebc9aa2e..f2342ad2 100644 --- a/examples/providers/singleton_delegation.py +++ b/examples/providers/singleton_delegation.py @@ -1,12 +1,12 @@ -"""`di.Singleton` providers delegation example.""" +"""`Singleton` providers delegation example.""" -import dependency_injector as di +from dependency_injector import providers # Some singleton provider and few delegates of it: -singleton_provider = di.Singleton(object) +singleton_provider = providers.Singleton(object) singleton_provider_delegate1 = singleton_provider.delegate() -singleton_provider_delegate2 = di.Delegate(singleton_provider) +singleton_provider_delegate2 = providers.Delegate(singleton_provider) # Making some asserts: assert singleton_provider_delegate1() is singleton_provider diff --git a/examples/providers/singleton_reseting.py b/examples/providers/singleton_reseting.py index cdc9e078..19c9b238 100644 --- a/examples/providers/singleton_reseting.py +++ b/examples/providers/singleton_reseting.py @@ -1,13 +1,13 @@ -"""`di.Singleton` providers resetting example.""" +"""`Singleton` providers resetting example.""" -import dependency_injector as di +from dependency_injector import providers class UserService(object): """Example class UserService.""" # Users service singleton provider: -users_service_provider = di.Singleton(UserService) +users_service_provider = providers.Singleton(UserService) # Retrieving several UserService objects: user_service1 = users_service_provider()