python-dependency-injector/dependency_injector/providers.py

859 lines
24 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-12-11 12:18:09 +03:00
from .utils import represent_provider
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
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
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.
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.
2015-11-25 15:58:51 +03:00
:py:class:`Provider` implements provider overriding logic that should be
2015-11-25 16:13:27 +03:00
also common for all providers:
2015-11-25 15:58:51 +03:00
.. code-block:: python
provider1 = Factory(SomeClass)
provider2 = Factory(ChildSomeClass)
provider1.override(provider2)
some_instance = provider1()
assert isinstance(some_instance, ChildSomeClass)
Also :py:class:`Provider` implements helper function for creating its
delegates:
.. code-block:: python
provider = Factory(object)
delegate = provider.delegate()
delegated = delegate()
assert provider is delegated
2015-11-23 14:48:07 +03:00
All providers should extend this class.
2015-12-11 23:46:49 +03:00
.. py:attribute:: overridden_by
Tuple of overriding providers, if any.
:type: tuple[:py:class:`Provider`] | None
2015-11-23 14:48:07 +03:00
"""
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-11-25 15:58:51 +03:00
super(Provider, self).__init__()
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` | None
2015-11-23 14:48:07 +03:00
"""
return self.overridden_by[-1] if self.overridden_by else None
2015-11-23 14:48:07 +03:00
def override(self, provider):
"""Override provider with another provider.
2015-11-25 15:58:51 +03:00
:param provider: Overriding provider.
2015-11-23 14:48:07 +03:00
: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.
2015-11-25 16:13:27 +03:00
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
overridden.
2015-11-23 14:48:07 +03:00
:rtype: None
"""
if not self.overridden_by:
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-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
2015-02-25 02:48:51 +03:00
2015-12-11 12:18:09 +03:00
:rtype: str
"""
return represent_provider(provider=self, provides=None)
__repr__ = __str__
@six.python_2_unicode_compatible
2015-03-14 01:02:01 +03:00
class Delegate(Provider):
2015-11-25 15:58:51 +03:00
""":py:class:`Delegate` provider delegates another provider.
.. code-block:: python
provider = Factory(object)
delegate = Delegate(provider)
delegated = delegate()
assert provider is delegated
2015-12-11 23:46:49 +03:00
.. py:attribute:: delegated
Delegated provider.
:type: :py:class:`Provider`
2015-11-25 15:58:51 +03:00
"""
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
2015-11-25 15:58:51 +03:00
:provider delegated: Delegated provider.
:type delegated: :py:class:`Provider`
2015-02-25 02:48:51 +03:00
"""
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.
2015-11-25 15:58:51 +03:00
:param args: Tuple of context positional arguments.
2015-11-23 14:48:07 +03:00
:type args: tuple[object]
2015-11-25 15:58:51 +03:00
:param kwargs: Dictionary of context keyword arguments.
2015-11-23 14:48:07 +03:00
: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-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.delegated)
__repr__ = __str__
2015-01-11 16:03:45 +03:00
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
2015-12-11 23:46:49 +03:00
class Callable(Provider):
""":py:class:`Callable` provider calls wrapped callable on every call.
:py:class:`Callable` provider provides callable that is called on every
provider call with some predefined dependency injections.
:py:class:`Callable` syntax of passing injections is the same like
:py:class:`Factory` one:
.. code-block:: python
# simplified syntax for passing positional and keyword argument
# injections:
some_function = Callable(some_function, 'arg1', 'arg2', arg3=3, arg4=4)
# extended (full) syntax for passing positional and keyword argument
# injections:
some_function = Callable(some_function,
injections.Arg(1),
injections.Arg(2),
injections.KwArg('some_arg', 3),
injections.KwArg('other_arg', 4))
.. py:attribute:: provides
Provided callable.
:type: callable
.. 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`]
"""
__slots__ = ('provides', 'args', 'kwargs')
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Wrapped callable.
:type provides: callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
if not callable(provides):
raise Error('Provider{0} expected to get callable, '
'got {0}'.format('.'.join((self.__class__.__module__,
self.__class__.__name__)),
provides))
self.provides = provides
self.args = _parse_args_injections(args)
self.kwargs = _parse_kwargs_injections(args, kwargs)
super(Callable, self).__init__()
@property
def injections(self):
"""Read-only tuple of all injections.
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
"""
return self.args + self.kwargs
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
"""
return self.provides(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs))
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.provides)
__repr__ = __str__
class Factory(Callable):
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:
.. code-block:: python
2015-11-24 11:33:20 +03:00
# simplified syntax for passing positional and keyword argument
# injections only:
2015-11-23 14:48:07 +03:00
factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4)
2015-01-04 17:26:33 +03:00
2015-11-24 11:33:20 +03:00
# extended (full) syntax for passing any type of injections:
2015-11-23 14:48:07 +03:00
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
2015-12-11 23:46:49 +03:00
.. 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`.
2015-12-11 23:46:49 +03:00
:type: type | None
2015-12-11 23:46:49 +03:00
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. 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`]
"""
2015-12-11 23:46:49 +03:00
provided_type = None
__slots__ = ('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
"""
2015-12-11 23:46:49 +03:00
if (self.__class__.provided_type and
not issubclass(provides, self.__class__.provided_type)):
raise Error('{0} can provide only {1} instances'.format(
'.'.join((self.__class__.__module__,
self.__class__.__name__)),
self.__class__.provided_type))
2015-11-23 14:48:07 +03:00
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
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
2015-12-11 23:46:49 +03:00
super(Factory, self).__init__(provides, *args, **kwargs)
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.
2015-11-25 15:58:51 +03:00
:param args: Tuple of context positional arguments.
2015-11-23 14:48:07 +03:00
:type args: tuple[object]
2015-11-25 15:58:51 +03:00
:param kwargs: Dictionary of context keyword arguments.
2015-11-23 14:48:07 +03:00
:type kwargs: dict[str, object]
:rtype: object
"""
2015-12-11 23:46:49 +03:00
instance = super(Factory, self)._provide(*args, **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
2015-12-11 23:46:49 +03:00
class Singleton(Factory):
""":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
2015-12-11 23:46:49 +03:00
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()
2015-12-11 23:46:49 +03:00
.. py:attribute:: provided_type
2015-01-04 17:26:33 +03:00
2015-12-11 23:46:49 +03:00
If provided type is defined, :py:class:`Factory` checks that
:py:attr:`Factory.provides` is subclass of
:py:attr:`Factory.provided_type`.
2015-12-11 23:46:49 +03:00
:type: type | None
2015-12-11 23:46:49 +03:00
.. py:attribute:: instance
2015-12-11 23:46:49 +03:00
Read-only reference to singleton's instance.
:type: object
2015-12-11 23:46:49 +03:00
.. py:attribute:: provides
2015-01-04 17:26:33 +03:00
2015-12-11 23:46:49 +03:00
Class or other callable that provides object.
2015-01-04 17:26:33 +03:00
:type: type | callable
2015-12-11 23:46:49 +03:00
.. py:attribute:: args
2015-12-11 23:46:49 +03:00
Tuple of positional argument injections.
2015-12-11 23:46:49 +03:00
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
2015-12-11 23:46:49 +03:00
.. py:attribute:: kwargs
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
Tuple of keyword argument injections.
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
.. py:attribute:: attributes
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
Tuple of attribute injections.
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
.. py:attribute:: methods
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
Tuple of method injections.
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
:type: tuple[:py:class:`dependency_injector.injections.Method`]
2015-12-11 12:18:09 +03:00
"""
2015-12-11 23:46:49 +03:00
__slots__ = ('instance',)
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
def __init__(self, provides, *args, **kwargs):
2015-12-11 12:18:09 +03:00
"""Initializer.
2015-12-11 23:46:49 +03:00
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
2015-12-11 12:18:09 +03:00
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
2015-12-11 23:46:49 +03:00
self.instance = None
super(Singleton, self).__init__(provides, *args, **kwargs)
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
def reset(self):
"""Reset cached instance, if any.
2015-12-11 12:18:09 +03:00
2015-12-11 23:46:49 +03:00
:rtype: None
2015-12-11 12:18:09 +03:00
"""
2015-12-11 23:46:49 +03:00
self.instance = None
2015-12-11 12:18:09 +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-12-11 23:46:49 +03:00
with GLOBAL_LOCK:
if not self.instance:
self.instance = super(Singleton, self)._provide(*args,
**kwargs)
return self.instance
2015-12-11 12:18:09 +03:00
2015-01-28 01:21:31 +03:00
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
class ExternalDependency(Provider):
""":py:class:`ExternalDependency` provider describes dependency interface.
This provider is used for description of dependency interface. That might
be useful when dependency could be provided in the client's code only,
but it's interface is known. Such situations could happen when required
dependency has non-determenistic list of dependencies itself.
.. code-block:: python
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
database_provider.override(Factory(sqlite3.connect, ':memory:'))
database = database_provider()
2015-12-11 23:46:49 +03:00
.. py:attribute:: instance_of
Class of required dependency.
:type: type
"""
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):
"""Return provided instance.
2015-11-25 15:58:51 +03:00
:param args: Tuple of context positional arguments.
:type args: tuple[object]
2015-11-25 15:58:51 +03:00
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: object
"""
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.
2015-11-25 15:58:51 +03:00
:param provider: Provider that provides required dependency.
:type provider: :py:class:`Provider`
:rtype: None
"""
return self.override(provider)
2015-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.instance_of)
__repr__ = __str__
2015-03-09 01:01:39 +03:00
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
class Static(Provider):
""":py:class:`Static` provider returns provided instance "as is".
:py:class:`Static` provider is base implementation that provides exactly
the same as it got on input.
2015-12-11 23:46:49 +03:00
.. py:attribute:: provides
Value that have to be provided.
:type: object
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
2015-12-11 12:18:09 +03:00
super(Static, self).__init__()
2015-01-04 17:26:33 +03:00
def _provide(self, *args, **kwargs):
"""Return provided instance.
2015-11-25 15:58:51 +03:00
:param args: Tuple of context positional arguments.
:type args: tuple[object]
2015-11-25 15:58:51 +03:00
: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-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.provides)
__repr__ = __str__
StaticProvider = Static
# Backward compatibility for versions < 1.11.1
2015-01-04 17:26:33 +03:00
2015-12-11 12:18:09 +03:00
class Class(Static):
""":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-12-11 12:18:09 +03:00
class Object(Static):
""":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-12-11 12:18:09 +03:00
class Function(Static):
""":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-12-11 12:18:09 +03:00
class Value(Static):
""":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
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
2015-01-28 14:08:54 +03:00
class Config(Provider):
2015-11-23 20:01:14 +03:00
""":py:class:`Config` provider provide dict values.
2015-03-09 01:01:39 +03:00
2015-11-23 20:01:14 +03:00
:py:class:`Config` provider creates :py:class:`ChildConfig` objects for all
undefined attribute calls. It makes possible to create deferred config
value providers. It might be useful in cases where it is needed to
define / pass some configuration in declarative manner, while
configuration values will be loaded / updated in application's runtime.
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-11-23 20:01:14 +03:00
"""Initializer.
:param value: Configuration dictionary.
:type value: dict[str, object]
"""
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):
2015-11-23 20:01:14 +03:00
"""Return instance of deferred config.
2015-11-25 15:58:51 +03:00
:param item: Name of configuration option or section.
2015-11-23 20:01:14 +03:00
:type item: str
:rtype: :py:class:`ChildConfig`
"""
2015-09-14 10:53:24 +03:00
return ChildConfig(parents=(item,), root_config=self)
def _provide(self, paths=None):
2015-11-23 20:01:14 +03:00
"""Return provided instance.
2015-11-25 15:58:51 +03:00
:param paths: Tuple of pieces of configuration option / section path.
2015-11-23 20:01:14 +03:00
:type args: tuple[str]
:rtype: object
"""
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):
2015-11-23 20:01:14 +03:00
"""Update current value from another one.
:param value: Configuration dictionary.
:type value: dict[str, object]
:rtype: None
"""
2015-09-14 10:53:24 +03:00
self.value.update(value)
2015-03-23 17:27:48 +03:00
2015-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.value)
__repr__ = __str__
2015-03-10 12:51:13 +03:00
2015-12-11 12:18:09 +03:00
@six.python_2_unicode_compatible
2015-09-14 10:53:24 +03:00
class ChildConfig(Provider):
2015-11-23 20:01:14 +03:00
""":py:class:`ChildConfig` provider provides value from :py:class:`Config`.
2015-03-10 12:51:13 +03:00
2015-11-23 20:01:14 +03:00
:py:class:`ChildConfig` provides 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-11-23 20:01:14 +03:00
"""Initializer.
:param parents: Tuple of pieces of configuration option / section
parent path.
:type parents: tuple[str]
:param root_config: Root configuration object.
:type root_config: :py:class:`Config`
"""
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):
2015-11-23 20:01:14 +03:00
"""Return instance of deferred config.
2015-11-25 15:58:51 +03:00
:param item: Name of configuration option or section.
2015-11-23 20:01:14 +03:00
:type item: str
:rtype: :py:class:`ChildConfig`
"""
2015-09-14 10:53:24 +03:00
return ChildConfig(parents=self.parents + (item,),
root_config=self.root_config)
def _provide(self, *args, **kwargs):
2015-11-23 20:01:14 +03:00
"""Return provided instance.
2015-11-25 15:58:51 +03:00
:param args: Tuple of context positional arguments.
2015-11-23 20:01:14 +03:00
:type args: tuple[object]
2015-11-25 15:58:51 +03:00
:param kwargs: Dictionary of context keyword arguments.
2015-11-23 20:01:14 +03:00
:type kwargs: dict[str, object]
:rtype: object
"""
2015-09-14 10:53:24 +03:00
return self.root_config(self.parents)
2015-12-11 12:18:09 +03:00
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self,
provides='.'.join(self.parents))
__repr__ = __str__