Merge remote-tracking branch 'origin/configuration_provider'

This commit is contained in:
Roman Mogilatov 2016-12-02 15:00:50 +02:00
commit 3295e979fa
6 changed files with 5108 additions and 0 deletions

View File

@ -51,6 +51,10 @@ setup(name='dependency-injector',
['src/dependency_injector/providers/base.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.configuration',
['src/dependency_injector/providers/configuration.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.callables',
['src/dependency_injector/providers/callables.c'],
define_macros=defined_macros,

View File

@ -7,6 +7,9 @@ from .base import (
ExternalDependency,
OverridingContext,
)
from .configuration import (
Configuration,
)
from .callables import (
Callable,
DelegatedCallable,
@ -48,6 +51,8 @@ __all__ = (
'ExternalDependency',
'OverridingContext',
'Configuration',
'Callable',
'DelegatedCallable',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
"""Dependency injector configuration providers.
Powered by Cython.
"""
from .base cimport (
Provider,
)
cdef class Configuration(Provider):
cdef str __name
cdef object __value
cdef dict __children
cpdef str get_name(self)
cpdef object update(self, object value)
cpdef object _provide(self, tuple args, dict kwargs)
cpdef str _get_child_name(self, str child_name)

View File

@ -0,0 +1,116 @@
"""Dependency injector configuration providers.
Powered by Cython.
"""
from .base cimport (
Provider,
)
from .utils cimport (
represent_provider,
deepcopy,
)
cdef class Configuration(Provider):
"""Configuration provider.
Configudation provider helps with implementing late static binding of
configuration options - use first, set last.
.. code-block:: python
config = Configuration('config')
print(config.section1.option1()) # None
print(config.section1.option2()) # None
config.update({'section1': {'option1': 1,
'option2': 2}})
print(config.section1.option1()) # 1
print(config.section1.option2()) # 2
"""
def __init__(self, name):
"""Initializer."""
self.__name = name
self.__value = None
self.__children = dict()
super(Configuration, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(self.__name)
copied.update(deepcopy(self.__value))
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.__name)
def __getattr__(self, name):
"""Return child configuration provider."""
cdef Configuration child_provider
cdef object value
child_provider = self.__children.get(name)
if child_provider is None:
child_provider = self.__class__(self._get_child_name(name))
if isinstance(self.__value, dict):
child_provider.update(self.__value.get(name))
self.__children[name] = child_provider
return child_provider
cpdef str get_name(self):
"""Name of configuration unit."""
return self.__name
cpdef object update(self, value):
"""Set configuration options."""
cdef Configuration child_provider
cdef object child_value
self.__value = value
if not isinstance(self.__value, dict):
return
for name in self.__value:
child_provider = self.__children.get(name)
if child_provider is None:
continue
child_provider.update(self.__value.get(name))
cpdef object _provide(self, tuple args, dict kwargs):
"""Return result of provided callable's call."""
return self.__value
cpdef str _get_child_name(self, str child_name):
cdef str child_full_name
child_full_name = ''
if self.__name:
child_full_name += self.__name + '.'
child_full_name += child_name
return child_full_name

View File

@ -0,0 +1,96 @@
"""Dependency injector config providers unit tests."""
import unittest2 as unittest
from dependency_injector import providers
class ConfigTests(unittest.TestCase):
def setUp(self):
self.config = providers.Configuration(name='config')
def tearDown(self):
del self.config
def test_providers_are_providers(self):
self.assertTrue(providers.is_provider(self.config.a))
self.assertTrue(providers.is_provider(self.config.a.b))
self.assertTrue(providers.is_provider(self.config.a.b.c))
self.assertTrue(providers.is_provider(self.config.a.b.d))
def test_providers_are_not_delegates(self):
self.assertFalse(providers.is_delegated(self.config.a))
self.assertFalse(providers.is_delegated(self.config.a.b))
self.assertFalse(providers.is_delegated(self.config.a.b.c))
self.assertFalse(providers.is_delegated(self.config.a.b.d))
def test_providers_identity(self):
self.assertIs(self.config.a, self.config.a)
self.assertIs(self.config.a.b, self.config.a.b)
self.assertIs(self.config.a.b.c, self.config.a.b.c)
self.assertIs(self.config.a.b.d, self.config.a.b.d)
def test_get_name(self):
self.assertEqual(self.config.a.b.c.get_name(), 'config.a.b.c')
def test_providers_value_setting(self):
a = self.config.a
ab = self.config.a.b
abc = self.config.a.b.c
abd = self.config.a.b.d
self.config.update({'a': {'b': {'c': 1, 'd': 2}}})
self.assertEqual(a(), {'b': {'c': 1, 'd': 2}})
self.assertEqual(ab(), {'c': 1, 'd': 2})
self.assertEqual(abc(), 1)
self.assertEqual(abd(), 2)
def test_value_of_undefined_option(self):
self.assertIsNone(self.config.a())
def test_deepcopy(self):
provider = providers.Configuration('config')
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Configuration)
def test_deepcopy_from_memo(self):
provider = providers.Configuration('config')
provider_copy_memo = providers.Configuration('config')
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_overridden(self):
provider = providers.Configuration('config')
object_provider = providers.Object(object())
provider.override(object_provider)
provider_copy = providers.deepcopy(provider)
object_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Configuration)
self.assertIsNot(object_provider, object_provider_copy)
self.assertIsInstance(object_provider_copy, providers.Object)
def test_repr(self):
self.assertEqual(repr(self.config),
'<dependency_injector.providers.configuration.'
'Configuration({0}) at {1}>'.format(
repr('config'),
hex(id(self.config))))
def test_repr_child(self):
self.assertEqual(repr(self.config.a.b.c),
'<dependency_injector.providers.configuration.'
'Configuration({0}) at {1}>'.format(
repr('config.a.b.c'),
hex(id(self.config.a.b.c))))