Update containers documentation

+ Refactor provider overriding system
This commit is contained in:
Roman Mogilatov 2016-06-06 11:26:53 +03:00
parent 71c871caf7
commit 1eee0fe529
19 changed files with 392 additions and 334 deletions

View File

@ -10,15 +10,44 @@ from dependency_injector import (
class DynamicContainer(object): class DynamicContainer(object):
"""Dynamic inversion of control container.""" """Dynamic inversion of control container.
.. code-block:: python
services = DynamicContainer()
services.auth = providers.Factory(AuthService)
services.users = providers.Factory(UsersService,
auth_service=services.auth)
.. py:attribute:: providers
Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
.. py:attribute:: overridden
Tuple of overriding containers.
:type: tuple[:py:class:`DynamicContainer`]
.. py:attribute:: provider_type
Type of providers that could be placed in container.
:type: type
"""
__IS_CONTAINER__ = True __IS_CONTAINER__ = True
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer.
:rtype: None
"""
self.provider_type = providers.Provider self.provider_type = providers.Provider
self.providers = dict() self.providers = dict()
self.overridden_by = tuple() self.overridden = tuple()
super(DynamicContainer, self).__init__() super(DynamicContainer, self).__init__()
def __setattr__(self, name, value): def __setattr__(self, name, value):
@ -26,6 +55,14 @@ class DynamicContainer(object):
If value of attribute is provider, it will be added into providers If value of attribute is provider, it will be added into providers
dictionary. dictionary.
:param name: Attribute's name
:type name: str
:param value: Attribute's value
:type value: object
:rtype: None
""" """
if utils.is_provider(value): if utils.is_provider(value):
_check_provider_type(self, value) _check_provider_type(self, value)
@ -37,6 +74,11 @@ class DynamicContainer(object):
If value of attribute is provider, it will be deleted from providers If value of attribute is provider, it will be deleted from providers
dictionary. dictionary.
:param name: Attribute's name
:type name: str
:rtype: None
""" """
if name in self.providers: if name in self.providers:
del self.providers[name] del self.providers[name]
@ -46,10 +88,10 @@ class DynamicContainer(object):
"""Override current container by overriding container. """Override current container by overriding container.
:param overriding: Overriding container. :param overriding: Overriding container.
:type overriding: :py:class:`DeclarativeContainer` :type overriding: :py:class:`DynamicContainer`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to :raise: :py:exc:`dependency_injector.errors.Error` if trying to
override container by itself or its subclasses override container by itself
:rtype: None :rtype: None
""" """
@ -57,7 +99,7 @@ class DynamicContainer(object):
raise errors.Error('Container {0} could not be overridden ' raise errors.Error('Container {0} could not be overridden '
'with itself'.format(self)) 'with itself'.format(self))
self.overridden_by += (overriding,) self.overridden += (overriding,)
for name, provider in six.iteritems(overriding.providers): for name, provider in six.iteritems(overriding.providers):
try: try:
@ -70,10 +112,10 @@ class DynamicContainer(object):
:rtype: None :rtype: None
""" """
if not self.overridden_by: if not self.overridden:
raise errors.Error('Container {0} is not overridden'.format(self)) raise errors.Error('Container {0} is not overridden'.format(self))
self.overridden_by = self.overridden_by[:-1] self.overridden = self.overridden[:-1]
for provider in six.itervalues(self.providers): for provider in six.itervalues(self.providers):
provider.reset_last_overriding() provider.reset_last_overriding()
@ -83,7 +125,7 @@ class DynamicContainer(object):
:rtype: None :rtype: None
""" """
self.overridden_by = tuple() self.overridden = tuple()
for provider in six.itervalues(self.providers): for provider in six.itervalues(self.providers):
provider.reset_override() provider.reset_override()
@ -120,6 +162,14 @@ class DeclarativeContainerMetaClass(type):
If value of attribute is provider, it will be added into providers If value of attribute is provider, it will be added into providers
dictionary. dictionary.
:param name: Attribute's name
:type name: str
:param value: Attribute's value
:type value: object
:rtype: None
""" """
if utils.is_provider(value): if utils.is_provider(value):
_check_provider_type(cls, value) _check_provider_type(cls, value)
@ -132,6 +182,11 @@ class DeclarativeContainerMetaClass(type):
If value of attribute is provider, it will be deleted from providers If value of attribute is provider, it will be deleted from providers
dictionary. dictionary.
:param name: Attribute's name
:type name: str
:rtype: None
""" """
if name in cls.providers and name in cls.cls_providers: if name in cls.providers and name in cls.cls_providers:
del cls.providers[name] del cls.providers[name]
@ -141,21 +196,61 @@ class DeclarativeContainerMetaClass(type):
@six.add_metaclass(DeclarativeContainerMetaClass) @six.add_metaclass(DeclarativeContainerMetaClass)
class DeclarativeContainer(object): class DeclarativeContainer(object):
"""Declarative inversion of control container.""" """Declarative inversion of control container.
.. code-block:: python
class Services(DeclarativeContainer):
auth = providers.Factory(AuthService)
users = providers.Factory(UsersService,
auth_service=auth)
"""
__IS_CONTAINER__ = True __IS_CONTAINER__ = True
provider_type = providers.Provider provider_type = providers.Provider
"""Type of providers that could be placed in container.
providers = dict() :type: type
cls_providers = dict() """
inherited_providers = dict()
overridden_by = tuple()
instance_type = DynamicContainer instance_type = DynamicContainer
"""Type of container that is returned on instantiating declarative
container.
:type: type
"""
providers = dict()
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
cls_providers = dict()
"""Read-only dictionary of current container providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
inherited_providers = dict()
"""Read-only dictionary of inherited providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
overridden = tuple()
"""Tuple of overriding containers.
:type: tuple[:py:class:`DeclarativeContainer`]
"""
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
"""Constructor.""" """Constructor.
:return: Dynamic container with copy of all providers.
:rtype: :py:class:`DynamicContainer`
"""
container = cls.instance_type(*args, **kwargs) container = cls.instance_type(*args, **kwargs)
container.provider_type = cls.provider_type container.provider_type = cls.provider_type
@ -180,7 +275,7 @@ class DeclarativeContainer(object):
raise errors.Error('Container {0} could not be overridden ' raise errors.Error('Container {0} could not be overridden '
'with itself or its subclasses'.format(cls)) 'with itself or its subclasses'.format(cls))
cls.overridden_by += (overriding,) cls.overridden += (overriding,)
for name, provider in six.iteritems(overriding.cls_providers): for name, provider in six.iteritems(overriding.cls_providers):
try: try:
@ -194,10 +289,10 @@ class DeclarativeContainer(object):
:rtype: None :rtype: None
""" """
if not cls.overridden_by: if not cls.overridden:
raise errors.Error('Container {0} is not overridden'.format(cls)) raise errors.Error('Container {0} is not overridden'.format(cls))
cls.overridden_by = cls.overridden_by[:-1] cls.overridden = cls.overridden[:-1]
for provider in six.itervalues(cls.providers): for provider in six.itervalues(cls.providers):
provider.reset_last_overriding() provider.reset_last_overriding()
@ -208,7 +303,7 @@ class DeclarativeContainer(object):
:rtype: None :rtype: None
""" """
cls.overridden_by = tuple() cls.overridden = tuple()
for provider in six.itervalues(cls.providers): for provider in six.itervalues(cls.providers):
provider.reset_override() provider.reset_override()

View File

@ -51,7 +51,7 @@ class Provider(object):
All providers should extend this class. All providers should extend this class.
.. py:attribute:: overridden_by .. py:attribute:: overridden
Tuple of overriding providers, if any. Tuple of overriding providers, if any.
@ -60,11 +60,11 @@ class Provider(object):
__IS_PROVIDER__ = True __IS_PROVIDER__ = True
__OPTIMIZED_CALLS__ = True __OPTIMIZED_CALLS__ = True
__slots__ = ('overridden_by', 'provide', '__call__') __slots__ = ('overridden', 'provide', '__call__')
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer."""
self.overridden_by = tuple() self.overridden = tuple()
super(Provider, self).__init__() super(Provider, self).__init__()
# Enable __call__() / _provide() optimization # Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__: if self.__class__.__OPTIMIZED_CALLS__:
@ -81,7 +81,9 @@ class Provider(object):
def _call_last_overriding(self, *args, **kwargs): def _call_last_overriding(self, *args, **kwargs):
"""Call last overriding provider and return result.""" """Call last overriding provider and return result."""
return self.last_overriding(*args, **kwargs) return (self.overridden[-1](*args, **kwargs)
if self.overridden
else None)
def provide_injection(self): def provide_injection(self):
"""Injection strategy implementation. """Injection strategy implementation.
@ -90,22 +92,6 @@ class Provider(object):
""" """
return self.provide() return self.provide()
@property
def is_overridden(self):
"""Read-only property that is set to ``True`` if provider is overridden.
:rtype: bool
"""
return bool(self.overridden_by)
@property
def last_overriding(self):
"""Read-only reference to the last overriding provider, if any.
:type: :py:class:`Provider` | None
"""
return self.overridden_by[-1] if self.overridden_by else None
def override(self, provider): def override(self, provider):
"""Override provider with another provider. """Override provider with another provider.
@ -124,7 +110,7 @@ class Provider(object):
if not is_provider(provider): if not is_provider(provider):
provider = Object(provider) provider = Object(provider)
self.overridden_by += (ensure_is_provider(provider),) self.overridden += (ensure_is_provider(provider),)
# Disable __call__() / _provide() optimization # Disable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__: if self.__class__.__OPTIMIZED_CALLS__:
@ -140,12 +126,12 @@ class Provider(object):
:rtype: None :rtype: None
""" """
if not self.overridden_by: if not self.overridden:
raise Error('Provider {0} is not overridden'.format(str(self))) raise Error('Provider {0} is not overridden'.format(str(self)))
self.overridden_by = self.overridden_by[:-1] self.overridden = self.overridden[:-1]
if not self.is_overridden: if not self.overridden:
# Enable __call__() / _provide() optimization # Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__: if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._provide self.__call__ = self.provide = self._provide
@ -155,7 +141,7 @@ class Provider(object):
:rtype: None :rtype: None
""" """
self.overridden_by = tuple() self.overridden = tuple()
# Enable __call__() / _provide() optimization # Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__: if self.__class__.__OPTIMIZED_CALLS__:
@ -324,10 +310,10 @@ class ExternalDependency(Provider):
:rtype: object :rtype: object
""" """
if not self.is_overridden: if not self.overridden:
raise Error('Dependency is not defined') raise Error('Dependency is not defined')
instance = self.last_overriding(*args, **kwargs) instance = self._call_last_overriding(*args, **kwargs)
if not isinstance(instance, self.instance_of): if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) + raise Error('{0} is not an '.format(instance) +

View File

@ -1,12 +1,12 @@
Declarative containers Declarative containers
-------------------- ----------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` is a container of providers that could be :py:class:`DeclarativeContainer` is inversion of control container that
defined in declarative manner. It should cover most of the cases when list could be defined in declarative manner. It should cover most of the cases
of providers that would be included in container is deterministic (container when list of providers that would be included in container is deterministic
will not change its structure in runtime). (container will not change its structure in runtime).
Declarative containers have to extend base declarative container class - Declarative containers have to extend base declarative container class -
:py:class:`dependency_injector.containers.DeclarativeContainer`. :py:class:`dependency_injector.containers.DeclarativeContainer`.

View File

@ -1,31 +1,27 @@
Dynamic catalogs Dynamic containers
---------------- ------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DynamicCatalog` is a catalog of providers that could be created in :py:class:`DynamicContainer` is an inversion of control container with dynamic
application's runtime. It should cover most of the cases when list of structure. It should cover most of the cases when list of providers that
providers that would be included in catalog is non-deterministic in terms of would be included in container is non-deterministic and depends on
apllication code (catalog's structure could be determined just after application's flow or its configuration (container's structure could be
application will be started and will do some initial work, like parsing list determined just after application will be started and will do some initial
of catalog's providers from the configuration). work, like parsing list of container's providers from the configuration).
:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` have While :py:class:`DeclarativeContainer` acts on class-level,
100% API parity. :py:class:`DynamicContainer` does the same on instance-level.
Main difference between :py:class:`DeclarativeCatalog` and Here is an simple example of defining dynamic container with several factories:
:py:class:`DynamicCatalog` is that :py:class:`DeclarativeCatalog` acts on
class-level, while :py:class:`DynamicCatalog` do the same on
instance-level.
Here is an simple example of defining dynamic catalog with several factories: .. literalinclude:: ../../examples/containers/dynamic.py
.. literalinclude:: ../../examples/catalogs/dynamic.py
:language: python :language: python
:linenos: :linenos:
Next one example demonstrates creation and runtime filling of dynamic catalog: Next example demonstrates creation of dynamic container based on some
configuration:
.. literalinclude:: ../../examples/catalogs/dynamic_runtime_creation.py .. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
:language: python :language: python
:linenos: :linenos:

View File

@ -1,5 +1,5 @@
Containers IoC Containers
========== ==============
Containers are collections of providers. Main purpose of containers is to group Containers are collections of providers. Main purpose of containers is to group
providers. providers.

View File

@ -1,52 +1,40 @@
Overriding of catalogs Overriding of containers
---------------------- ------------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
Catalogs can be overridden by other catalogs. This, actually, means that Containers can be overridden by other containers. This, actually, means that
all of the providers from overriding catalog will override providers with the all of the providers from overriding container will override providers with
same names in overridden catalog. the same names in overridden container.
There are two ways to override :py:class:`DeclarativeCatalog` with another There are two ways to override :py:class:`DeclarativeContainer` with another
catalog: container:
- Use :py:meth:`DeclarativeCatalog.override` method. - Use :py:meth:`DeclarativeContainer.override` method.
- Use :py:func:`override` class decorator. - Use :py:func:`override` class decorator.
Example of overriding catalog using :py:meth:`DeclarativeCatalog.override` Example of overriding container using :py:meth:`DeclarativeContainer.override`
method: method:
.. literalinclude:: ../../examples/catalogs/override_declarative.py .. literalinclude:: ../../examples/containers/override_declarative.py
:language: python :language: python
:linenos: :linenos:
Example of overriding catalog using :py:func:`override` decorator: Example of overriding container using :py:func:`override` decorator:
.. literalinclude:: ../../examples/catalogs/override_declarative_decorator.py .. literalinclude:: ../../examples/containers/override_declarative_decorator.py
:language: python :language: python
:linenos: :linenos:
Also there are several useful :py:class:`DeclarativeCatalog` methods and Also there are several useful :py:class:`DeclarativeContainer` methods and
properties that help to work with catalog overridings: properties that help to work with container overridings:
- :py:attr:`DeclarativeCatalog.is_overridden` - read-only property that is set - :py:attr:`DeclarativeContainer.overridden` - tuple of all overriding
to ``True`` if catalog is overridden. containers.
- :py:attr:`DeclarativeCatalog.last_overriding` - read-only reference to - :py:meth:`DeclarativeContainer.reset_last_overriding()` - reset last
the last overriding catalog, if any. overriding provider for each container providers.
- :py:attr:`DeclarativeCatalog.overridden_by` - tuple of all overriding - :py:meth:`DeclarativeContainer.reset_override()` - reset all overridings
catalogs. for each container providers.
- :py:meth:`DeclarativeCatalog.reset_last_overriding()` - reset last
overriding catalog.
- :py:meth:`DeclarativeCatalog.reset_override()` - reset all overridings for
all catalog providers.
:py:class:`DynamicCatalog` has exactly the same functionality, except of :py:class:`DynamicContainer` has exactly the same functionality, except of
:py:func:`override` decorator. Also :py:class:`DynamicCatalog` can override :py:func:`override` decorator.
:py:class:`DeclarativeCatalog` and vise versa.
Example of overriding :py:class:`DeclarativeCatalog` by
:py:class:`DynamicCatalog`:
.. literalinclude:: ../../examples/catalogs/override_declarative_by_dynamic.py
:language: python
:linenos:

View File

@ -1,45 +1,24 @@
Specialization of catalogs Specialization of containers
-------------------------- ----------------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` could be :py:class:`DeclarativeContainer` could be specialized for any kind of needs
specialized for any kind of needs via declaring its subclasses. via declaring its subclasses.
One of such `builtin` features is a limitation to One of such `builtin` features is a limitation for providers type.
:py:class:`DeclarativeCatalog` (and :py:class:`DynamicCatalog`) provider type.
Next example shows usage of this feature with :py:class:`DeclarativeCatalog` Next example shows usage of this feature with :py:class:`DeclarativeContainer`
in couple with feature of :py:class:`dependency_injector.providers.Factory` in couple with feature of :py:class:`dependency_injector.providers.Factory`
for limitation of its provided type: for limitation of its provided type:
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
Listing of `services.py`:
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/services.py
:language: python :language: python
:linenos: :linenos:
Listing of `catalog.py`: Limitation for providers type could be used with :py:class:`DynamicContainer`
as well:
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/catalog.py .. literalinclude:: ../../examples/containers/dynamic_provider_type.py
:language: python
:linenos:
Limitation to provider type could be used with :py:class:`DynamicCatalog`
as well.
Next example does the same that previous one, but use
:py:class:`DynamicCatalog` instead of :py:class:`DeclarativeCatalog`:
Listing of `services.py`:
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/services.py
:language: python
:linenos:
Listing of `catalog.py`:
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/catalog.py
:language: python :language: python
:linenos: :linenos:

View File

@ -0,0 +1,48 @@
"""Specializing declarative container and factory provider example."""
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
class SequencesContainer(containers.DeclarativeContainer):
"""IoC container.
Can contain only sequence providers.
"""
provider_type = SequenceProvider
if __name__ == '__main__':
try:
class _SequenceContainer1(SequencesContainer):
object_provider = providers.Factory(object)
except errors.Error as exception:
print exception
# <class '__main__._SequenceContainer1'> can contain only
# <class '__main__.SequenceProvider'> instances
try:
class _SequenceContainer2(SequencesContainer):
object_provider = SequenceProvider(object)
except errors.Error as exception:
print exception
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
class _SequenceContaier3(SequencesContainer):
list_provider = SequenceProvider(list)
assert _SequenceContaier3.list_provider() == list()

View File

@ -1,17 +0,0 @@
"""Specialized declarative IoC container example."""
import core
import services
class Services(core.ServicesContainer):
"""IoC container of service providers."""
users = core.ServiceProvider(services.UsersService,
config={'option1': '111',
'option2': '222'})
auth = core.ServiceProvider(services.AuthService,
config={'option3': '333',
'option4': '444'},
users_service=users)

View File

@ -1,26 +0,0 @@
"""Base classes for services."""
from dependency_injector import containers
from dependency_injector import providers
class BaseService(object):
"""Base service class."""
class ServiceProvider(providers.Factory):
"""Service provider.
Can provide :py:class:`Base` only.
"""
provided_type = BaseService
class ServicesContainer(containers.DeclarativeContainer):
"""Base IoC container of service providers.
Can include :py:class:`Provider`'s only.
"""
provider_type = ServiceProvider

View File

@ -1,42 +0,0 @@
"""Main module."""
import core
import services
import container
from dependency_injector import providers
from dependency_injector import errors
if __name__ == '__main__':
# Creating users & auth services:
users_service = container.Services.users()
auth_service = container.Services.auth()
# Making some asserts:
assert users_service.config == {'option1': '111',
'option2': '222'}
assert auth_service.config == {'option3': '333',
'option4': '444'}
assert isinstance(auth_service.users_service, services.UsersService)
# Trying to declare services container with other provider type:
try:
class _Services1(core.ServicesContainer):
users = providers.Factory(services.UsersService)
except errors.Error as exception:
print exception
# <class '__main__._Services1'> can contain only
# <class 'core.ServiceProvider'> instances
# Trying to declare services container with correct provider by invalid
# provided type:
try:
class _Services2(core.ServicesContainer):
users = core.ServiceProvider(object)
except errors.Error as exception:
print exception
# <class 'core.ServiceProvider'> can provide only
# <class 'core.BaseService'> instances

View File

@ -1,22 +0,0 @@
"""Base classes for services."""
import core
class UsersService(core.BaseService):
"""Users service."""
def __init__(self, config):
"""Initializer."""
self.config = config
super(UsersService, self).__init__()
class AuthService(core.BaseService):
"""Auth service."""
def __init__(self, config, users_service):
"""Initializer."""
self.config = config
self.users_service = users_service
super(AuthService, self).__init__()

View File

@ -0,0 +1,18 @@
"""Dynamic container simple example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
# Defining dynamic container:
container = containers.DynamicContainer()
container.factory1 = providers.Factory(object)
container.factory2 = providers.Factory(object)
# Creating some objects:
object1 = container.factory1()
object2 = container.factory2()
# Making some asserts:
assert object1 is not object2
assert isinstance(object1, object) and isinstance(object2, object)

View File

@ -0,0 +1,41 @@
"""Specializing dynamic container and factory provider example."""
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
sequences_container = containers.DynamicContainer()
sequences_container.provider_type = SequenceProvider
if __name__ == '__main__':
try:
sequences_container.object_provider = providers.Factory(object)
except errors.Error as exception:
print exception
# <dependency_injector.containers.DynamicContainer object at
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
# instances
try:
sequences_container.object_provider = SequenceProvider(object)
except errors.Error as exception:
print exception
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
sequences_container.list_provider = SequenceProvider(list)
assert sequences_container.list_provider() == list()

View File

@ -0,0 +1,61 @@
"""Creation of dynamic container based on some configuration example."""
import dependency_injector.containers as containers
# Defining several example services:
class UsersService(object):
"""Example users service."""
class AuthService(object):
"""Example auth service."""
def import_cls(cls_name):
"""Import class by its fully qualified name.
In terms of current example it is just a small helper function. Please,
don't use it in production approaches.
"""
path_components = cls_name.split('.')
module = __import__('.'.join(path_components[:-1]),
locals(),
globals(),
fromlist=path_components[-1:])
return getattr(module, path_components[-1])
# "Parsing" some configuration:
config = {
'services': {
'users': {
'class': '__main__.UsersService',
'provider_class': 'dependency_injector.providers.Factory',
},
'auth': {
'class': '__main__.AuthService',
'provider_class': 'dependency_injector.providers.Factory',
}
}
}
# Creating empty container of service providers:
services = containers.DynamicContainer()
# Filling dynamic container with service providers using configuration:
for service_name, service_info in config['services'].iteritems():
# Runtime importing of service and service provider classes:
service_cls = import_cls(service_info['class'])
service_provider_cls = import_cls(service_info['provider_class'])
# Binding service provider to the dynamic service providers catalog:
setattr(services, service_name, service_provider_cls(service_cls))
# Creating some objects:
users_service = services.users()
auth_service = services.auth()
# Making some asserts:
assert isinstance(users_service, UsersService)
assert isinstance(auth_service, AuthService)

View File

@ -1,45 +1,28 @@
"""Declarative IoC container overriding example.""" """Declarative IoC container overriding example."""
import collections import dependency_injector.containers as containers
import dependency_injector.providers as providers
from dependency_injector import containers
from dependency_injector import providers
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
"""Example IoC container.""" """IoC container."""
object1_factory = providers.Factory(Object1, sequence_factory = providers.Factory(list)
arg1=1,
arg2=2)
object2_factory = providers.Factory(Object2,
object1=object1_factory)
class OverridingContainer(containers.DeclarativeContainer): class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container.""" """Overriding IoC container."""
object2_factory = providers.Factory(ExtendedObject2) sequence_factory = providers.Factory(tuple)
# Overriding `Container` with `OverridingContainer`: # Overriding `Container` with `OverridingContainer`:
Container.override(OverridingContainer) Container.override(OverridingContainer)
# Creating some objects using overridden container: # Creating some objects using overridden container:
object2_1 = Container.object2_factory() sequence_1 = Container.sequence_factory([1, 2, 3])
object2_2 = Container.object2_factory() sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts: # Making some asserts:
assert Container.overridden_by == (OverridingContainer,) assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -1,23 +1,13 @@
"""Declarative IoC container overriding using `@override()` decorator.""" """Declarative IoC container overriding using `@override()` decorator."""
import collections import dependency_injector.containers as containers
import dependency_injector.providers as providers
from dependency_injector import containers
from dependency_injector import providers
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
"""Example IoC container.""" """IoC container."""
object1_factory = providers.Factory(Object1, arg1=1, arg2=2) sequence_factory = providers.Factory(list)
object2_factory = providers.Factory(Object2, object1=object1_factory)
# Overriding `Container` with `OverridingContainer`: # Overriding `Container` with `OverridingContainer`:
@ -25,17 +15,13 @@ class Container(containers.DeclarativeContainer):
class OverridingContainer(containers.DeclarativeContainer): class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container.""" """Overriding IoC container."""
object2_factory = providers.Factory(ExtendedObject2) sequence_factory = providers.Factory(tuple)
# Creating some objects using overridden container: # Creating some objects using overridden container:
object2_1 = Container.object2_factory() sequence_1 = Container.sequence_factory([1, 2, 3])
object2_2 = Container.object2_factory() sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts: # Making some asserts:
assert Container.overridden_by == (OverridingContainer,) assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -42,14 +42,14 @@ class ProviderTests(unittest.TestCase):
"""Test provider overriding.""" """Test provider overriding."""
overriding_provider = providers.Provider() overriding_provider = providers.Provider()
self.provider.override(overriding_provider) self.provider.override(overriding_provider)
self.assertTrue(self.provider.is_overridden) self.assertTrue(self.provider.overridden)
def test_overriding_context(self): def test_overriding_context(self):
"""Test provider overriding context.""" """Test provider overriding context."""
overriding_provider = providers.Provider() overriding_provider = providers.Provider()
with self.provider.override(overriding_provider): with self.provider.override(overriding_provider):
self.assertTrue(self.provider.is_overridden) self.assertTrue(self.provider.overridden)
self.assertFalse(self.provider.is_overridden) self.assertFalse(self.provider.overridden)
def test_override_with_itself(self): def test_override_with_itself(self):
"""Test provider overriding with itself.""" """Test provider overriding with itself."""
@ -61,21 +61,6 @@ class ProviderTests(unittest.TestCase):
self.provider.override(obj) self.provider.override(obj)
self.assertIs(self.provider(), obj) self.assertIs(self.provider(), obj)
def test_last_overriding(self):
"""Test getting last overriding provider."""
overriding_provider1 = providers.Provider()
overriding_provider2 = providers.Provider()
self.provider.override(overriding_provider1)
self.assertIs(self.provider.last_overriding, overriding_provider1)
self.provider.override(overriding_provider2)
self.assertIs(self.provider.last_overriding, overriding_provider2)
def test_last_overriding_of_not_overridden_provider(self):
"""Test getting last overriding from not overridden provider."""
self.assertIsNone(self.provider.last_overriding)
def test_reset_last_overriding(self): def test_reset_last_overriding(self):
"""Test reseting of last overriding provider.""" """Test reseting of last overriding provider."""
overriding_provider1 = providers.Provider() overriding_provider1 = providers.Provider()
@ -84,13 +69,13 @@ class ProviderTests(unittest.TestCase):
self.provider.override(overriding_provider1) self.provider.override(overriding_provider1)
self.provider.override(overriding_provider2) self.provider.override(overriding_provider2)
self.assertIs(self.provider.last_overriding, overriding_provider2) self.assertIs(self.provider.overridden[-1], overriding_provider2)
self.provider.reset_last_overriding() self.provider.reset_last_overriding()
self.assertIs(self.provider.last_overriding, overriding_provider1) self.assertIs(self.provider.overridden[-1], overriding_provider1)
self.provider.reset_last_overriding() self.provider.reset_last_overriding()
self.assertFalse(self.provider.is_overridden) self.assertFalse(self.provider.overridden)
def test_reset_last_overriding_of_not_overridden_provider(self): def test_reset_last_overriding_of_not_overridden_provider(self):
"""Test resetting of last overriding on not overridden provier.""" """Test resetting of last overriding on not overridden provier."""
@ -101,13 +86,12 @@ class ProviderTests(unittest.TestCase):
overriding_provider = providers.Provider() overriding_provider = providers.Provider()
self.provider.override(overriding_provider) self.provider.override(overriding_provider)
self.assertTrue(self.provider.is_overridden) self.assertTrue(self.provider.overridden)
self.assertIs(self.provider.last_overriding, overriding_provider) self.assertEqual(self.provider.overridden, (overriding_provider,))
self.provider.reset_override() self.provider.reset_override()
self.assertFalse(self.provider.is_overridden) self.assertEqual(self.provider.overridden, tuple())
self.assertIsNone(self.provider.last_overriding)
def test_repr(self): def test_repr(self):
"""Test representation of provider.""" """Test representation of provider."""

View File

@ -138,10 +138,10 @@ class DeclarativeContainerTests(unittest.TestCase):
_Container.override(_OverridingContainer1) _Container.override(_OverridingContainer1)
_Container.override(_OverridingContainer2) _Container.override(_OverridingContainer2)
self.assertEqual(_Container.overridden_by, self.assertEqual(_Container.overridden,
(_OverridingContainer1, (_OverridingContainer1,
_OverridingContainer2)) _OverridingContainer2))
self.assertEqual(_Container.p11.overridden_by, self.assertEqual(_Container.p11.overridden,
(_OverridingContainer1.p11, (_OverridingContainer1.p11,
_OverridingContainer2.p11)) _OverridingContainer2.p11))
@ -169,10 +169,10 @@ class DeclarativeContainerTests(unittest.TestCase):
p11 = providers.Provider() p11 = providers.Provider()
p12 = providers.Provider() p12 = providers.Provider()
self.assertEqual(_Container.overridden_by, self.assertEqual(_Container.overridden,
(_OverridingContainer1, (_OverridingContainer1,
_OverridingContainer2)) _OverridingContainer2))
self.assertEqual(_Container.p11.overridden_by, self.assertEqual(_Container.p11.overridden,
(_OverridingContainer1.p11, (_OverridingContainer1.p11,
_OverridingContainer2.p11)) _OverridingContainer2.p11))
@ -192,9 +192,9 @@ class DeclarativeContainerTests(unittest.TestCase):
_Container.override(_OverridingContainer2) _Container.override(_OverridingContainer2)
_Container.reset_last_overriding() _Container.reset_last_overriding()
self.assertEqual(_Container.overridden_by, self.assertEqual(_Container.overridden,
(_OverridingContainer1,)) (_OverridingContainer1,))
self.assertEqual(_Container.p11.overridden_by, self.assertEqual(_Container.p11.overridden,
(_OverridingContainer1.p11,)) (_OverridingContainer1.p11,))
def test_reset_last_overridding_when_not_overridden(self): def test_reset_last_overridding_when_not_overridden(self):
@ -218,8 +218,8 @@ class DeclarativeContainerTests(unittest.TestCase):
_Container.override(_OverridingContainer2) _Container.override(_OverridingContainer2)
_Container.reset_override() _Container.reset_override()
self.assertEqual(_Container.overridden_by, tuple()) self.assertEqual(_Container.overridden, tuple())
self.assertEqual(_Container.p11.overridden_by, tuple()) self.assertEqual(_Container.p11.overridden, tuple())
def test_copy(self): def test_copy(self):
"""Test copy decorator.""" """Test copy decorator."""
@ -361,15 +361,15 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
container.override(overriding_container1) container.override(overriding_container1)
container.override(overriding_container2) container.override(overriding_container2)
self.assertEqual(container.overridden_by, self.assertEqual(container.overridden,
(overriding_container1, (overriding_container1,
overriding_container2)) overriding_container2))
self.assertEqual(container.p11.overridden_by, self.assertEqual(container.p11.overridden,
(overriding_container1.p11, (overriding_container1.p11,
overriding_container2.p11)) overriding_container2.p11))
self.assertEqual(_Container.overridden_by, tuple()) self.assertEqual(_Container.overridden, tuple())
self.assertEqual(_Container.p11.overridden_by, tuple()) self.assertEqual(_Container.p11.overridden, tuple())
def test_override_with_itself(self): def test_override_with_itself(self):
"""Test override container with itself.""" """Test override container with itself."""
@ -397,9 +397,9 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
container.override(overriding_container2) container.override(overriding_container2)
container.reset_last_overriding() container.reset_last_overriding()
self.assertEqual(container.overridden_by, self.assertEqual(container.overridden,
(overriding_container1,)) (overriding_container1,))
self.assertEqual(container.p11.overridden_by, self.assertEqual(container.p11.overridden,
(overriding_container1.p11,)) (overriding_container1.p11,))
def test_reset_last_overridding_when_not_overridden(self): def test_reset_last_overridding_when_not_overridden(self):
@ -429,8 +429,8 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
container.override(overriding_container2) container.override(overriding_container2)
container.reset_override() container.reset_override()
self.assertEqual(container.overridden_by, tuple()) self.assertEqual(container.overridden, tuple())
self.assertEqual(container.p11.overridden_by, tuple()) self.assertEqual(container.p11.overridden, tuple())
if __name__ == '__main__': if __name__ == '__main__':