python-dependency-injector/dependency_injector/providers/creational.py

388 lines
10 KiB
Python

"""Dependency injector creational providers."""
from dependency_injector.providers.callable import Callable
from dependency_injector.utils import (
is_attribute_injection,
is_method_injection,
GLOBAL_LOCK,
)
from dependency_injector.errors import Error
class Factory(Callable):
""":py:class:`Factory` provider creates new instance on every call.
:py:class:`Factory` supports different syntaxes of passing injections:
.. code-block:: python
# simplified syntax for passing positional and keyword argument
# injections only:
factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4)
# extended (full) syntax for passing any type of injections:
factory = Factory(SomeClass,
injections.Arg(1),
injections.Arg(2),
injections.KwArg('some_arg', 3),
injections.KwArg('other_arg', 4),
injections.Attribute('some_attribute', 5))
Retrieving of provided instance can be performed via calling
:py:class:`Factory` object:
.. code-block:: python
factory = Factory(SomeClass,
some_arg1=1,
some_arg2=2)
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:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
provided_type = None
__slots__ = ('cls', 'attributes', 'methods')
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
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))
self.attributes = tuple(injection
for injection in args
if is_attribute_injection(injection))
self.methods = tuple(injection
for injection in args
if is_method_injection(injection))
super(Factory, self).__init__(provides, *args, **kwargs)
self.cls = self.provides
@property
def injections(self):
"""Read-only tuple of all injections.
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
"""
return self.args + self.kwargs + self.attributes + self.methods
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
"""
if self.args:
args = tuple(arg.value for arg in self.args) + args
for kwarg in self.kwargs:
if kwarg.name not in kwargs:
kwargs[kwarg.name] = kwarg.value
instance = self.provides(*args, **kwargs)
for attribute in self.attributes:
setattr(instance, attribute.name, attribute.value)
for method in self.methods:
getattr(instance, method.name)(method.value)
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:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__IS_DELEGATED__ = True
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
singleton = Singleton(SomeClass,
some_arg1=1,
some_arg2=2)
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:: instance
Read-only reference to singleton's instance.
:type: object
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__slots__ = ('instance',)
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
self.instance = None
super(Singleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.instance = None
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
"""
if self.instance:
return self.instance
with GLOBAL_LOCK:
self.instance = super(Singleton, self)._provide(*args, **kwargs)
return self.instance
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:: instance
Read-only reference to singleton's instance.
:type: object
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__IS_DELEGATED__ = True