Refactor configuration provider

This commit is contained in:
Roman Mogylatov 2018-01-24 19:58:03 +02:00
parent 86a8efa294
commit e877b33fd1
5 changed files with 3344 additions and 2479 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -71,16 +71,10 @@ cdef class CallableDelegate(Delegate):
# Configuration providers
cdef class Configuration(Provider):
cdef class Configuration(Object):
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)
# Factory providers
cdef class Factory(Provider):

View File

@ -823,7 +823,7 @@ cdef class CallableDelegate(Delegate):
super(Delegate, self).__init__(callable)
cdef class Configuration(Provider):
cdef class Configuration(Object):
"""Configuration provider.
Configuration provider helps with implementing late static binding of
@ -836,7 +836,7 @@ cdef class Configuration(Provider):
print(config.section1.option1()) # None
print(config.section1.option2()) # None
config.update({'section1': {'option1': 1,
config.override({'section1': {'option1': 1,
'option2': 2}})
print(config.section1.option1()) # 1
@ -852,12 +852,10 @@ cdef class Configuration(Provider):
:param default: Default values of configuration unit.
:type default: dict
"""
super(Configuration, self).__init__(default)
self.__name = name
self.__value = None
self.__children = dict()
if default is not None:
self.update(default)
super(Configuration, self).__init__()
self.__children = self._create_children(default)
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
@ -868,7 +866,7 @@ cdef class Configuration(Provider):
return copied
copied = self.__class__(self.__name)
copied.__value = deepcopy(self.__value, memo)
copied.__provides = deepcopy(self.__provides, memo)
copied.__children = deepcopy(self.__children, memo)
self._copy_overridings(copied)
@ -884,9 +882,6 @@ cdef class Configuration(Provider):
def __getattr__(self, str name):
"""Return child configuration provider."""
cdef Configuration child_provider
cdef object value
if name.startswith('__') and name.endswith('__'):
raise AttributeError(
'\'{cls}\' object has no attribute '
@ -896,55 +891,103 @@ cdef class Configuration(Provider):
child_provider = self.__children.get(name)
if child_provider is None:
child_provider = self.__class__(self._get_child_name(name))
child_name = self._get_child_full_name(name)
child_provider = self.__class__(child_name)
if isinstance(self.__value, dict):
child_provider.update(self.__value.get(name))
value = self.__call__()
if isinstance(value, dict):
child_value = value.get(name)
child_provider.override(child_value)
self.__children[name] = child_provider
return child_provider
cpdef str get_name(self):
def get_name(self):
"""Name of configuration unit."""
return self.__name
cpdef object update(self, value):
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`
:return: Overriding context.
:rtype: :py:class:`OverridingContext`
"""
overriding_context = super(Configuration, self).override(provider)
value = self.__call__()
if not isinstance(value, dict):
return
for name in value:
child_provider = self.__children.get(name)
if child_provider is None:
continue
child_provider.override(value.get(name))
return overriding_context
def reset_last_overriding(self):
"""Reset last overriding provider.
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
overridden.
:rtype: None
"""
for child in self.__children.values():
try:
child.reset_last_overriding()
except Error:
pass
super(Configuration, self).reset_last_overriding()
def reset_override(self):
"""Reset all overriding providers.
:rtype: None
"""
for child in self.__children.values():
child.reset_override()
super(Configuration, self).reset_override()
def update(self, value):
"""Set configuration options.
.. deprecated:: 3.11
Use :py:meth:`Configuration.override` instead.
:param value: Value of configuration option.
:type value: object | dict
:rtype: None
"""
cdef Configuration child_provider
cdef object child_value
self.override(value)
self.__value = value
def _create_children(self, value):
children = dict()
if not isinstance(self.__value, dict):
return
if not isinstance(value, dict):
return children
for name in self.__value:
child_provider = self.__children.get(name)
for child_name, child_value in value.items():
child_full_name = self._get_child_full_name(child_name)
child_provider = self.__class__(child_full_name, child_value)
children[child_name] = child_provider
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
return children
def _get_child_full_name(self, child_name):
child_full_name = ''
if self.__name:
child_full_name += self.__name + '.'
child_full_name += child_name
return child_full_name

View File

@ -60,8 +60,33 @@ class ConfigTests(unittest.TestCase):
self.assertEqual(abc(), 1)
self.assertEqual(abd(), 2)
def test_providers_with_default_value(self):
def test_providers_value_override(self):
a = self.config.a
ab = self.config.a.b
abc = self.config.a.b.c
abd = self.config.a.b.d
self.config.override({'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_providers_with_already_overridden_value(self):
self.config.override({'a': {'b': {'c': 1, 'd': 2}}})
a = self.config.a
ab = self.config.a.b
abc = self.config.a.b.c
abd = self.config.a.b.d
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_providers_with_default_value(self):
self.config = providers.Configuration(
name='config', default={'a': {'b': {'c': 1, 'd': 2}}})
@ -75,6 +100,27 @@ class ConfigTests(unittest.TestCase):
self.assertEqual(abc(), 1)
self.assertEqual(abd(), 2)
def test_providers_with_default_value_overriding(self):
self.config = providers.Configuration(
name='config', default={'a': {'b': {'c': 1, 'd': 2}}})
self.assertEqual(self.config.a(), {'b': {'c': 1, 'd': 2}})
self.assertEqual(self.config.a.b(), {'c': 1, 'd': 2})
self.assertEqual(self.config.a.b.c(), 1)
self.assertEqual(self.config.a.b.d(), 2)
self.config.override({'a': {'b': {'c': 3, 'd': 4}}})
self.assertEqual(self.config.a(), {'b': {'c': 3, 'd': 4}})
self.assertEqual(self.config.a.b(), {'c': 3, 'd': 4})
self.assertEqual(self.config.a.b.c(), 3)
self.assertEqual(self.config.a.b.d(), 4)
self.config.reset_override()
self.assertEqual(self.config.a(), {'b': {'c': 1, 'd': 2}})
self.assertEqual(self.config.a.b(), {'c': 1, 'd': 2})
self.assertEqual(self.config.a.b.c(), 1)
self.assertEqual(self.config.a.b.d(), 2)
def test_value_of_undefined_option(self):
self.assertIsNone(self.config.a())