mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-25 02:53:56 +03:00
Add latest containers module updates + movie_lister refactoring
This commit is contained in:
parent
d6f48b6e1d
commit
8fdb190118
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from dependency_injector import utils
|
from dependency_injector import (
|
||||||
|
utils,
|
||||||
|
errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerMetaClass(type):
|
class DeclarativeContainerMetaClass(type):
|
||||||
|
@ -21,6 +24,7 @@ class DeclarativeContainerMetaClass(type):
|
||||||
|
|
||||||
attributes['cls_providers'] = dict(cls_providers)
|
attributes['cls_providers'] = dict(cls_providers)
|
||||||
attributes['inherited_providers'] = dict(inherited_providers)
|
attributes['inherited_providers'] = dict(inherited_providers)
|
||||||
|
attributes['providers'] = dict(cls_providers + inherited_providers)
|
||||||
|
|
||||||
return type.__new__(mcs, class_name, bases, attributes)
|
return type.__new__(mcs, class_name, bases, attributes)
|
||||||
|
|
||||||
|
@ -32,62 +36,69 @@ class DeclarativeContainerMetaClass(type):
|
||||||
"""
|
"""
|
||||||
if utils.is_provider(value):
|
if utils.is_provider(value):
|
||||||
cls.providers[name] = value
|
cls.providers[name] = value
|
||||||
|
cls.cls_providers[name] = value
|
||||||
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
|
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
|
||||||
|
|
||||||
|
def __delattr__(cls, name):
|
||||||
|
"""Delete class attribute.
|
||||||
|
|
||||||
class Container(object):
|
If value of attribute is provider, it will be deleted from providers
|
||||||
"""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
|
|
||||||
dictionary.
|
dictionary.
|
||||||
"""
|
"""
|
||||||
if utils.is_provider(value):
|
if name in cls.providers and name in cls.cls_providers:
|
||||||
self.providers[name] = value
|
del cls.providers[name]
|
||||||
super(Container, self).__setattr__(name, value)
|
del cls.cls_providers[name]
|
||||||
|
super(DeclarativeContainerMetaClass, cls).__delattr__(name)
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(DeclarativeContainerMetaClass)
|
@six.add_metaclass(DeclarativeContainerMetaClass)
|
||||||
class DeclarativeContainer(object):
|
class DeclarativeContainer(object):
|
||||||
"""Declarative inversion of control container."""
|
"""Declarative inversion of control container."""
|
||||||
|
|
||||||
|
__IS_CATALOG__ = True
|
||||||
|
|
||||||
|
providers = dict()
|
||||||
cls_providers = dict()
|
cls_providers = dict()
|
||||||
inherited_providers = dict()
|
inherited_providers = dict()
|
||||||
|
|
||||||
def __init__(self):
|
overridden_by = tuple()
|
||||||
"""Initializer."""
|
|
||||||
self.providers = dict()
|
@classmethod
|
||||||
self.providers.update(self.__class__.inherited_providers)
|
def override(cls, overriding):
|
||||||
self.providers.update(self.__class__.cls_providers)
|
"""Override current container by overriding container.
|
||||||
super(DeclarativeContainer, self).__init__()
|
|
||||||
|
: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.
|
""":py:class:`DeclarativeContainer` overriding decorator.
|
||||||
|
|
||||||
:param declarative_container: Container that should be overridden by
|
:param catalog: Container that should be overridden by decorated container.
|
||||||
decorated container.
|
:type catalog: :py:class:`DeclarativeContainer`
|
||||||
:type declarative_container: :py:class:`DeclarativeContainer`
|
|
||||||
|
|
||||||
:return: Declarative container's overriding decorator.
|
:return: Declarative container's overriding decorator.
|
||||||
:rtype: callable(:py:class:`DeclarativeContainer`)
|
:rtype: callable(:py:class:`DeclarativeContainer`)
|
||||||
"""
|
"""
|
||||||
def decorator(overriding_container):
|
def decorator(overriding_container):
|
||||||
"""Overriding decorator."""
|
"""Overriding decorator."""
|
||||||
declarative_container.override(overriding_container)
|
container.override(overriding_container)
|
||||||
return overriding_container
|
return overriding_container
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
@ -9,7 +9,7 @@ This mini application uses ``movies`` library, that is configured to work with
|
||||||
csv file movies database.
|
csv file movies database.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dependency_injector import catalogs
|
from dependency_injector import containers
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
from dependency_injector import injections
|
from dependency_injector import injections
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ from movies import finders
|
||||||
from settings import MOVIES_CSV_PATH
|
from settings import MOVIES_CSV_PATH
|
||||||
|
|
||||||
|
|
||||||
@catalogs.override(MoviesModule)
|
@containers.override(MoviesModule)
|
||||||
class MyMoviesModule(catalogs.DeclarativeCatalog):
|
class MyMoviesModule(containers.DeclarativeContainer):
|
||||||
"""Customized catalog of movies module component providers."""
|
"""IoC container for overriding movies module component providers."""
|
||||||
|
|
||||||
movie_finder = providers.Factory(finders.CsvMovieFinder,
|
movie_finder = providers.Factory(finders.CsvMovieFinder,
|
||||||
*MoviesModule.movie_finder.injections,
|
movie_model=MoviesModule.movie_model,
|
||||||
csv_file=MOVIES_CSV_PATH,
|
csv_file=MOVIES_CSV_PATH,
|
||||||
delimeter=',')
|
delimeter=',')
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ sqlite movies database.
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from dependency_injector import catalogs
|
from dependency_injector import containers
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
from dependency_injector import injections
|
from dependency_injector import injections
|
||||||
|
|
||||||
|
@ -21,18 +21,18 @@ from movies import finders
|
||||||
from settings import MOVIES_DB_PATH
|
from settings import MOVIES_DB_PATH
|
||||||
|
|
||||||
|
|
||||||
class ApplicationModule(catalogs.DeclarativeCatalog):
|
class ApplicationModule(containers.DeclarativeContainer):
|
||||||
"""Catalog of application component providers."""
|
"""IoC container of application component providers."""
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
|
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
|
||||||
|
|
||||||
|
|
||||||
@catalogs.override(MoviesModule)
|
@containers.override(MoviesModule)
|
||||||
class MyMoviesModule(catalogs.DeclarativeCatalog):
|
class MyMoviesModule(containers.DeclarativeContainer):
|
||||||
"""Customized catalog of movies module component providers."""
|
"""IoC container for overriding movies module component providers."""
|
||||||
|
|
||||||
movie_finder = providers.Factory(finders.SqliteMovieFinder,
|
movie_finder = providers.Factory(finders.SqliteMovieFinder,
|
||||||
*MoviesModule.movie_finder.injections,
|
movie_model=MoviesModule.movie_model,
|
||||||
database=ApplicationModule.database)
|
database=ApplicationModule.database)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ sqlite movies database and csv file movies database.
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from dependency_injector import catalogs
|
from dependency_injector import containers
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
from dependency_injector import injections
|
from dependency_injector import injections
|
||||||
|
|
||||||
|
@ -22,27 +22,27 @@ from settings import MOVIES_CSV_PATH
|
||||||
from settings import MOVIES_DB_PATH
|
from settings import MOVIES_DB_PATH
|
||||||
|
|
||||||
|
|
||||||
class ApplicationModule(catalogs.DeclarativeCatalog):
|
class ApplicationModule(containers.DeclarativeContainer):
|
||||||
"""Catalog of application component providers."""
|
"""IoC container of application component providers."""
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
|
database = providers.Singleton(sqlite3.connect, MOVIES_DB_PATH)
|
||||||
|
|
||||||
|
|
||||||
@catalogs.copy(MoviesModule)
|
@containers.copy(MoviesModule)
|
||||||
class DbMoviesModule(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,
|
movie_finder = providers.Factory(finders.SqliteMovieFinder,
|
||||||
*MoviesModule.movie_finder.injections,
|
movie_model=MoviesModule.movie_model,
|
||||||
database=ApplicationModule.database)
|
database=ApplicationModule.database)
|
||||||
|
|
||||||
|
|
||||||
@catalogs.copy(MoviesModule)
|
@containers.copy(MoviesModule)
|
||||||
class CsvMoviesModule(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,
|
movie_finder = providers.Factory(finders.CsvMovieFinder,
|
||||||
*MoviesModule.movie_finder.injections,
|
movie_model=MoviesModule.movie_model,
|
||||||
csv_file=MOVIES_CSV_PATH,
|
csv_file=MOVIES_CSV_PATH,
|
||||||
delimeter=',')
|
delimeter=',')
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
"""Movies package.
|
"""Movies package.
|
||||||
|
|
||||||
Top-level package of movies library. This package contains catalog of movies
|
Top-level package of movies library. This package contains IoC container of
|
||||||
module component providers - ``MoviesModule``. It is recommended to use movies
|
movies module component providers - ``MoviesModule``. It is recommended to use
|
||||||
library functionality by fetching required instances from ``MoviesModule``
|
movies library functionality by fetching required instances from
|
||||||
providers.
|
``MoviesModule`` providers.
|
||||||
|
|
||||||
``MoviesModule.movie_finder`` is a factory that provides abstract component
|
``MoviesModule.movie_finder`` is a factory that provides abstract component
|
||||||
``finders.MovieFinder``. This provider should be overridden by provider of
|
``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.
|
Each of ``MoviesModule`` providers could be overridden.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dependency_injector import catalogs
|
from dependency_injector import containers
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
from . import finders
|
from . import finders
|
||||||
|
@ -20,8 +20,8 @@ from . import listers
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
class MoviesModule(catalogs.DeclarativeCatalog):
|
class MoviesModule(containers.DeclarativeContainer):
|
||||||
"""Catalog of movies module component providers."""
|
"""IoC container of movies module component providers."""
|
||||||
|
|
||||||
movie_model = providers.DelegatedFactory(models.Movie)
|
movie_model = providers.DelegatedFactory(models.Movie)
|
||||||
|
|
||||||
|
|
95
tests/test_containers.py
Normal file
95
tests/test_containers.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user