diff --git a/dependency_injector/catalogs.py b/dependency_injector/catalogs.py index 3ca2f28c..976912f8 100644 --- a/dependency_injector/catalogs.py +++ b/dependency_injector/catalogs.py @@ -13,7 +13,23 @@ from .utils import ensure_is_catalog_bundle @six.python_2_unicode_compatible class CatalogBundle(object): - """Bundle of catalog providers.""" + """Bundle of catalog providers. + + :py:class:`CatalogBundle` is a frozen, limited collection of catalog + providers. While catalog could be used as a centralized place for + particular providers group, such bundles of catalog providers can be used + for creating several frozen, limited scopes that could be passed to + different subsystems. + + :py:class:`CatalogBundle` has API's parity with catalogs + (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of + retrieving the providers, but it is "frozen" in terms of modification + provider's list. + + :py:class:`CatalogBundle` is considered to be dependable on catalogs + (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by + its design. + """ catalog = None """Bundle's catalog. @@ -25,6 +41,16 @@ class CatalogBundle(object): __IS_CATALOG_BUNDLE__ = True __slots__ = ('providers', '__dict__') + @classmethod + def sub_cls_factory(cls, catalog): + """Create bundle subclass for catalog. + + :return: Subclass of + :py:class:`dependency_injector.catalogs.CatalogBundle` + :rtype: :py:class:`dependency_injector.catalogs.CatalogBundle` + """ + return type('BundleSubclass', (cls,), dict(catalog=catalog)) + def __init__(self, *providers): """Initializer. @@ -44,16 +70,6 @@ class CatalogBundle(object): self.__dict__.update(self.providers) super(CatalogBundle, self).__init__() - @classmethod - def sub_cls_factory(cls, catalog): - """Create bundle subclass for catalog. - - :return: Subclass of - :py:class:`dependency_injector.catalogs.CatalogBundle` - :rtype: :py:class:`dependency_injector.catalogs.CatalogBundle` - """ - return type('BundleSubclass', (cls,), dict(catalog=catalog)) - def get_provider(self, name): """Return provider with specified name or raise an error. @@ -523,10 +539,10 @@ class DeclarativeCatalogMetaClass(type): class DeclarativeCatalog(object): """Declarative catalog of providers. - ``DeclarativeCatalog`` is a catalog of providers that could be defined in - declarative manner. It should cover most of the cases when list of - providers that would be included in catalog is deterministic (catalog will - not change its structure in runtime). + :py:class:`DeclarativeCatalog` is a catalog of providers that could be + defined in declarative manner. It should cover most of the cases when list + of providers that would be included in catalog is deterministic (catalog + will not change its structure in runtime). """ Bundle = CatalogBundle diff --git a/docs/catalogs/bundles.rst b/docs/catalogs/bundles.rst index 3dbfc99e..b9d305b9 100644 --- a/docs/catalogs/bundles.rst +++ b/docs/catalogs/bundles.rst @@ -1,23 +1,43 @@ Catalog provider bundles ------------------------ -``di.DeclarativeCatalog.Bundle`` is a limited collection of catalog providers. -While catalog could be used as a centralized place for particular providers -group, such bundles of catalog providers can be used for creating several -limited scopes that could be passed to different subsystems. +.. module:: dependency_injector.catalogs -``di.DeclarativeCatalog.Bundle`` has exactly the same API as -``di.DeclarativeCatalog`` except of the limitations on getting providers. +:py:class:`CatalogBundle` is a frozen, limited collection of catalog +providers. While catalog could be used as a centralized place for +particular providers group, such bundles of catalog providers can be used +for creating several frozen, limited scopes that could be passed to different +subsystems. -Each ``di.DeclarativeCatalog`` has a reference to its bundle class - -``di.DeclarativeCatalog.Bundle``. For example, if some concrete catalog has name -``SomeCatalog``, then its bundle class could be reached as -``SomeCatalog.Bundle``. +:py:class:`CatalogBundle` has API's parity with catalogs +(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of +retrieving the providers, but it is "frozen" in terms of modification +provider's list. -``di.DeclarativeCatalog.Bundle`` expects to get the list of its catalog providers +:py:class:`CatalogBundle` is considered to be dependable on catalogs +(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by +its design. + +Each catalog (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) +has a reference to its bundle class - :py:attr:`DeclarativeCatalog.Bundle` +(or :py:attr:`DynamicCatalog.Bundle` consequently). For example, subclass of +:py:class:`CatalogBundle` for some concrete declarative catalog +``SomeCatalog`` could be reached as ``SomeCatalog.Bundle``. + +:py:class:`CatalogBundle` expects to get the list of its catalog providers as positional arguments and will limit the scope of created bundle to this list. +.. note:: + + Some notes about :py:class:`CatalogBundle` design. + + Design and syntax of :py:class:`CatalogBundle` was developed with the idea + of keeping full functionalities of type-hinting and introspection of + modern IDE's. This design came from some practical experience of using + :py:class:`CatalogBundle` and considered to be the most comfortable for + developer. + Example: .. image:: /images/catalogs/bundles.png diff --git a/docs/catalogs/declarative.rst b/docs/catalogs/declarative.rst index 321fceab..84188dd6 100644 --- a/docs/catalogs/declarative.rst +++ b/docs/catalogs/declarative.rst @@ -3,10 +3,10 @@ Declarative catalogs .. module:: dependency_injector.catalogs -:py:class:`DeclarativeCatalog` is a catalog of providers that could be -defined in declarative manner. It should cover most of the cases when -list of providers that would be included in catalog is deterministic -(catalog will not change its structure in runtime). +:py:class:`DeclarativeCatalog` is a catalog of providers that could be +defined in declarative manner. It should cover most of the cases when list +of providers that would be included in catalog is deterministic (catalog +will not change its structure in runtime). Declarative catalogs have to extend base declarative catalog class - :py:class:`dependency_injector.catalogs.DeclarativeCatalog`. diff --git a/docs/images/catalogs/bundles.png b/docs/images/catalogs/bundles.png new file mode 100644 index 00000000..f015088a Binary files /dev/null and b/docs/images/catalogs/bundles.png differ diff --git a/examples/catalogs/bundles/catalogs.py b/examples/catalogs/bundles/catalogs.py index 2a9a7056..52e16861 100644 --- a/examples/catalogs/bundles/catalogs.py +++ b/examples/catalogs/bundles/catalogs.py @@ -1,38 +1,40 @@ """Catalog bundles example.""" -import dependency_injector as di +from dependency_injector import catalogs +from dependency_injector import providers +from dependency_injector import errors import services import views # Declaring services catalog: -class Services(di.DeclarativeCatalog): +class Services(catalogs.DeclarativeCatalog): """Example catalog of service providers.""" - users = di.Factory(services.UsersService) - """:type: di.Provider -> services.UsersService""" + users = providers.Factory(services.Users) + """:type: providers.Provider -> services.Users""" - auth = di.Factory(services.AuthService) - """:type: di.Provider -> services.AuthService""" + auth = providers.Factory(services.Auth) + """:type: providers.Provider -> services.Auth""" - photos = di.Factory(services.PhotosService) - """:type: di.Provider -> services.PhotosService""" + photos = providers.Factory(services.Photos) + """:type: providers.Provider -> services.Photos""" # Declaring views catalog: -class Views(di.DeclarativeCatalog): +class Views(catalogs.DeclarativeCatalog): """Example catalog of web views.""" - auth = di.Factory(views.AuthView, - services=Services.Bundle(Services.users, - Services.auth)) - """:type: di.Provider -> views.AuthView""" + auth = providers.Factory(views.Auth, + services=Services.Bundle(Services.users, + Services.auth)) + """:type: providers.Provider -> views.Auth""" - photos = di.Factory(views.PhotosView, - services=Services.Bundle(Services.users, - Services.photos)) - """:type: di.Provider -> views.PhotosView""" + photos = providers.Factory(views.Photos, + services=Services.Bundle(Services.users, + Services.photos)) + """:type: providers.Provider -> views.Photos""" # Creating example views: @@ -47,7 +49,7 @@ assert auth_view.services.users is Services.users assert auth_view.services.auth is Services.auth try: auth_view.services.photos -except di.Error: +except errors.Error: # `photos` service provider is not in scope of `auth_view` services bundle, # so `di.Error` will be raised. pass @@ -56,7 +58,7 @@ assert photos_view.services.users is Services.users assert photos_view.services.photos is Services.photos try: photos_view.services.auth -except di.Error as exception: +except errors.Error as exception: # `auth` service provider is not in scope of `photo_processing_view` # services bundle, so `di.Error` will be raised. pass diff --git a/examples/catalogs/bundles/services.py b/examples/catalogs/bundles/services.py index 91b9cae5..95f48b65 100644 --- a/examples/catalogs/bundles/services.py +++ b/examples/catalogs/bundles/services.py @@ -5,13 +5,13 @@ class BaseService(object): """Example base class of service.""" -class UsersService(BaseService): +class Users(BaseService): """Example users service.""" -class AuthService(BaseService): +class Auth(BaseService): """Example auth service.""" -class PhotosService(BaseService): +class Photos(BaseService): """Example photo service.""" diff --git a/examples/catalogs/bundles/views.py b/examples/catalogs/bundles/views.py index a3932d63..2be4dd03 100644 --- a/examples/catalogs/bundles/views.py +++ b/examples/catalogs/bundles/views.py @@ -7,15 +7,15 @@ class BaseWebView(object): def __init__(self, services): """Initializer. - :type services: catalogs.Services :param services: Bundle of service providers + :type services: catalogs.Services """ self.services = services -class AuthView(BaseWebView): +class Auth(BaseWebView): """Example auth web view.""" -class PhotosView(BaseWebView): +class Photos(BaseWebView): """Example photo processing web view."""