diff --git a/dependency_injector/catalogs.py b/dependency_injector/catalogs.py index 6a1ff06a..d788ea7e 100644 --- a/dependency_injector/catalogs.py +++ b/dependency_injector/catalogs.py @@ -123,6 +123,13 @@ class CatalogBundle(object): class DynamicCatalog(object): """Dynamic catalog of providers. + :py:class:`DynamicCatalog` is a catalog of providers that could be created + in application's runtime. It should cover most of the cases when list of + providers that would be included in catalog is non-deterministic in terms + of apllication code (catalog's structure could be determined just after + application will be started and will do some initial work, like parsing + list of catalog's providers from the configuration). + .. code-block:: python services = DynamicCatalog(auth=providers.Factory(AuthService), diff --git a/docs/catalogs/declarative.rst b/docs/catalogs/declarative.rst index 84188dd6..52d369c4 100644 --- a/docs/catalogs/declarative.rst +++ b/docs/catalogs/declarative.rst @@ -32,7 +32,8 @@ attribute names in Python. contains some callable object, that returns particular instance as a result of its call. -Here is an simple example of declarative catalog with several factories: +Here is an simple example of defining declarative catalog with several +factories: .. image:: /images/catalogs/declarative.png :width: 85% diff --git a/docs/catalogs/dynamic.rst b/docs/catalogs/dynamic.rst index 68d36218..22439464 100644 --- a/docs/catalogs/dynamic.rst +++ b/docs/catalogs/dynamic.rst @@ -2,3 +2,28 @@ Dynamic catalogs ---------------- .. module:: dependency_injector.catalogs + +:py:class:`DynamicCatalog` is a catalog of providers that could be created in +application's runtime. It should cover most of the cases when list of +providers that would be included in catalog is non-deterministic in terms of +apllication code (catalog's structure could be determined just after +application will be started and will do some initial work, like parsing list +of catalog's providers from the configuration). + +:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` have +100% API parity. + +Main difference between :py:class:`DeclarativeCatalog` and +: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/catalogs/dynamic.py + :language: python + +Next one example demonstrates creation and runtime filling of dynamic catalog: + +.. literalinclude:: ../../examples/catalogs/dynamic_runtime_creation.py + :language: python diff --git a/examples/catalogs/declarative.py b/examples/catalogs/declarative.py index c35c7d3a..f11dbe35 100644 --- a/examples/catalogs/declarative.py +++ b/examples/catalogs/declarative.py @@ -4,6 +4,7 @@ from dependency_injector import catalogs from dependency_injector import providers +# Defining declarative catalog: class Catalog(catalogs.DeclarativeCatalog): """Providers catalog.""" diff --git a/examples/catalogs/declarative_injections.py b/examples/catalogs/declarative_injections.py index 52c5855b..0f277f3f 100644 --- a/examples/catalogs/declarative_injections.py +++ b/examples/catalogs/declarative_injections.py @@ -39,7 +39,7 @@ class Services(catalogs.DeclarativeCatalog): """:type: providers.Provider -> AuthService""" -# Retrieving catalog providers: +# Retrieving service providers from catalog: users_service = Services.users() auth_service = Services.auth() diff --git a/examples/catalogs/dynamic.py b/examples/catalogs/dynamic.py new file mode 100644 index 00000000..b89bc92c --- /dev/null +++ b/examples/catalogs/dynamic.py @@ -0,0 +1,18 @@ +"""Dynamic catalog simple example.""" + +from dependency_injector import catalogs +from dependency_injector import providers + + +# Defining dynamic catalog: +catalog = catalogs.DynamicCatalog(factory1=providers.Factory(object), + factory2=providers.Factory(object)) + +# Creating some objects: +object1 = catalog.factory1() +object2 = catalog.factory2() + +# Making some asserts: +assert object1 is not object2 +assert isinstance(object1, object) +assert isinstance(object2, object) diff --git a/examples/catalogs/dynamic_runtime_creation.py b/examples/catalogs/dynamic_runtime_creation.py new file mode 100644 index 00000000..c42570b1 --- /dev/null +++ b/examples/catalogs/dynamic_runtime_creation.py @@ -0,0 +1,66 @@ +"""Dynamic catalog creation and runtime filling of it example.""" + +from dependency_injector import catalogs + + +# 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('.') + if len(path_components) == 1: + path_components.insert(0, '__main__') + module = __import__('.'.join(path_components[0:-1]), + locals(), + globals(), + fromlist=path_components[-1:]) + return getattr(module, path_components[-1]) + + +# "Parsing" some configuration: +config = { + 'services': { + 'users': { + 'class': 'UsersService', + 'provider_class': 'dependency_injector.providers.Factory', + }, + 'auth': { + 'class': 'AuthService', + 'provider_class': 'dependency_injector.providers.Factory', + } + } +} + +# Defining dynamic service providers catalog: +services = catalogs.DynamicCatalog() + +# Filling dynamic service providers catalog according to the 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']) + + # Creating service provider: + service_provider = service_provider_cls(service_cls) + + # Binding service provider to the dynamic service providers catalog: + services.bind_provider(service_name, service_provider) + +# Creating some objects: +users_service = services.users() +auth_service = services.auth() + +# Making some asserts: +assert isinstance(users_service, UsersService) +assert isinstance(auth_service, AuthService)