mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-26 11:33:58 +03:00
Merge branch 'release/3.17.0' into master
This commit is contained in:
commit
827f9b57bb
|
@ -7,6 +7,11 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
|
3.17.0
|
||||||
|
------
|
||||||
|
- Add ``Container`` provider.
|
||||||
|
- Add ``Configuration`` providers linking.
|
||||||
|
|
||||||
3.16.1
|
3.16.1
|
||||||
------
|
------
|
||||||
- Update ``singleton_thread_locals.py`` to support Python 3 (thanks to
|
- Update ``singleton_thread_locals.py`` to support Python 3 (thanks to
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Dependency injector top-level package."""
|
"""Dependency injector top-level package."""
|
||||||
|
|
||||||
__version__ = '3.16.1'
|
__version__ = '3.17.0'
|
||||||
"""Version number that follows semantic versioning.
|
"""Version number that follows semantic versioning.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -54,6 +54,19 @@ class DynamicContainer(object):
|
||||||
self.overridden = tuple()
|
self.overridden = tuple()
|
||||||
super(DynamicContainer, self).__init__()
|
super(DynamicContainer, self).__init__()
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
"""Create and return full copy of container."""
|
||||||
|
copied = memo.get(id(self))
|
||||||
|
if copied is not None:
|
||||||
|
return copied
|
||||||
|
|
||||||
|
copied = self.__class__()
|
||||||
|
copied.provider_type = Provider
|
||||||
|
copied.providers = deepcopy(self.providers, memo)
|
||||||
|
copied.overridden = deepcopy(self.overridden, memo)
|
||||||
|
|
||||||
|
return copied
|
||||||
|
|
||||||
def __setattr__(self, str name, object value):
|
def __setattr__(self, str name, object value):
|
||||||
"""Set instance attribute.
|
"""Set instance attribute.
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -92,6 +92,7 @@ cdef class CoroutineDelegate(Delegate):
|
||||||
cdef class Configuration(Object):
|
cdef class Configuration(Object):
|
||||||
cdef str __name
|
cdef str __name
|
||||||
cdef dict __children
|
cdef dict __children
|
||||||
|
cdef list __linked
|
||||||
|
|
||||||
|
|
||||||
# Factory providers
|
# Factory providers
|
||||||
|
@ -175,6 +176,14 @@ cdef class List(Provider):
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Container(Provider):
|
||||||
|
cdef object container_cls
|
||||||
|
cdef dict overriding_providers
|
||||||
|
cdef object container
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Injections
|
# Injections
|
||||||
cdef class Injection(object):
|
cdef class Injection(object):
|
||||||
cdef object __value
|
cdef object __value
|
||||||
|
|
|
@ -1029,6 +1029,7 @@ cdef class Configuration(Object):
|
||||||
|
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self.__children = self._create_children(default)
|
self.__children = self._create_children(default)
|
||||||
|
self.__linked = list()
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
"""Create and return full copy of provider."""
|
"""Create and return full copy of provider."""
|
||||||
|
@ -1041,6 +1042,7 @@ cdef class Configuration(Object):
|
||||||
copied = self.__class__(self.__name)
|
copied = self.__class__(self.__name)
|
||||||
copied.__provides = deepcopy(self.__provides, memo)
|
copied.__provides = deepcopy(self.__provides, memo)
|
||||||
copied.__children = deepcopy(self.__children, memo)
|
copied.__children = deepcopy(self.__children, memo)
|
||||||
|
copied.__linked = deepcopy(self.__linked, memo)
|
||||||
|
|
||||||
self._copy_overridings(copied, memo)
|
self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
@ -1093,11 +1095,17 @@ cdef class Configuration(Object):
|
||||||
"""
|
"""
|
||||||
overriding_context = super(Configuration, self).override(provider)
|
overriding_context = super(Configuration, self).override(provider)
|
||||||
|
|
||||||
|
for linked in self.__linked:
|
||||||
|
linked.override(provider)
|
||||||
|
|
||||||
|
if isinstance(provider, Configuration):
|
||||||
|
provider.link_provider(self)
|
||||||
|
|
||||||
value = self.__call__()
|
value = self.__call__()
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
return
|
return
|
||||||
|
|
||||||
for name in value:
|
for name in value.keys():
|
||||||
child_provider = self.__children.get(name)
|
child_provider = self.__children.get(name)
|
||||||
if child_provider is None:
|
if child_provider is None:
|
||||||
continue
|
continue
|
||||||
|
@ -1129,6 +1137,10 @@ cdef class Configuration(Object):
|
||||||
child.reset_override()
|
child.reset_override()
|
||||||
super(Configuration, self).reset_override()
|
super(Configuration, self).reset_override()
|
||||||
|
|
||||||
|
def link_provider(self, provider):
|
||||||
|
"""Configuration link two configuration providers."""
|
||||||
|
self.__linked.append(<Configuration?>provider)
|
||||||
|
|
||||||
def update(self, value):
|
def update(self, value):
|
||||||
"""Set configuration options.
|
"""Set configuration options.
|
||||||
|
|
||||||
|
@ -2077,6 +2089,58 @@ cdef class List(Provider):
|
||||||
return list(__provide_positional_args(args, self.__args, self.__args_len))
|
return list(__provide_positional_args(args, self.__args, self.__args_len))
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Container(Provider):
|
||||||
|
"""Container provider provides an instance of declarative container.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Provider is experimental. Its interface may change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, container_cls, container=None, **overriding_providers):
|
||||||
|
"""Initialize provider."""
|
||||||
|
self.container_cls = container_cls
|
||||||
|
self.overriding_providers = overriding_providers
|
||||||
|
|
||||||
|
if container is None:
|
||||||
|
container = container_cls()
|
||||||
|
container.override_providers(**overriding_providers)
|
||||||
|
self.container = container
|
||||||
|
|
||||||
|
super(Container, 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.container_cls,
|
||||||
|
deepcopy(self.container, memo),
|
||||||
|
**deepcopy(self.overriding_providers, memo),
|
||||||
|
)
|
||||||
|
# self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
return copied
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
"""Return dependency provider."""
|
||||||
|
if name.startswith('__') and name.endswith('__'):
|
||||||
|
raise AttributeError(
|
||||||
|
'\'{cls}\' object has no attribute '
|
||||||
|
'\'{attribute_name}\''.format(cls=self.__class__.__name__,
|
||||||
|
attribute_name=name))
|
||||||
|
return getattr(self.container, name)
|
||||||
|
|
||||||
|
def override(self, provider):
|
||||||
|
"""Override provider with another provider."""
|
||||||
|
raise Error('Provider {0} can not be overridden'.format(self))
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs):
|
||||||
|
"""Return single instance."""
|
||||||
|
return self.container
|
||||||
|
|
||||||
|
|
||||||
cdef class Injection(object):
|
cdef class Injection(object):
|
||||||
"""Abstract injection class."""
|
"""Abstract injection class."""
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
class ConfigTests(unittest.TestCase):
|
class ConfigTests(unittest.TestCase):
|
||||||
|
@ -177,3 +177,72 @@ class ConfigTests(unittest.TestCase):
|
||||||
'Configuration({0}) at {1}>'.format(
|
'Configuration({0}) at {1}>'.format(
|
||||||
repr('config.a.b.c'),
|
repr('config.a.b.c'),
|
||||||
hex(id(self.config.a.b.c))))
|
hex(id(self.config.a.b.c))))
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigLinkingTests(unittest.TestCase):
|
||||||
|
|
||||||
|
class TestCore(containers.DeclarativeContainer):
|
||||||
|
config = providers.Configuration('core')
|
||||||
|
value_getter = providers.Callable(lambda _: _, config.value)
|
||||||
|
|
||||||
|
class TestServices(containers.DeclarativeContainer):
|
||||||
|
config = providers.Configuration('services')
|
||||||
|
value_getter = providers.Callable(lambda _: _, config.value)
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
root_config = providers.Configuration('main')
|
||||||
|
core = self.TestCore(config=root_config.core)
|
||||||
|
services = self.TestServices(config=root_config.services)
|
||||||
|
|
||||||
|
root_config.override(
|
||||||
|
{
|
||||||
|
'core': {
|
||||||
|
'value': 'core',
|
||||||
|
},
|
||||||
|
'services': {
|
||||||
|
'value': 'services',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(core.config(), {'value': 'core'})
|
||||||
|
self.assertEqual(core.config.value(), 'core')
|
||||||
|
self.assertEqual(core.value_getter(), 'core')
|
||||||
|
|
||||||
|
self.assertEqual(services.config(), {'value': 'services'})
|
||||||
|
self.assertEqual(services.config.value(), 'services')
|
||||||
|
self.assertEqual(services.value_getter(), 'services')
|
||||||
|
|
||||||
|
def test_double_override(self):
|
||||||
|
root_config = providers.Configuration('main')
|
||||||
|
core = self.TestCore(config=root_config.core)
|
||||||
|
services = self.TestServices(config=root_config.services)
|
||||||
|
|
||||||
|
root_config.override(
|
||||||
|
{
|
||||||
|
'core': {
|
||||||
|
'value': 'core1',
|
||||||
|
},
|
||||||
|
'services': {
|
||||||
|
'value': 'services1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
root_config.override(
|
||||||
|
{
|
||||||
|
'core': {
|
||||||
|
'value': 'core2',
|
||||||
|
},
|
||||||
|
'services': {
|
||||||
|
'value': 'services2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(core.config(), {'value': 'core2'})
|
||||||
|
self.assertEqual(core.config.value(), 'core2')
|
||||||
|
self.assertEqual(core.value_getter(), 'core2')
|
||||||
|
|
||||||
|
self.assertEqual(services.config(), {'value': 'services2'})
|
||||||
|
self.assertEqual(services.config.value(), 'services2')
|
||||||
|
self.assertEqual(services.value_getter(), 'services2')
|
||||||
|
|
54
tests/unit/providers/test_container_py2_py3.py
Normal file
54
tests/unit/providers/test_container_py2_py3.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
"""Dependency injector container provider unit tests."""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
TEST_VALUE_1 = 'core_section_value1'
|
||||||
|
TEST_CONFIG_1 = {
|
||||||
|
'core': {
|
||||||
|
'section': {
|
||||||
|
'value': TEST_VALUE_1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_VALUE_2 = 'core_section_value2'
|
||||||
|
TEST_CONFIG_2 = {
|
||||||
|
'core': {
|
||||||
|
'section': {
|
||||||
|
'value': TEST_VALUE_2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _copied(value):
|
||||||
|
return copy.deepcopy(value)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCore(containers.DeclarativeContainer):
|
||||||
|
config = providers.Configuration('core')
|
||||||
|
value_getter = providers.Callable(lambda _: _, config.section.value)
|
||||||
|
|
||||||
|
|
||||||
|
class TestApplication(containers.DeclarativeContainer):
|
||||||
|
config = providers.Configuration('config')
|
||||||
|
core = providers.Container(TestCore, config=config.core)
|
||||||
|
dict_factory = providers.Factory(dict, value=core.value_getter)
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
application = TestApplication(config=_copied(TEST_CONFIG_1))
|
||||||
|
self.assertEqual(application.dict_factory(), {'value': TEST_VALUE_1})
|
||||||
|
|
||||||
|
def test_double_override(self):
|
||||||
|
application = TestApplication()
|
||||||
|
application.config.override(_copied(TEST_CONFIG_1))
|
||||||
|
application.config.override(_copied(TEST_CONFIG_2))
|
||||||
|
self.assertEqual(application.dict_factory(), {'value': TEST_VALUE_2})
|
Loading…
Reference in New Issue
Block a user