python-dependency-injector/dependency_injector/providers.py

584 lines
17 KiB
Python
Raw Normal View History

"""Providers module."""
2015-01-04 17:26:33 +03:00
import six
from .injections import _parse_args_injections
from .injections import _parse_kwargs_injections
from .injections import _get_injectable_args
from .injections import _get_injectable_kwargs
2015-03-10 12:51:13 +03:00
from .utils import ensure_is_provider
from .utils import is_attribute_injection
from .utils import is_method_injection
2015-09-04 02:33:15 +03:00
from .utils import GLOBAL_LOCK
2015-01-10 12:24:25 +03:00
2015-03-13 18:31:07 +03:00
from .errors import Error
2015-01-04 17:26:33 +03:00
class Provider(object):
2015-11-23 14:48:07 +03:00
"""Base provider class.
:py:class:`Provider` is callable (implements ``__call__`` method). Every
call to provider object returns provided result, according to the providing
strategy of particular provider. This ``callable`` functionality is a
regular part of providers API and it should be the same for all provider's
subclasses.
:py:class:`Provider` implements provider overriding logic that should be
also common for all providers.
Implementation of particular providing strategy should be done in
:py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
method is called every time when not overridden provider is called.
All providers should extend this class.
"""
2015-01-04 17:26:33 +03:00
2015-07-22 10:53:16 +03:00
__IS_PROVIDER__ = True
__slots__ = ('overridden_by',)
2015-01-11 19:10:11 +03:00
def __init__(self):
2015-03-09 01:01:39 +03:00
"""Initializer."""
2015-09-14 10:53:24 +03:00
self.overridden_by = None
2015-01-11 16:03:45 +03:00
2015-01-04 17:26:33 +03:00
def __call__(self, *args, **kwargs):
2015-11-23 14:48:07 +03:00
"""Return provided instance.
Implementation of current method adds ``callable`` functionality for
providers API and it should be common for all provider's subclasses.
Also this method implements provider overriding logic that is also
common for all providers. Implementation of particular providing
strategy should be done in :py:meth:`Provider._provide` of
:py:class:`Provider` subclass.
"""
2015-09-14 10:53:24 +03:00
if self.overridden_by:
2015-05-25 10:45:52 +03:00
return self.last_overriding(*args, **kwargs)
return self._provide(*args, **kwargs)
2015-05-25 10:45:52 +03:00
def _provide(self, *args, **kwargs):
2015-05-25 10:45:52 +03:00
"""Providing strategy implementation.
Abstract protected method that implements providing strategy of
particular provider. Current method is called every time when not
overridden provider is called. Need to be overridden in subclasses.
"""
2015-01-04 17:26:33 +03:00
raise NotImplementedError()
@property
def is_overridden(self):
2015-11-23 14:48:07 +03:00
"""Read-only property that is set to ``True`` if provider is overridden.
:rtype: bool
"""
2015-09-14 10:53:24 +03:00
return bool(self.overridden_by)
@property
def last_overriding(self):
2015-11-23 14:48:07 +03:00
"""Read-only reference to the last overriding provider, if any.
:type: :py:class:`Provider`
"""
try:
2015-09-14 10:53:24 +03:00
return self.overridden_by[-1]
except (TypeError, IndexError):
raise Error('Provider {0} is not overridden'.format(str(self)))
2015-11-23 14:48:07 +03:00
def override(self, provider):
"""Override provider with another provider.
:param provider: overriding provider
:type provider: :py:class:`Provider`
:raise: :py:exc:`dependency_injector.errors.Error`
"""
if provider is self:
raise Error('Provider {0} could not be overridden '
'with itself'.format(self))
if not self.is_overridden:
self.overridden_by = (ensure_is_provider(provider),)
else:
self.overridden_by += (ensure_is_provider(provider),)
def reset_last_overriding(self):
2015-11-23 14:48:07 +03:00
"""Reset last overriding provider.
:rtype: None
"""
2015-09-14 10:53:24 +03:00
if not self.is_overridden:
raise Error('Provider {0} is not overridden'.format(str(self)))
2015-09-14 10:53:24 +03:00
self.overridden_by = self.overridden_by[:-1]
def reset_override(self):
2015-11-23 14:48:07 +03:00
"""Reset all overriding providers.
:rtype: None
"""
2015-09-14 10:53:24 +03:00
self.overridden_by = None
2015-11-23 14:48:07 +03:00
def delegate(self):
"""Return provider's delegate.
:rtype: :py:class:`Delegate`
"""
return Delegate(self)
2015-02-25 02:48:51 +03:00
2015-03-14 01:02:01 +03:00
class Delegate(Provider):
2015-03-09 01:01:39 +03:00
"""Provider's delegate."""
2015-02-25 02:48:51 +03:00
2015-09-14 10:53:24 +03:00
__slots__ = ('delegated',)
2015-02-25 02:48:51 +03:00
def __init__(self, delegated):
2015-03-09 01:01:39 +03:00
"""Initializer.
2015-02-25 02:48:51 +03:00
:type delegated: Provider
"""
2015-09-14 10:53:24 +03:00
self.delegated = ensure_is_provider(delegated)
2015-03-14 01:02:01 +03:00
super(Delegate, self).__init__()
2015-02-25 02:48:51 +03:00
def _provide(self, *args, **kwargs):
2015-11-23 14:48:07 +03:00
"""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
"""
2015-09-14 10:53:24 +03:00
return self.delegated
2015-02-23 01:16:27 +03:00
2015-01-11 16:03:45 +03:00
class Factory(Provider):
2015-11-23 14:48:07 +03:00
""":py:class:`Factory` provider creates new instance on every call.
:py:class:`Factory` supports different syntaxes of passing injections:
+ simplified one syntax for passing positional and keyword argument
injections only:
.. code-block:: python
factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4)
2015-01-04 17:26:33 +03:00
2015-11-23 14:48:07 +03:00
- extended (full) one syntax for passing any type of injections:
.. code-block:: python
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()
2015-01-04 17:26:33 +03:00
"""
__slots__ = ('provides', 'args', 'kwargs', 'attributes', 'methods')
def __init__(self, provides, *args, **kwargs):
2015-11-23 14:48:07 +03:00
"""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 not callable(provides):
raise Error('Factory provider expects to get callable, ' +
'got {0} instead'.format(str(provides)))
2015-09-14 10:53:24 +03:00
self.provides = provides
2015-11-23 14:48:07 +03:00
"""Class or other callable that provides object for creation.
:type: type | callable
"""
self.args = _parse_args_injections(args)
2015-11-23 14:48:07 +03:00
"""Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
"""
self.kwargs = _parse_kwargs_injections(args, kwargs)
2015-11-23 14:48:07 +03:00
"""Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
"""
2015-09-14 10:53:24 +03:00
self.attributes = tuple(injection
for injection in args
2015-09-14 10:53:24 +03:00
if is_attribute_injection(injection))
2015-11-23 14:48:07 +03:00
"""Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
"""
2015-09-14 10:53:24 +03:00
self.methods = tuple(injection
for injection in args
2015-09-14 10:53:24 +03:00
if is_method_injection(injection))
2015-11-23 14:48:07 +03:00
"""Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
super(Factory, self).__init__()
2015-01-04 17:26:33 +03:00
2015-11-23 14:48:07 +03:00
@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):
2015-11-23 14:48:07 +03:00
"""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
"""
instance = self.provides(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs))
2015-09-14 10:53:24 +03:00
for attribute in self.attributes:
setattr(instance, attribute.name, attribute.value)
2015-09-14 10:53:24 +03:00
for method in self.methods:
getattr(instance, method.name)(method.value)
2015-01-04 17:26:33 +03:00
2015-03-09 01:01:39 +03:00
return instance
2015-01-04 17:26:33 +03:00
class Singleton(Provider):
""":py:class:`Singleton` provider returns same instance on every call.
2015-03-09 01:01:39 +03:00
: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.
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()
2015-01-04 17:26:33 +03:00
"""
2015-09-14 10:53:24 +03:00
__slots__ = ('instance', 'factory')
def __init__(self, provides, *args, **kwargs):
2015-03-09 01:01:39 +03:00
"""Initializer."""
2015-09-14 10:53:24 +03:00
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__()
2015-01-04 17:26:33 +03:00
@property
def provides(self):
"""Class or other callable that provides object for creation.
2015-01-04 17:26:33 +03:00
: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
2015-01-28 01:21:31 +03:00
@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.
: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
2015-01-28 01:21:31 +03:00
class ExternalDependency(Provider):
"""External dependency provider.
Those provider is used when dependency obviously have to be overridden by
the client's code, but it's interface is known.
"""
2015-09-14 10:53:24 +03:00
__slots__ = ('instance_of',)
2015-03-09 01:01:39 +03:00
def __init__(self, instance_of):
"""Initializer."""
if not isinstance(instance_of, six.class_types):
2015-03-23 17:27:48 +03:00
raise Error('ExternalDependency provider expects to get class, ' +
'got {0} instead'.format(str(instance_of)))
2015-09-14 10:53:24 +03:00
self.instance_of = instance_of
super(ExternalDependency, self).__init__()
def __call__(self, *args, **kwargs):
2015-03-09 01:01:39 +03:00
"""Return provided instance."""
2015-09-14 10:53:24 +03:00
if not self.is_overridden:
raise Error('Dependency is not defined')
instance = self.last_overriding(*args, **kwargs)
2015-09-14 10:53:24 +03:00
if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) +
2015-09-14 10:53:24 +03:00
'instance of {0}'.format(self.instance_of))
return instance
def provided_by(self, provider):
"""Set external dependency provider."""
return self.override(provider)
2015-09-14 10:53:24 +03:00
class StaticProvider(Provider):
""":py:class:`StaticProvider` returns provided instance "as is".
2015-03-09 01:01:39 +03:00
:py:class:`StaticProvider` is base implementation that provides exactly
the same as it got on input.
2015-01-04 17:26:33 +03:00
"""
2015-09-14 10:53:24 +03:00
__slots__ = ('provides',)
2015-01-04 17:26:33 +03:00
def __init__(self, provides):
"""Initializer.
:param provides: value that have to be provided.
:type provides: object
"""
2015-09-14 10:53:24 +03:00
self.provides = provides
"""Value that have to be provided.
:type: object
"""
2015-09-14 10:53:24 +03:00
super(StaticProvider, self).__init__()
2015-01-04 17:26:33 +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
"""
2015-09-14 10:53:24 +03:00
return self.provides
2015-01-04 17:26:33 +03:00
2015-09-14 10:53:24 +03:00
class Class(StaticProvider):
""":py:class:`Class` returns provided class "as is".
.. code-block:: python
cls_provider = Class(object)
object_cls = cls_provider()
"""
2015-01-04 17:26:33 +03:00
2015-09-14 10:53:24 +03:00
class Object(StaticProvider):
""":py:class:`Object` returns provided object "as is".
.. code-block:: python
object_provider = Object(object())
object_instance = object_provider()
"""
2015-01-04 17:26:33 +03:00
2015-09-14 10:53:24 +03:00
class Function(StaticProvider):
""":py:class:`Function` returns provided function "as is".
.. code-block:: python
function_provider = Function(len)
len_function = function_provider()
"""
2015-01-04 17:26:33 +03:00
2015-09-14 10:53:24 +03:00
class Value(StaticProvider):
""":py:class:`Value` returns provided value "as is".
.. code-block:: python
value_provider = Value(31337)
value = value_provider()
"""
2015-01-28 01:48:33 +03:00
class Callable(Provider):
2015-03-09 01:01:39 +03:00
"""Callable provider.
Callable provider provides callable that is called on every provider call
with some predefined dependency injections.
2015-01-28 01:48:33 +03:00
"""
__slots__ = ('callback', 'args', 'kwargs')
def __init__(self, callback, *args, **kwargs):
2015-03-09 01:01:39 +03:00
"""Initializer."""
if not callable(callback):
raise Error('Callable expected, got {0}'.format(str(callback)))
2015-09-14 10:53:24 +03:00
self.callback = callback
self.args = _parse_args_injections(args)
self.kwargs = _parse_kwargs_injections(args, kwargs)
2015-01-28 01:48:33 +03:00
super(Callable, self).__init__()
def _provide(self, *args, **kwargs):
2015-03-09 01:01:39 +03:00
"""Return provided instance."""
return self.callback(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs))
2015-01-28 14:08:54 +03:00
@property
def injections(self):
"""Return tuple of all injections."""
return self.args + self.kwargs
2015-01-28 14:08:54 +03:00
class Config(Provider):
2015-03-09 01:01:39 +03:00
"""Config provider.
2015-01-28 14:08:54 +03:00
Config provider provides dict values. Also config provider creates
child config objects for all undefined attribute calls. It makes possible
to create deferred config value provider.
2015-01-28 14:08:54 +03:00
"""
2015-09-14 10:53:24 +03:00
__slots__ = ('value',)
2015-01-28 14:08:54 +03:00
def __init__(self, value=None):
2015-03-09 01:01:39 +03:00
"""Initializer."""
2015-01-28 14:08:54 +03:00
if not value:
value = dict()
2015-09-14 10:53:24 +03:00
self.value = value
2015-01-28 14:08:54 +03:00
super(Config, self).__init__()
def __getattr__(self, item):
"""Return instance of deferred config."""
2015-09-14 10:53:24 +03:00
return ChildConfig(parents=(item,), root_config=self)
def _provide(self, paths=None):
2015-03-09 01:01:39 +03:00
"""Return provided instance."""
2015-09-14 10:53:24 +03:00
value = self.value
2015-01-28 14:08:54 +03:00
if paths:
for path in paths:
try:
value = value[path]
except KeyError:
raise Error('Config key '
'"{0}" is undefined'.format('.'.join(paths)))
return value
2015-03-10 12:51:13 +03:00
2015-03-23 17:27:48 +03:00
def update_from(self, value):
"""Update current value from another one."""
2015-09-14 10:53:24 +03:00
self.value.update(value)
2015-03-23 17:27:48 +03:00
2015-03-10 12:51:13 +03:00
2015-09-14 10:53:24 +03:00
class ChildConfig(Provider):
"""Child config provider.
2015-03-10 12:51:13 +03:00
Child config provide an value from the root config object according to
the current path in the config tree.
2015-03-10 12:51:13 +03:00
"""
2015-09-14 10:53:24 +03:00
__slots__ = ('parents', 'root_config')
2015-03-16 12:41:42 +03:00
def __init__(self, parents, root_config):
2015-03-10 12:51:13 +03:00
"""Initializer."""
2015-09-14 10:53:24 +03:00
self.parents = parents
self.root_config = root_config
super(ChildConfig, self).__init__()
2015-03-10 12:51:13 +03:00
def __getattr__(self, item):
"""Return instance of deferred config."""
2015-09-14 10:53:24 +03:00
return ChildConfig(parents=self.parents + (item,),
root_config=self.root_config)
def _provide(self, *args, **kwargs):
"""Return provided instance."""
2015-09-14 10:53:24 +03:00
return self.root_config(self.parents)