Feature/declarative container initialization (#176)

* Add overriding functionality on declarative container initialization

* Update changelog

* Update bundles example
This commit is contained in:
Roman Mogylatov 2017-12-24 23:03:13 +02:00 committed by GitHub
parent 1115e783e0
commit 1c6160e827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1748 additions and 1182 deletions

View File

@ -12,6 +12,13 @@ Development version
- Rename ``ExternalDependency`` provider to ``Dependency``. - Rename ``ExternalDependency`` provider to ``Dependency``.
- Add default value for ``instance_of`` argument of ``Dependency`` provider - - Add default value for ``instance_of`` argument of ``Dependency`` provider -
``Dependency(instance_of=object)``. ``Dependency(instance_of=object)``.
- Change initialization of declarative container, so it accepts overriding
providers as keyword arguments -
``DeclarativeContainer(**overriding_providers)``.
- Add method to dynamic catalog for setting groups of providers -
``DynamicContainer.set_providers(**providers)``.
- Add method to dynamic catalog for overriding groups of providers -
``DynamicContainer.set_providers(**overriding_providers)``.
3.8.2 3.8.2

View File

@ -1,6 +1,6 @@
"""Photos bundle.""" """Photos bundle."""
from core import containers from dependency_injector import containers
from dependency_injector import providers from dependency_injector import providers
from . import entities from . import entities

View File

@ -1,6 +1,6 @@
"""Users bundle.""" """Users bundle."""
from core import containers from dependency_injector import containers
from dependency_injector import providers from dependency_injector import providers
from . import entities from . import entities

View File

@ -1,43 +0,0 @@
"""Containers module."""
import six
from dependency_injector import containers, providers
class DeclarativeContainer(containers.DeclarativeContainer):
"""Declarative container."""
def __new__(cls, **dependencies):
"""Constructor.
:return: Dynamic container with copy of all providers.
:rtype: :py:class:`DynamicContainer`
"""
# Make copy of declarative container providers for container instance
container_providers = providers.deepcopy(cls.providers)
# Fetch container dependencies
container_dependencies = dict()
for name, provider in six.iteritems(container_providers):
if isinstance(provider, providers.Dependency):
container_dependencies[name] = provider
# Satisfy container dependencies
for name, dependency in six.iteritems(container_dependencies):
try:
dependency_provider = dependencies[name]
except KeyError:
raise Exception('Dependency {name} of container {container} '
'is not satisfied'.format(
name=name, container=cls))
else:
dependency.provided_by(dependency_provider)
# Create dynamic container
container = cls.instance_type()
container.provider_type = cls.provider_type
for name, provider in six.iteritems(container_providers):
setattr(container, name, provider)
return container

View File

@ -1,6 +1,6 @@
"""Example application - Bundles.""" """Example application - Bundles."""
from core import containers from dependency_injector import containers
from dependency_injector import providers from dependency_injector import providers
from bundles.users import Users from bundles.users import Users

File diff suppressed because it is too large Load Diff

View File

@ -88,6 +88,18 @@ class DynamicContainer(object):
del self.providers[name] del self.providers[name]
super(DynamicContainer, self).__delattr__(name) super(DynamicContainer, self).__delattr__(name)
def set_providers(self, **providers):
"""Set container providers.
:param providers: Dictionary of providers
:type providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
:rtype: None
"""
for name, provider in six.iteritems(providers):
setattr(self, name, provider)
def override(self, object overriding): def override(self, object overriding):
"""Override current container by overriding container. """Override current container by overriding container.
@ -111,6 +123,19 @@ class DynamicContainer(object):
except AttributeError: except AttributeError:
pass pass
def override_providers(self, **overriding_providers):
"""Override container providers.
:param overriding_providers: Dictionary of providers
:type overriding_providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
:rtype: None
"""
for name, overriding_provider in six.iteritems(overriding_providers):
container_provider = getattr(self, name)
container_provider.override(overriding_provider)
def reset_last_overriding(self): def reset_last_overriding(self):
"""Reset last overriding provider for each container providers. """Reset last overriding provider for each container providers.
@ -265,18 +290,16 @@ class DeclarativeContainer(object):
:type: tuple[:py:class:`DeclarativeContainer`] :type: tuple[:py:class:`DeclarativeContainer`]
""" """
def __new__(cls, *args, **kwargs): def __new__(cls, **overriding_providers):
"""Constructor. """Constructor.
:return: Dynamic container with copy of all providers. :return: Dynamic container with copy of all providers.
:rtype: :py:class:`DynamicContainer` :rtype: :py:class:`DynamicContainer`
""" """
container = cls.instance_type(*args, **kwargs) container = cls.instance_type()
container.provider_type = cls.provider_type container.provider_type = cls.provider_type
container.set_providers(**deepcopy(cls.providers))
for name, provider in six.iteritems(deepcopy(cls.providers)): container.override_providers(**overriding_providers)
setattr(container, name, provider)
return container return container
@classmethod @classmethod

View File

@ -265,3 +265,12 @@ class DeclarativeContainerTests(unittest.TestCase):
dict(Container1=Container.Container1, dict(Container1=Container.Container1,
Container2=Container.Container2, Container2=Container.Container2,
Container3=Container.Container3)) Container3=Container.Container3))
def test_init_with_overriding_providers(self):
p1 = providers.Provider()
p2 = providers.Provider()
container = ContainerA(p11=p1, p12=p2)
self.assertIs(container.p11.last_overriding, p1)
self.assertIs(container.p12.last_overriding, p2)

View File

@ -75,6 +75,16 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
self.assertIs(ContainerA.provider_type, self.assertIs(ContainerA.provider_type,
containers.DeclarativeContainer.provider_type) containers.DeclarativeContainer.provider_type)
def test_set_providers(self):
p13 = providers.Provider()
p14 = providers.Provider()
container_a = ContainerA()
container_a.set_providers(p13=p13, p14=p14)
self.assertIs(container_a.p13, p13)
self.assertIs(container_a.p14, p14)
def test_override(self): def test_override(self):
class _Container(containers.DeclarativeContainer): class _Container(containers.DeclarativeContainer):
p11 = providers.Provider() p11 = providers.Provider()
@ -108,6 +118,22 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
with self.assertRaises(errors.Error): with self.assertRaises(errors.Error):
container.override(container) container.override(container)
def test_override_providers(self):
p1 = providers.Provider()
p2 = providers.Provider()
container_a = ContainerA()
container_a.override_providers(p11=p1, p12=p2)
self.assertIs(container_a.p11.last_overriding, p1)
self.assertIs(container_a.p12.last_overriding, p2)
def test_override_providers_with_unknown_provider(self):
container_a = ContainerA()
with self.assertRaises(AttributeError):
container_a.override_providers(unknown=providers.Provider())
def test_reset_last_overridding(self): def test_reset_last_overridding(self):
class _Container(containers.DeclarativeContainer): class _Container(containers.DeclarativeContainer):
p11 = providers.Provider() p11 = providers.Provider()