Add latest containers module updates + movie_lister refactoring

This commit is contained in:
Roman Mogilatov 2016-05-27 19:12:49 +03:00
parent d6f48b6e1d
commit 8fdb190118
6 changed files with 168 additions and 62 deletions

View File

@ -2,7 +2,10 @@
import six
from dependency_injector import utils
from dependency_injector import (
utils,
errors,
)
class DeclarativeContainerMetaClass(type):
@ -21,6 +24,7 @@ class DeclarativeContainerMetaClass(type):
attributes['cls_providers'] = dict(cls_providers)
attributes['inherited_providers'] = dict(inherited_providers)
attributes['providers'] = dict(cls_providers + inherited_providers)
return type.__new__(mcs, class_name, bases, attributes)
@ -32,62 +36,69 @@ class DeclarativeContainerMetaClass(type):
"""
if utils.is_provider(value):
cls.providers[name] = value
cls.cls_providers[name] = value
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
def __delattr__(cls, name):
"""Delete class attribute.
class Container(object):
"""Inversion of control container."""
__IS_CATALOG__ = True
def __init__(self):
"""Initializer."""
self.providers = dict()
def bind_providers(self, **providers):
"""Bind providers to the container."""
for name, provider in six.iteritems(providers):
setattr(self, name, utils.ensure_is_provider(provider))
return self
def __setattr__(self, name, value):
"""Set instance attribute.
If value of attribute is provider, it will be added into providers
If value of attribute is provider, it will be deleted from providers
dictionary.
"""
if utils.is_provider(value):
self.providers[name] = value
super(Container, self).__setattr__(name, value)
if name in cls.providers and name in cls.cls_providers:
del cls.providers[name]
del cls.cls_providers[name]
super(DeclarativeContainerMetaClass, cls).__delattr__(name)
@six.add_metaclass(DeclarativeContainerMetaClass)
class DeclarativeContainer(object):
"""Declarative inversion of control container."""
__IS_CATALOG__ = True
providers = dict()
cls_providers = dict()
inherited_providers = dict()
def __init__(self):
"""Initializer."""
self.providers = dict()
self.providers.update(self.__class__.inherited_providers)
self.providers.update(self.__class__.cls_providers)
super(DeclarativeContainer, self).__init__()
overridden_by = tuple()
@classmethod
def override(cls, overriding):
"""Override current container by overriding container.
:param overriding: Overriding container.
:type overriding: :py:class:`DeclarativeContainer`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
override container by itself or its subclasses
:rtype: None
"""
if issubclass(cls, overriding):
raise errors.Error('Catalog {0} could not be overridden '
'with itself or its subclasses'.format(cls))
cls.overridden_by += (overriding,)
for name, provider in six.iteritems(overriding.cls_providers):
try:
getattr(cls, name).override(provider)
except AttributeError:
pass
def override(declarative_container):
def override(container):
""":py:class:`DeclarativeContainer` overriding decorator.
:param declarative_container: Container that should be overridden by
decorated container.
:type declarative_container: :py:class:`DeclarativeContainer`
:param catalog: Container that should be overridden by decorated container.
:type catalog: :py:class:`DeclarativeContainer`
:return: Declarative container's overriding decorator.
:rtype: callable(:py:class:`DeclarativeContainer`)
"""
def decorator(overriding_container):
"""Overriding decorator."""
declarative_container.override(overriding_container)
container.override(overriding_container)
return overriding_container
return decorator

View File

@ -9,7 +9,7 @@ This mini application uses ``movies`` library, that is configured to work with
csv file movies database.
"""
from dependency_injector import catalogs
from dependency_injector import containers
from dependency_injector import providers
from dependency_injector import injections
@ -19,12 +19,12 @@ from movies import finders
from settings import MOVIES_CSV_PATH
@catalogs.override(MoviesModule)
class MyMoviesModule(catalogs.DeclarativeCatalog):
"""Customized catalog of movies module component providers."""
@containers.override(MoviesModule)
class MyMoviesModule(containers.DeclarativeContainer):
"""IoC container for overriding movies module component providers."""
movie_finder = providers.Factory(finders.CsvMovieFinder,
*MoviesModule.movie_finder.injections,
movie_model=MoviesModule.movie_model,
csv_file=MOVIES_CSV_PATH,
delimeter=',')

View File

@ -11,7 +11,7 @@ sqlite movies database.
import sqlite3
from dependency_injector import catalogs
from dependency_injector import containers
from dependency_injector import providers
from dependency_injector import injections
@ -21,18 +21,18 @@ from movies import finders
from settings import MOVIES_DB_PATH
class ApplicationModule(catalogs.DeclarativeCatalog):
"""Catalog of application component providers."""
class ApplicationModule(containers.DeclarativeContainer):
"""IoC container of application component providers."""
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
@catalogs.override(MoviesModule)
class MyMoviesModule(catalogs.DeclarativeCatalog):
"""Customized catalog of movies module component providers."""
@containers.override(MoviesModule)
class MyMoviesModule(containers.DeclarativeContainer):
"""IoC container for overriding movies module component providers."""
movie_finder = providers.Factory(finders.SqliteMovieFinder,
*MoviesModule.movie_finder.injections,
movie_model=MoviesModule.movie_model,
database=ApplicationModule.database)

View File

@ -11,7 +11,7 @@ sqlite movies database and csv file movies database.
import sqlite3
from dependency_injector import catalogs
from dependency_injector import containers
from dependency_injector import providers
from dependency_injector import injections
@ -22,27 +22,27 @@ from settings import MOVIES_CSV_PATH
from settings import MOVIES_DB_PATH
class ApplicationModule(catalogs.DeclarativeCatalog):
"""Catalog of application component providers."""
class ApplicationModule(containers.DeclarativeContainer):
"""IoC container of application component providers."""
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
@catalogs.copy(MoviesModule)
@containers.copy(MoviesModule)
class DbMoviesModule(MoviesModule):
"""Customized catalog of movies module component providers."""
"""IoC container for overriding movies module component providers."""
movie_finder = providers.Factory(finders.SqliteMovieFinder,
*MoviesModule.movie_finder.injections,
movie_model=MoviesModule.movie_model,
database=ApplicationModule.database)
@catalogs.copy(MoviesModule)
@containers.copy(MoviesModule)
class CsvMoviesModule(MoviesModule):
"""Customized catalog of movies module component providers."""
"""IoC container for overriding movies module component providers."""
movie_finder = providers.Factory(finders.CsvMovieFinder,
*MoviesModule.movie_finder.injections,
movie_model=MoviesModule.movie_model,
csv_file=MOVIES_CSV_PATH,
delimeter=',')

View File

@ -1,9 +1,9 @@
"""Movies package.
Top-level package of movies library. This package contains catalog of movies
module component providers - ``MoviesModule``. It is recommended to use movies
library functionality by fetching required instances from ``MoviesModule``
providers.
Top-level package of movies library. This package contains IoC container of
movies module component providers - ``MoviesModule``. It is recommended to use
movies library functionality by fetching required instances from
``MoviesModule`` providers.
``MoviesModule.movie_finder`` is a factory that provides abstract component
``finders.MovieFinder``. This provider should be overridden by provider of
@ -12,7 +12,7 @@ concrete finder implementation in terms of library configuration.
Each of ``MoviesModule`` providers could be overridden.
"""
from dependency_injector import catalogs
from dependency_injector import containers
from dependency_injector import providers
from . import finders
@ -20,8 +20,8 @@ from . import listers
from . import models
class MoviesModule(catalogs.DeclarativeCatalog):
"""Catalog of movies module component providers."""
class MoviesModule(containers.DeclarativeContainer):
"""IoC container of movies module component providers."""
movie_model = providers.DelegatedFactory(models.Movie)

95
tests/test_containers.py Normal file
View File

@ -0,0 +1,95 @@
"""Dependency injector container unit tests."""
import unittest2 as unittest
from dependency_injector import (
containers,
providers,
)
class ContainerA(containers.DeclarativeContainer):
"""Declarative IoC container A."""
p11 = providers.Provider()
p12 = providers.Provider()
class ContainerB(ContainerA):
"""Declarative IoC container B.
Extends container A.
"""
p21 = providers.Provider()
p22 = providers.Provider()
class DeclarativeContainerTests(unittest.TestCase):
"""Declarative container tests."""
def test_providers_attribute_with(self):
"""Test providers attribute."""
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12))
self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12,
p21=ContainerB.p21,
p22=ContainerB.p22))
def test_cls_providers_attribute_with(self):
"""Test cls_providers attribute."""
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12))
self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21,
p22=ContainerB.p22))
def test_inherited_providers_attribute(self):
"""Test inherited_providers attribute."""
self.assertEqual(ContainerA.inherited_providers, dict())
self.assertEqual(ContainerB.inherited_providers,
dict(p11=ContainerA.p11,
p12=ContainerA.p12))
def test_set_get_del_provider_attribute(self):
"""Test set/get/del provider attributes."""
a_p13 = providers.Provider()
b_p23 = providers.Provider()
ContainerA.p13 = a_p13
ContainerB.p23 = b_p23
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12,
p13=a_p13))
self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12,
p21=ContainerB.p21,
p22=ContainerB.p22,
p23=b_p23))
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12,
p13=a_p13))
self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21,
p22=ContainerB.p22,
p23=b_p23))
del ContainerA.p13
del ContainerB.p23
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12))
self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12,
p21=ContainerB.p21,
p22=ContainerB.p22))
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12))
self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21,
p22=ContainerB.p22))
if __name__ == '__main__':
unittest.main()