2016-04-03 17:27:53 +03:00
|
|
|
"""Dependency injector creational providers."""
|
|
|
|
|
2016-05-18 01:17:15 +03:00
|
|
|
import six
|
|
|
|
|
2016-04-03 17:27:53 +03:00
|
|
|
from dependency_injector.providers.callable import Callable
|
2016-05-29 16:39:39 +03:00
|
|
|
from dependency_injector.providers.base import _parse_keyword_injections
|
2016-05-18 00:05:10 +03:00
|
|
|
from dependency_injector.utils import GLOBAL_LOCK
|
2016-04-03 17:27:53 +03:00
|
|
|
from dependency_injector.errors import Error
|
|
|
|
|
|
|
|
|
|
|
|
class Factory(Callable):
|
2016-05-18 01:17:15 +03:00
|
|
|
r""":py:class:`Factory` provider creates new instance on every call.
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-18 01:17:15 +03:00
|
|
|
:py:class:`Factory` supports positional & keyword argument injections,
|
2016-05-22 14:57:25 +03:00
|
|
|
as well as attribute injections.
|
|
|
|
|
|
|
|
Positional and keyword argument injections could be defined like this:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
factory = Factory(SomeClass,
|
|
|
|
'positional_arg1', 'positional_arg2',
|
|
|
|
keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
factory = Factory(SomeClass) \
|
2016-05-29 16:39:39 +03:00
|
|
|
.add_args('positional_arg1', 'positional_arg2') \
|
|
|
|
.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
2016-05-22 14:57:25 +03:00
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
factory = Factory(SomeClass)
|
2016-05-29 16:39:39 +03:00
|
|
|
factory.add_args('positional_arg1', 'positional_arg2')
|
|
|
|
factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
2016-05-22 14:57:25 +03:00
|
|
|
|
|
|
|
|
|
|
|
Attribute injections are defined by using :py:meth:`Factory.attributes`:
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2016-05-18 01:17:15 +03:00
|
|
|
factory = Factory(SomeClass) \
|
2016-05-29 16:39:39 +03:00
|
|
|
.add_attributes(attribute1=1, attribute2=2)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
Retrieving of provided instance can be performed via calling
|
|
|
|
:py:class:`Factory` object:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2016-05-18 01:17:15 +03:00
|
|
|
factory = Factory(SomeClass)
|
2016-04-03 17:27:53 +03:00
|
|
|
some_object = factory()
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, :py:class:`Factory` checks that
|
|
|
|
:py:attr:`Factory.provides` is subclass of
|
|
|
|
:py:attr:`Factory.provided_type`.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
provided_type = None
|
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
__slots__ = ('cls', 'attributes')
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-18 15:07:48 +03:00
|
|
|
def __init__(self, provides, *args, **kwargs):
|
2016-04-03 17:27:53 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Class or other callable that provides object
|
|
|
|
for creation.
|
|
|
|
:type provides: type | callable
|
|
|
|
"""
|
|
|
|
if (self.__class__.provided_type and
|
|
|
|
not issubclass(provides, self.__class__.provided_type)):
|
|
|
|
raise Error('{0} can provide only {1} instances'.format(
|
|
|
|
self.__class__, self.__class__.provided_type))
|
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
self.attributes = dict()
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-18 15:07:48 +03:00
|
|
|
super(Factory, self).__init__(provides, *args, **kwargs)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
self.cls = self.provides
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
def add_attributes(self, **kwargs):
|
2016-05-18 00:05:10 +03:00
|
|
|
"""Add attribute injections.
|
2016-05-16 11:16:14 +03:00
|
|
|
|
|
|
|
:param kwargs: Dictionary of injections.
|
|
|
|
:type kwargs: dict
|
|
|
|
|
2016-05-18 00:05:10 +03:00
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
2016-05-29 16:39:39 +03:00
|
|
|
self.attributes.update(_parse_keyword_injections(kwargs))
|
2016-05-17 22:37:50 +03:00
|
|
|
return self
|
2016-05-16 11:16:14 +03:00
|
|
|
|
2016-04-03 17:27:53 +03:00
|
|
|
def _provide(self, *args, **kwargs):
|
|
|
|
"""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
|
|
|
|
"""
|
2016-05-29 16:39:39 +03:00
|
|
|
instance = super(Factory, self)._provide(*args, **kwargs)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
for name, arg in six.iteritems(self.attributes):
|
2016-06-01 19:59:24 +03:00
|
|
|
setattr(instance, name, arg.provide_injection())
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
|
|
|
|
class DelegatedFactory(Factory):
|
|
|
|
""":py:class:`DelegatedFactory` is a delegated :py:class:`Factory`.
|
|
|
|
|
|
|
|
:py:class:`DelegatedFactory` is a :py:class:`Factory`, that is injected
|
|
|
|
"as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, :py:class:`Factory` checks that
|
|
|
|
:py:attr:`Factory.provides` is subclass of
|
|
|
|
:py:attr:`Factory.provided_type`.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
2016-06-01 19:59:24 +03:00
|
|
|
def provide_injection(self):
|
2016-05-29 16:39:39 +03:00
|
|
|
"""Injection strategy implementation.
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
|
|
|
return self
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
|
|
|
|
class Singleton(Factory):
|
|
|
|
""":py:class:`Singleton` provider returns same instance on every call.
|
|
|
|
|
|
|
|
:py:class:`Singleton` provider creates instance once and return it on every
|
|
|
|
call. :py:class:`Singleton` extends :py:class:`Factory`, 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.
|
|
|
|
|
|
|
|
Retrieving of provided instance can be performed via calling
|
|
|
|
:py:class:`Singleton` object:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2016-05-18 01:17:15 +03:00
|
|
|
singleton = Singleton(SomeClass)
|
2016-04-03 17:27:53 +03:00
|
|
|
some_object = singleton()
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, :py:class:`Factory` checks that
|
|
|
|
:py:attr:`Factory.provides` is subclass of
|
|
|
|
:py:attr:`Factory.provided_type`.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
__slots__ = ('instance',)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-18 15:07:48 +03:00
|
|
|
def __init__(self, provides, *args, **kwargs):
|
2016-04-03 17:27:53 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Class or other callable that provides object
|
|
|
|
for creation.
|
|
|
|
:type provides: type | callable
|
|
|
|
"""
|
2016-05-29 16:39:39 +03:00
|
|
|
self.instance = None
|
2016-05-18 15:07:48 +03:00
|
|
|
super(Singleton, self).__init__(provides, *args, **kwargs)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2016-05-29 16:39:39 +03:00
|
|
|
self.instance = None
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
def _provide(self, *args, **kwargs):
|
|
|
|
"""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
|
|
|
|
"""
|
2016-05-29 16:39:39 +03:00
|
|
|
if self.instance:
|
|
|
|
return self.instance
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
with GLOBAL_LOCK:
|
2016-05-29 16:39:39 +03:00
|
|
|
self.instance = super(Singleton, self)._provide(*args, **kwargs)
|
2016-04-03 17:27:53 +03:00
|
|
|
|
2016-05-29 16:39:39 +03:00
|
|
|
return self.instance
|
2016-04-03 17:27:53 +03:00
|
|
|
|
|
|
|
|
|
|
|
class DelegatedSingleton(Singleton):
|
|
|
|
""":py:class:`DelegatedSingleton` is a delegated :py:class:`Singleton`.
|
|
|
|
|
|
|
|
:py:class:`DelegatedSingleton` is a :py:class:`Singleton`, that is injected
|
|
|
|
"as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, :py:class:`Factory` checks that
|
|
|
|
:py:attr:`Factory.provides` is subclass of
|
|
|
|
:py:attr:`Factory.provided_type`.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
2016-06-01 19:59:24 +03:00
|
|
|
def provide_injection(self):
|
2016-05-29 16:39:39 +03:00
|
|
|
"""Injection strategy implementation.
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
|
|
|
return self
|