mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-30 23:47:40 +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