Merge remote-tracking branch 'origin/docs_update_for_0.11'

This commit is contained in:
Roman Mogilatov 2015-11-26 15:35:13 +02:00
commit 1ecd24af7d
97 changed files with 2259 additions and 829 deletions

View File

@ -67,7 +67,10 @@ Examples
"""Concept example of `Dependency Injector`.""" """Concept example of `Dependency Injector`."""
import sqlite3 import sqlite3
import dependency_injector as di
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class UsersService(object): class UsersService(object):
@ -87,20 +90,20 @@ Examples
self.users_service = users_service self.users_service = users_service
class Services(di.DeclarativeCatalog): class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers.""" """Catalog of service providers."""
database = di.Singleton(sqlite3.connect, ':memory:') database = providers.Singleton(sqlite3.connect, ':memory:')
""":type: di.Provider -> sqlite3.Connection""" """:type: providers.Provider -> sqlite3.Connection"""
users = di.Factory(UsersService, users = providers.Factory(UsersService,
db=database) db=database)
""":type: di.Provider -> UsersService""" """:type: providers.Provider -> UsersService"""
auth = di.Factory(AuthService, auth = providers.Factory(AuthService,
db=database, db=database,
users_service=users) users_service=users)
""":type: di.Provider -> AuthService""" """:type: providers.Provider -> AuthService"""
# Retrieving catalog providers: # Retrieving catalog providers:
@ -115,9 +118,9 @@ Examples
# Making some "inline" injections: # Making some "inline" injections:
@di.inject(users_service=Services.users) @injections.inject(users_service=Services.users)
@di.inject(auth_service=Services.auth) @injections.inject(auth_service=Services.auth)
@di.inject(database=Services.database) @injections.inject(database=Services.database)
def example(users_service, auth_service, database): def example(users_service, auth_service, database):
"""Example callback.""" """Example callback."""
assert users_service.db is auth_service.db assert users_service.db is auth_service.db

View File

@ -47,7 +47,11 @@ from .errors import UndefinedProviderError
from . import catalogs from . import catalogs
catalog = catalogs catalog = catalogs
VERSION = '0.10.5' VERSION = '0.11.0'
"""Version number that follows semantic versioning.
:type: str
"""
__all__ = ( __all__ = (

View File

@ -7,40 +7,79 @@ from .errors import UndefinedProviderError
from .utils import is_provider from .utils import is_provider
from .utils import is_catalog from .utils import is_catalog
from .utils import is_declarative_catalog
from .utils import ensure_is_provider from .utils import ensure_is_provider
from .utils import ensure_is_catalog_bundle from .utils import ensure_is_catalog_bundle
@six.python_2_unicode_compatible @six.python_2_unicode_compatible
class CatalogBundle(object): 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 catalog = None
""":type: DeclarativeCatalog""" """Bundle's catalog.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`
"""
__IS_CATALOG_BUNDLE__ = True __IS_CATALOG_BUNDLE__ = True
__slots__ = ('providers', '__dict__') __slots__ = ('providers', '__dict__')
@classmethod
def sub_cls_factory(cls, catalog):
"""Create bundle subclass for catalog.
:return: Subclass of :py:class:`CatalogBundle`.
:rtype: :py:class:`CatalogBundle`
"""
return type('BundleSubclass', (cls,), dict(catalog=catalog))
def __init__(self, *providers): def __init__(self, *providers):
"""Initializer.""" """Initializer.
:param providers: Tuple of catalog's bundle providers.
:type providers: tuple[
:py:class:`dependency_injector.providers.Provider`]
"""
self.providers = dict() self.providers = dict()
"""Dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
for provider in providers: for provider in providers:
provider_name = self.catalog.get_provider_bind_name(provider) provider_name = self.catalog.get_provider_bind_name(provider)
self.providers[provider_name] = provider self.providers[provider_name] = provider
self.__dict__.update(self.providers) self.__dict__.update(self.providers)
super(CatalogBundle, self).__init__() super(CatalogBundle, self).__init__()
@classmethod
def sub_cls_factory(cls, catalog):
"""Create bundle class for catalog.
:rtype: CatalogBundle
:return: Subclass of CatalogBundle
"""
return type('BundleSubclass', (cls,), dict(catalog=catalog))
def get_provider(self, name): def get_provider(self, name):
"""Return provider with specified name or raise an error.""" """Return provider with specified name or raise an error.
:param name: Provider's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
:return: Provider with specified name.
:rtype: :py:class:`dependency_injector.providers.Provider`
"""
try: try:
return self.providers[name] return self.providers[name]
except KeyError: except KeyError:
@ -48,18 +87,33 @@ class CatalogBundle(object):
self)) self))
def has_provider(self, name): def has_provider(self, name):
"""Check if there is provider with certain name.""" """Check if there is provider with certain name.
:param name: Provider's name.
:type name: str
:rtype: bool
"""
return name in self.providers return name in self.providers
def __getattr__(self, item): def __getattr__(self, item):
"""Raise an error on every attempt to get undefined provider.""" """Return provider with specified name or raise en error.
:param name: Attribute's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
"""
if item.startswith('__') and item.endswith('__'): if item.startswith('__') and item.endswith('__'):
return super(CatalogBundle, self).__getattr__(item) return super(CatalogBundle, self).__getattr__(item)
raise UndefinedProviderError('Provider "{0}" is not a part ' raise UndefinedProviderError('Provider "{0}" is not a part '
'of {1}'.format(item, self)) 'of {1}'.format(item, self))
def __repr__(self): def __repr__(self):
"""Return string representation of catalog bundle.""" """Return string representation of catalog's bundle.
:rtype: str
"""
return '<{0}.Bundle({1})>'.format( return '<{0}.Bundle({1})>'.format(
self.catalog.name, ', '.join(six.iterkeys(self.providers))) self.catalog.name, ', '.join(six.iterkeys(self.providers)))
@ -68,7 +122,22 @@ class CatalogBundle(object):
@six.python_2_unicode_compatible @six.python_2_unicode_compatible
class DynamicCatalog(object): class DynamicCatalog(object):
"""Catalog of providers.""" """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),
users=providers.Factory(UsersService))
users_service = services.users()
"""
__IS_CATALOG__ = True __IS_CATALOG__ = True
__slots__ = ('name', 'providers', 'provider_names', 'overridden_by', __slots__ = ('name', 'providers', 'provider_names', 'overridden_by',
@ -77,64 +146,132 @@ class DynamicCatalog(object):
def __init__(self, **providers): def __init__(self, **providers):
"""Initializer. """Initializer.
:type providers: dict[str, dependency_injector.providers.Provider] :param providers: Dictionary of catalog providers.
:type providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
""" """
self.Bundle = CatalogBundle.sub_cls_factory(self)
"""Catalog's bundle class.
:type: :py:class:`CatalogBundle`
"""
self.name = '.'.join((self.__class__.__module__, self.name = '.'.join((self.__class__.__module__,
self.__class__.__name__)) self.__class__.__name__))
self.providers = dict() """Catalog's name.
self.provider_names = dict()
self.overridden_by = tuple()
self.Bundle = CatalogBundle.sub_cls_factory(self) By default, it is catalog's module + catalog's class name.
:type: str
"""
self.providers = dict()
"""Dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
self.provider_names = dict()
self.overridden_by = tuple()
"""Tuple of overriding catalogs.
:type: tuple[
:py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`]
"""
self.bind_providers(providers) self.bind_providers(providers)
super(DynamicCatalog, self).__init__() super(DynamicCatalog, self).__init__()
def is_bundle_owner(self, bundle): def is_bundle_owner(self, bundle):
"""Check if catalog is bundle owner.""" """Check if catalog is bundle owner.
:param bundle: Catalog's bundle instance.
:type bundle: :py:class:`CatalogBundle`
:rtype: bool
"""
return ensure_is_catalog_bundle(bundle) and bundle.catalog is self return ensure_is_catalog_bundle(bundle) and bundle.catalog is self
def get_provider_bind_name(self, provider): def get_provider_bind_name(self, provider):
"""Return provider's name in catalog.""" """Return provider's name in catalog.
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
:return: Provider's name.
:rtype: str
"""
if not self.is_provider_bound(provider): if not self.is_provider_bound(provider):
raise Error('Can not find bind name for {0} in catalog {1}'.format( raise Error('Can not find bind name for {0} in catalog {1}'.format(
provider, self)) provider, self))
return self.provider_names[provider] return self.provider_names[provider]
def is_provider_bound(self, provider): def is_provider_bound(self, provider):
"""Check if provider is bound to the catalog.""" """Check if provider is bound to the catalog.
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:rtype: bool
"""
return provider in self.provider_names return provider in self.provider_names
def filter(self, provider_type): def filter(self, provider_type):
"""Return dict of providers, that are instance of provided type.""" """Return dictionary of providers, that are instance of provided type.
:param provider_type: Provider's type.
:type provider_type: :py:class:`dependency_injector.providers.Provider`
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return dict((name, provider) return dict((name, provider)
for name, provider in six.iteritems(self.providers) for name, provider in six.iteritems(self.providers)
if isinstance(provider, provider_type)) if isinstance(provider, provider_type))
@property @property
def is_overridden(self): def is_overridden(self):
"""Check if catalog is overridden by another catalog.""" """Read-only property that is set to ``True`` if catalog is overridden.
:rtype: bool
"""
return bool(self.overridden_by) return bool(self.overridden_by)
@property @property
def last_overriding(self): def last_overriding(self):
"""Return last overriding catalog.""" """Read-only reference to the last overriding catalog, if any.
try:
return self.overridden_by[-1] :type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
except (TypeError, IndexError): None
raise Error('Catalog {0} is not overridden'.format(self)) """
return self.overridden_by[-1] if self.overridden_by else None
def override(self, overriding): def override(self, overriding):
"""Override current catalog providers by overriding catalog providers. """Override current catalog providers by overriding catalog providers.
:type overriding: DynamicCatalog :param overriding: Overriding catalog.
:type overriding: :py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
override catalog by itself
:rtype: None
""" """
if overriding is self:
raise Error('Catalog {0} could not be overridden '
'with itself'.format(self))
self.overridden_by += (overriding,) self.overridden_by += (overriding,)
for name, provider in six.iteritems(overriding.providers): for name, provider in six.iteritems(overriding.providers):
self.get_provider(name).override(provider) self.get_provider(name).override(provider)
def reset_last_overriding(self): def reset_last_overriding(self):
"""Reset last overriding catalog.""" """Reset last overriding catalog.
:rtype: None
"""
if not self.is_overridden: if not self.is_overridden:
raise Error('Catalog {0} is not overridden'.format(self)) raise Error('Catalog {0} is not overridden'.format(self))
self.overridden_by = self.overridden_by[:-1] self.overridden_by = self.overridden_by[:-1]
@ -142,13 +279,25 @@ class DynamicCatalog(object):
provider.reset_last_overriding() provider.reset_last_overriding()
def reset_override(self): def reset_override(self):
"""Reset all overridings for all catalog providers.""" """Reset all overridings for all catalog providers.
:rtype: None
"""
self.overridden_by = tuple() self.overridden_by = tuple()
for provider in six.itervalues(self.providers): for provider in six.itervalues(self.providers):
provider.reset_override() provider.reset_override()
def get_provider(self, name): def get_provider(self, name):
"""Return provider with specified name or raise an error.""" """Return provider with specified name or raise an error.
:param name: Provider's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
:return: Provider with specified name.
:rtype: :py:class:`dependency_injector.providers.Provider`
"""
try: try:
return self.providers[name] return self.providers[name]
except KeyError: except KeyError:
@ -156,7 +305,18 @@ class DynamicCatalog(object):
'name - {1}'.format(self, name)) 'name - {1}'.format(self, name))
def bind_provider(self, name, provider): def bind_provider(self, name, provider):
"""Bind provider to catalog with specified name.""" """Bind provider to catalog with specified name.
:param name: Name of the provider.
:type name: str
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
provider = ensure_is_provider(provider) provider = ensure_is_provider(provider)
if name in self.providers: if name in self.providers:
@ -170,29 +330,66 @@ class DynamicCatalog(object):
self.provider_names[provider] = name self.provider_names[provider] = name
def bind_providers(self, providers): def bind_providers(self, providers):
"""Bind providers dictionary to catalog.""" """Bind providers dictionary to catalog.
:param providers: Dictionary of providers, where key is a name
and value is a provider.
:type providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
for name, provider in six.iteritems(providers): for name, provider in six.iteritems(providers):
self.bind_provider(name, provider) self.bind_provider(name, provider)
def has_provider(self, name): def has_provider(self, name):
"""Check if there is provider with certain name.""" """Check if there is provider with certain name.
:param name: Provider's name.
:type name: str
:rtype: bool
"""
return name in self.providers return name in self.providers
def unbind_provider(self, name): def unbind_provider(self, name):
"""Remove provider binding.""" """Remove provider binding.
:param name: Provider's name.
:type name: str
:rtype: None
"""
provider = self.get_provider(name) provider = self.get_provider(name)
del self.providers[name] del self.providers[name]
del self.provider_names[provider] del self.provider_names[provider]
def __getattr__(self, name): def __getattr__(self, name):
"""Return provider with specified name or raise en error.""" """Return provider with specified name or raise en error.
:param name: Attribute's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
"""
return self.get_provider(name) return self.get_provider(name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
"""Handle setting of catalog attributes. """Handle setting of catalog attributes.
Setting of attributes works as usual, but if value of attribute is Setting of attributes works as usual, but if value of attribute is
provider, this provider will be bound to catalog correctly. provider, this provider will be bound to catalog.
:param name: Attribute's name.
:type name: str
:param value: Attribute's value.
:type value: :py:class:`dependency_injector.providers.Provider` |
object
:rtype: None
""" """
if is_provider(value): if is_provider(value):
return self.bind_provider(name, value) return self.bind_provider(name, value)
@ -202,12 +399,20 @@ class DynamicCatalog(object):
"""Handle deleting of catalog attibute. """Handle deleting of catalog attibute.
Deleting of attributes works as usual, but if value of attribute is Deleting of attributes works as usual, but if value of attribute is
provider, this provider will be unbound from catalog correctly. provider, this provider will be unbound from catalog.
:param name: Attribute's name.
:type name: str
:rtype: None
""" """
self.unbind_provider(name) self.unbind_provider(name)
def __repr__(self): def __repr__(self):
"""Return Python representation of catalog.""" """Return Python representation of catalog.
:rtype: str
"""
return '<{0}({1})>'.format(self.name, return '<{0}({1})>'.format(self.name,
', '.join(six.iterkeys(self.providers))) ', '.join(six.iterkeys(self.providers)))
@ -233,44 +438,70 @@ class DeclarativeCatalogMetaClass(type):
cls = type.__new__(mcs, class_name, bases, attributes) cls = type.__new__(mcs, class_name, bases, attributes)
cls.catalog = DynamicCatalog() cls._catalog = DynamicCatalog()
cls.catalog.name = '.'.join((cls.__module__, cls.__name__)) cls._catalog.name = '.'.join((cls.__module__, cls.__name__))
cls.catalog.bind_providers(dict(providers)) cls._catalog.bind_providers(dict(providers))
cls.cls_providers = dict(cls_providers) cls.cls_providers = dict(cls_providers)
cls.inherited_providers = dict(inherited_providers) cls.inherited_providers = dict(inherited_providers)
cls.Bundle = cls.catalog.Bundle cls.Bundle = cls._catalog.Bundle
return cls return cls
@property @property
def name(cls): def name(cls):
"""Return catalog's name.""" """Read-only property that represents catalog's name.
return cls.catalog.name
Catalog's name is catalog's module + catalog's class name.
:type: str
"""
return cls._catalog.name
@property @property
def providers(cls): def providers(cls):
"""Return dict of catalog's providers.""" """Read-only dictionary of all providers.
return cls.catalog.providers
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._catalog.providers
@property @property
def overridden_by(cls): def overridden_by(cls):
"""Return tuple of overriding catalogs.""" """Tuple of overriding catalogs.
return cls.catalog.overridden_by
:type: tuple[
:py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`]
"""
return cls._catalog.overridden_by
@property @property
def is_overridden(cls): def is_overridden(cls):
"""Check if catalog is overridden by another catalog.""" """Read-only property that is set to ``True`` if catalog is overridden.
return cls.catalog.is_overridden
:rtype: bool
"""
return cls._catalog.is_overridden
@property @property
def last_overriding(cls): def last_overriding(cls):
"""Return last overriding catalog.""" """Read-only reference to the last overriding catalog, if any.
return cls.catalog.last_overriding
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
None
"""
return cls._catalog.last_overriding
def __getattr__(cls, name): def __getattr__(cls, name):
"""Return provider with specified name or raise en error.""" """Return provider with specified name or raise en error.
:param name: Attribute's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
"""
raise UndefinedProviderError('There is no provider "{0}" in ' raise UndefinedProviderError('There is no provider "{0}" in '
'catalog {1}'.format(name, cls)) 'catalog {1}'.format(name, cls))
@ -278,24 +509,41 @@ class DeclarativeCatalogMetaClass(type):
"""Handle setting of catalog attributes. """Handle setting of catalog attributes.
Setting of attributes works as usual, but if value of attribute is Setting of attributes works as usual, but if value of attribute is
provider, this provider will be bound to catalog correctly. provider, this provider will be bound to catalog.
:param name: Attribute's name.
:type name: str
:param value: Attribute's value.
:type value: :py:class:`dependency_injector.providers.Provider` |
object
:rtype: None
""" """
if is_provider(value): if is_provider(value):
setattr(cls.catalog, name, value) setattr(cls._catalog, name, value)
return super(DeclarativeCatalogMetaClass, cls).__setattr__(name, value) return super(DeclarativeCatalogMetaClass, cls).__setattr__(name, value)
def __delattr__(cls, name): def __delattr__(cls, name):
"""Handle deleting of catalog attibute. """Handle deleting of catalog attibute.
Deleting of attributes works as usual, but if value of attribute is Deleting of attributes works as usual, but if value of attribute is
provider, this provider will be unbound from catalog correctly. provider, this provider will be unbound from catalog.
:param name: Attribute's name.
:type name: str
:rtype: None
""" """
if is_provider(getattr(cls, name)): if is_provider(getattr(cls, name)):
delattr(cls.catalog, name) delattr(cls._catalog, name)
return super(DeclarativeCatalogMetaClass, cls).__delattr__(name) return super(DeclarativeCatalogMetaClass, cls).__delattr__(name)
def __repr__(cls): def __repr__(cls):
"""Return string representation of the catalog.""" """Return string representation of the catalog.
:rtype: str
"""
return '<{0}({1})>'.format(cls.name, return '<{0}({1})>'.format(cls.name,
', '.join(six.iterkeys(cls.providers))) ', '.join(six.iterkeys(cls.providers)))
@ -304,121 +552,274 @@ class DeclarativeCatalogMetaClass(type):
@six.add_metaclass(DeclarativeCatalogMetaClass) @six.add_metaclass(DeclarativeCatalogMetaClass)
class DeclarativeCatalog(object): class DeclarativeCatalog(object):
"""Declarative catalog catalog of providers. """Declarative catalog of providers.
:type name: str :py:class:`DeclarativeCatalog` is a catalog of providers that could be
:param name: Catalog's name 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).
:type catalog: DynamicCatalog .. code-block:: python
:param catalog: Instance of dynamic catalog
:type Bundle: CatalogBundle class Services(DeclarativeCatalog):
:param Bundle: Catalog's bundle class
:type providers: dict[str, dependency_injector.Provider] auth = providers.Factory(AuthService)
:param providers: Dict of all catalog providers, including inherited from
parent catalogs
:type cls_providers: dict[str, dependency_injector.Provider] users = providers.Factory(UsersService)
:param cls_providers: Dict of current catalog providers
:type inherited_providers: dict[str, dependency_injector.Provider] users_service = Services.users()
:param inherited_providers: Dict of providers, that are inherited from
parent catalogs
:type overridden_by: tuple[DeclarativeCatalog]
:param overridden_by: Tuple of overriding catalogs
:type is_overridden: bool
:param is_overridden: Read-only, evaluated in runtime, property that is
set to True if catalog is overridden
:type last_overriding: DeclarativeCatalog | None
:param last_overriding: Reference to the last overriding catalog, if any
""" """
Bundle = CatalogBundle Bundle = CatalogBundle
"""Catalog's bundle class.
:type: :py:class:`CatalogBundle`
"""
name = str() name = str()
"""Read-only property that represents catalog's name.
Catalog's name is catalog's module + catalog's class name.
:type: str
"""
cls_providers = dict() cls_providers = dict()
"""Read-only dictionary of current catalog providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
inherited_providers = dict() inherited_providers = dict()
"""Read-only dictionary of inherited providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
providers = dict() providers = dict()
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
overridden_by = tuple() overridden_by = tuple()
is_overridden = bool """Tuple of overriding catalogs.
last_overriding = None
catalog = DynamicCatalog :type: tuple[:py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`]
"""
is_overridden = bool
"""Read-only property that is set to ``True`` if catalog is overridden.
:type: bool
"""
last_overriding = None
"""Read-only reference to the last overriding catalog, if any.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` | None
"""
_catalog = DynamicCatalog
__IS_CATALOG__ = True __IS_CATALOG__ = True
@classmethod @classmethod
def is_bundle_owner(cls, bundle): def is_bundle_owner(cls, bundle):
"""Check if catalog is bundle owner.""" """Check if catalog is bundle owner.
return cls.catalog.is_bundle_owner(bundle)
:param bundle: Catalog's bundle instance.
:type bundle: :py:class:`CatalogBundle`
:rtype: bool
"""
return cls._catalog.is_bundle_owner(bundle)
@classmethod @classmethod
def get_provider_bind_name(cls, provider): def get_provider_bind_name(cls, provider):
"""Return provider's name in catalog.""" """Return provider's name in catalog.
return cls.catalog.get_provider_bind_name(provider)
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
:return: Provider's name.
:rtype: str
"""
return cls._catalog.get_provider_bind_name(provider)
@classmethod @classmethod
def is_provider_bound(cls, provider): def is_provider_bound(cls, provider):
"""Check if provider is bound to the catalog.""" """Check if provider is bound to the catalog.
return cls.catalog.is_provider_bound(provider)
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:rtype: bool
"""
return cls._catalog.is_provider_bound(provider)
@classmethod @classmethod
def filter(cls, provider_type): def filter(cls, provider_type):
"""Return dict of providers, that are instance of provided type.""" """Return dictionary of providers, that are instance of provided type.
return cls.catalog.filter(provider_type)
:param provider_type: Provider's type.
:type provider_type: :py:class:`dependency_injector.providers.Provider`
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._catalog.filter(provider_type)
@classmethod @classmethod
def override(cls, overriding): def override(cls, overriding):
"""Override current catalog providers by overriding catalog providers. """Override current catalog providers by overriding catalog providers.
:type overriding: DeclarativeCatalog | DynamicCatalog :param overriding: Overriding catalog.
:type overriding: :py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
override catalog by itself or its subclasses
:rtype: None
""" """
return cls.catalog.override(overriding) if is_declarative_catalog(overriding) and issubclass(cls, overriding):
raise Error('Catalog {0} could not be overridden '
'with itself or its subclasses'.format(cls))
return cls._catalog.override(overriding)
@classmethod @classmethod
def reset_last_overriding(cls): def reset_last_overriding(cls):
"""Reset last overriding catalog.""" """Reset last overriding catalog.
cls.catalog.reset_last_overriding()
:rtype: None
"""
cls._catalog.reset_last_overriding()
@classmethod @classmethod
def reset_override(cls): def reset_override(cls):
"""Reset all overridings for all catalog providers.""" """Reset all overridings for all catalog providers.
cls.catalog.reset_override()
:rtype: None
"""
cls._catalog.reset_override()
@classmethod @classmethod
def get_provider(cls, name): def get_provider(cls, name):
"""Return provider with specified name or raise an error.""" """Return provider with specified name or raise an error.
return cls.catalog.get_provider(name)
:param name: Provider's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
:return: Provider with specified name.
:rtype: :py:class:`dependency_injector.providers.Provider`
"""
return cls._catalog.get_provider(name)
get = get_provider # Backward compatibility for versions < 0.11.*
@classmethod @classmethod
def bind_provider(cls, name, provider): def bind_provider(cls, name, provider):
"""Bind provider to catalog with specified name.""" """Bind provider to catalog with specified name.
:param name: Name of the provider.
:type name: str
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
setattr(cls, name, provider) setattr(cls, name, provider)
@classmethod @classmethod
def bind_providers(cls, providers): def bind_providers(cls, providers):
"""Bind providers dictionary to catalog.""" """Bind providers dictionary to catalog.
:param providers: Dictionary of providers, where key is a name
and value is a provider.
:type providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
for name, provider in six.iteritems(providers): for name, provider in six.iteritems(providers):
setattr(cls, name, provider) setattr(cls, name, provider)
@classmethod @classmethod
def has_provider(cls, name): def has_provider(cls, name):
"""Check if there is provider with certain name.""" """Check if there is provider with certain name.
:param name: Provider's name.
:type name: str
:rtype: bool
"""
return hasattr(cls, name) return hasattr(cls, name)
has = has_provider # Backward compatibility for versions < 0.11.*
@classmethod @classmethod
def unbind_provider(cls, name): def unbind_provider(cls, name):
"""Remove provider binding.""" """Remove provider binding.
:param name: Provider's name.
:type name: str
:rtype: None
"""
delattr(cls, name) delattr(cls, name)
get = get_provider # Backward compatibility for versions < 0.11.* @classmethod
has = has_provider # Backward compatibility for versions < 0.11.* def __getattr__(cls, name): # pragma: no cover
"""Return provider with specified name or raise en error.
:param name: Attribute's name.
:type name: str
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
"""
raise NotImplementedError('Implementated in metaclass')
@classmethod
def __setattr__(cls, name, value): # pragma: no cover
"""Handle setting of catalog attributes.
Setting of attributes works as usual, but if value of attribute is
provider, this provider will be bound to catalog.
:param name: Attribute's name.
:type name: str
:param value: Attribute's value.
:type value: :py:class:`dependency_injector.providers.Provider` |
object
:rtype: None
"""
raise NotImplementedError('Implementated in metaclass')
@classmethod
def __delattr__(cls, name): # pragma: no cover
"""Handle deleting of catalog attibute.
Deleting of attributes works as usual, but if value of attribute is
provider, this provider will be unbound from catalog.
:param name: Attribute's name.
:type name: str
:rtype: None
"""
raise NotImplementedError('Implementated in metaclass')
# Backward compatibility for versions < 0.11.* # Backward compatibility for versions < 0.11.*
@ -426,7 +827,14 @@ AbstractCatalog = DeclarativeCatalog
def override(catalog): def override(catalog):
"""Catalog overriding decorator.""" """:py:class:`DeclarativeCatalog` overriding decorator.
:param catalog: Catalog that should be overridden by decorated catalog.
:type catalog: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`
:return: Declarative catalog's overriding decorator.
:rtype: callable(:py:class:`DeclarativeCatalog`)
"""
def decorator(overriding_catalog): def decorator(overriding_catalog):
"""Overriding decorator.""" """Overriding decorator."""
catalog.override(overriding_catalog) catalog.override(overriding_catalog)

View File

@ -2,8 +2,21 @@
class Error(Exception): class Error(Exception):
"""Base error.""" """Base error.
All dependency injector errors extend this error class.
"""
class UndefinedProviderError(Error, AttributeError): class UndefinedProviderError(Error, AttributeError):
"""Undefined provider error.""" """Undefined provider error.
This error is used when provider could not be defined, for example:
- provider with certain name could not be defined
- catalog's name of the certain provider could not be defined
- etc...
Also this error extends standard :py:class:`AttributeError`. This gives
possibility to use it correctly with ``__getattr__()``.
"""

View File

@ -13,33 +13,60 @@ from .utils import is_kwarg_injection
from .errors import Error from .errors import Error
IS_PYPY = '__pypy__' in sys.builtin_module_names _IS_PYPY = '__pypy__' in sys.builtin_module_names
if IS_PYPY or six.PY3: # pragma: no cover if _IS_PYPY or six.PY3: # pragma: no cover
OBJECT_INIT = six.get_unbound_function(object.__init__) _OBJECT_INIT = six.get_unbound_function(object.__init__)
else: # pragma: no cover else: # pragma: no cover
OBJECT_INIT = None _OBJECT_INIT = None
class Injection(object): class Injection(object):
"""Base injection class.""" """Base injection class.
All injections extend this class.
"""
__IS_INJECTION__ = True __IS_INJECTION__ = True
__slots__ = ('injectable', 'is_provider') __slots__ = ('injectable', 'is_provider')
def __init__(self, injectable): def __init__(self, injectable):
"""Initializer.""" """Initializer.
:param injectable: Injectable value, could be provider or any
other object.
:type injectable: object |
:py:class:`dependency_injector.providers.Provider`
"""
self.injectable = injectable self.injectable = injectable
"""Injectable value, could be provider or any other object.
:type: object | :py:class:`dependency_injector.providers.Provider`
"""
self.is_provider = is_provider(injectable) self.is_provider = is_provider(injectable)
"""Flag that is set to ``True`` if injectable value is provider.
:type: bool
"""
super(Injection, self).__init__()
@property @property
def value(self): def value(self):
"""Return injectable value.""" """Read-only property that represents injectable value.
Injectable values are provided "as is", except of providers
(subclasses of :py:class:`dependency_injector.providers.Provider`).
Providers will be called every time, when injection needs to be done.
:rtype: object
"""
if self.is_provider: if self.is_provider:
return self.injectable() return self.injectable()
return self.injectable return self.injectable
class NamedInjection(Injection): class _NamedInjection(Injection):
"""Base class of named injections.""" """Base class of named injections."""
__slots__ = ('name',) __slots__ = ('name',)
@ -47,7 +74,7 @@ class NamedInjection(Injection):
def __init__(self, name, injectable): def __init__(self, name, injectable):
"""Initializer.""" """Initializer."""
self.name = name self.name = name
super(NamedInjection, self).__init__(injectable) super(_NamedInjection, self).__init__(injectable)
class Arg(Injection): class Arg(Injection):
@ -56,19 +83,19 @@ class Arg(Injection):
__IS_ARG_INJECTION__ = True __IS_ARG_INJECTION__ = True
class KwArg(NamedInjection): class KwArg(_NamedInjection):
"""Keyword argument injection.""" """Keyword argument injection."""
__IS_KWARG_INJECTION__ = True __IS_KWARG_INJECTION__ = True
class Attribute(NamedInjection): class Attribute(_NamedInjection):
"""Attribute injection.""" """Attribute injection."""
__IS_ATTRIBUTE_INJECTION__ = True __IS_ATTRIBUTE_INJECTION__ = True
class Method(NamedInjection): class Method(_NamedInjection):
"""Method injection.""" """Method injection."""
__IS_METHOD_INJECTION__ = True __IS_METHOD_INJECTION__ = True
@ -77,7 +104,48 @@ class Method(NamedInjection):
def inject(*args, **kwargs): def inject(*args, **kwargs):
"""Dependency injection decorator. """Dependency injection decorator.
:return: (callable) -> (callable) :py:func:`inject` decorator can be used for making inline dependency
injections. It patches decorated callable in such way that dependency
injection will be done during every call of decorated callable.
:py:func:`inject` decorator supports different syntaxes of passing
injections:
.. code-block:: python
# Positional arguments injections (simplified syntax):
@inject(1, 2)
def some_function(arg1, arg2):
pass
# Keyword arguments injections (simplified syntax):
@inject(arg1=1)
@inject(arg2=2)
def some_function(arg1, arg2):
pass
# Keyword arguments injections (extended (full) syntax):
@inject(KwArg('arg1', 1))
@inject(KwArg('arg2', 2))
def some_function(arg1, arg2):
pass
# Keyword arguments injections into class init (simplified syntax):
@inject(arg1=1)
@inject(arg2=2)
class SomeClass(object):
def __init__(self, arg1, arg2):
pass
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:return: Class / callable decorator
:rtype: (callable) -> (type | callable)
""" """
arg_injections = _parse_args_injections(args) arg_injections = _parse_args_injections(args)
kwarg_injections = _parse_kwargs_injections(args, kwargs) kwarg_injections = _parse_kwargs_injections(args, kwargs)
@ -88,7 +156,7 @@ def inject(*args, **kwargs):
cls = callback_or_cls cls = callback_or_cls
try: try:
cls_init = six.get_unbound_function(cls.__init__) cls_init = six.get_unbound_function(cls.__init__)
assert cls_init is not OBJECT_INIT assert cls_init is not _OBJECT_INIT
except (AttributeError, AssertionError): except (AttributeError, AssertionError):
raise Error( raise Error(
'Class {0}.{1} has no __init__() '.format(cls.__module__, 'Class {0}.{1} has no __init__() '.format(cls.__module__,

View File

@ -16,7 +16,45 @@ from .errors import Error
class Provider(object): class Provider(object):
"""Base provider class.""" """Base provider class.
:py:class:`Provider` is callable (implements ``__call__`` method). Every
call to provider object returns provided result, according to the providing
strategy of particular provider. This ``callable`` functionality is a
regular part of providers API and it should be the same for all provider's
subclasses.
Implementation of particular providing strategy should be done in
:py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
method is called every time when not overridden provider is called.
:py:class:`Provider` implements provider overriding logic that should be
also common for all providers:
.. code-block:: python
provider1 = Factory(SomeClass)
provider2 = Factory(ChildSomeClass)
provider1.override(provider2)
some_instance = provider1()
assert isinstance(some_instance, ChildSomeClass)
Also :py:class:`Provider` implements helper function for creating its
delegates:
.. code-block:: python
provider = Factory(object)
delegate = provider.delegate()
delegated = delegate()
assert provider is delegated
All providers should extend this class.
"""
__IS_PROVIDER__ = True __IS_PROVIDER__ = True
__slots__ = ('overridden_by',) __slots__ = ('overridden_by',)
@ -24,9 +62,23 @@ class Provider(object):
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer."""
self.overridden_by = None self.overridden_by = None
"""Tuple of overriding providers, if any.
:type: tuple[:py:class:`Provider`] | None
"""
super(Provider, self).__init__()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
Implementation of current method adds ``callable`` functionality for
providers API and it should be common for all provider's subclasses.
Also this method implements provider overriding logic that is also
common for all providers. Implementation of particular providing
strategy should be done in :py:meth:`Provider._provide` of
:py:class:`Provider` subclass.
"""
if self.overridden_by: if self.overridden_by:
return self.last_overriding(*args, **kwargs) return self.last_overriding(*args, **kwargs)
return self._provide(*args, **kwargs) return self._provide(*args, **kwargs)
@ -40,12 +92,30 @@ class Provider(object):
""" """
raise NotImplementedError() raise NotImplementedError()
def delegate(self): @property
"""Return provider's delegate.""" def is_overridden(self):
return Delegate(self) """Read-only property that is set to ``True`` if provider is overridden.
:rtype: bool
"""
return bool(self.overridden_by)
@property
def last_overriding(self):
"""Read-only reference to the last overriding provider, if any.
:type: :py:class:`Provider` | None
"""
return self.overridden_by[-1] if self.overridden_by else None
def override(self, provider): def override(self, provider):
"""Override provider with another provider.""" """Override provider with another provider.
:param provider: Overriding provider.
:type provider: :py:class:`Provider`
:raise: :py:exc:`dependency_injector.errors.Error`
"""
if provider is self: if provider is self:
raise Error('Provider {0} could not be overridden ' raise Error('Provider {0} could not be overridden '
'with itself'.format(self)) 'with itself'.format(self))
@ -54,74 +124,174 @@ class Provider(object):
else: else:
self.overridden_by += (ensure_is_provider(provider),) self.overridden_by += (ensure_is_provider(provider),)
@property
def is_overridden(self):
"""Check if provider is overridden by another provider."""
return bool(self.overridden_by)
@property
def last_overriding(self):
"""Return last overriding provider."""
try:
return self.overridden_by[-1]
except (TypeError, IndexError):
raise Error('Provider {0} is not overridden'.format(str(self)))
def reset_last_overriding(self): def reset_last_overriding(self):
"""Reset last overriding provider.""" """Reset last overriding provider.
if not self.is_overridden:
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
overridden.
:rtype: None
"""
if not self.overridden_by:
raise Error('Provider {0} is not overridden'.format(str(self))) raise Error('Provider {0} is not overridden'.format(str(self)))
self.overridden_by = self.overridden_by[:-1] self.overridden_by = self.overridden_by[:-1]
def reset_override(self): def reset_override(self):
"""Reset all overriding providers.""" """Reset all overriding providers.
:rtype: None
"""
self.overridden_by = None self.overridden_by = None
def delegate(self):
"""Return provider's delegate.
:rtype: :py:class:`Delegate`
"""
return Delegate(self)
class Delegate(Provider): class Delegate(Provider):
"""Provider's delegate.""" """:py:class:`Delegate` provider delegates another provider.
.. code-block:: python
provider = Factory(object)
delegate = Delegate(provider)
delegated = delegate()
assert provider is delegated
"""
__slots__ = ('delegated',) __slots__ = ('delegated',)
def __init__(self, delegated): def __init__(self, delegated):
"""Initializer. """Initializer.
:type delegated: Provider :provider delegated: Delegated provider.
:type delegated: :py:class:`Provider`
""" """
self.delegated = ensure_is_provider(delegated) self.delegated = ensure_is_provider(delegated)
super(Delegate, self).__init__() super(Delegate, self).__init__()
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.delegated return self.delegated
class Factory(Provider): class Factory(Provider):
"""Factory provider. """:py:class:`Factory` provider creates new instance on every call.
Factory provider creates new instance of specified class on every call. :py:class:`Factory` supports different syntaxes of passing injections:
.. code-block:: python
# simplified syntax for passing positional and keyword argument
# injections only:
factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4)
# extended (full) syntax for passing any type of injections:
factory = Factory(SomeClass,
injections.Arg(1),
injections.Arg(2),
injections.KwArg('some_arg', 3),
injections.KwArg('other_arg', 4),
injections.Attribute('some_attribute', 5))
Retrieving of provided instance can be performed via calling
:py:class:`Factory` object:
.. code-block:: python
factory = Factory(SomeClass,
some_arg1=1,
some_arg2=2)
some_object = factory()
""" """
__slots__ = ('provides', 'args', 'kwargs', 'attributes', 'methods') __slots__ = ('provides', 'args', 'kwargs', 'attributes', 'methods')
def __init__(self, provides, *args, **kwargs): def __init__(self, provides, *args, **kwargs):
"""Initializer.""" """Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
if not callable(provides): if not callable(provides):
raise Error('Factory provider expects to get callable, ' + raise Error('Factory provider expects to get callable, ' +
'got {0} instead'.format(str(provides))) 'got {0} instead'.format(str(provides)))
self.provides = provides self.provides = provides
"""Class or other callable that provides object for creation.
:type: type | callable
"""
self.args = _parse_args_injections(args) self.args = _parse_args_injections(args)
"""Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
"""
self.kwargs = _parse_kwargs_injections(args, kwargs) self.kwargs = _parse_kwargs_injections(args, kwargs)
"""Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
"""
self.attributes = tuple(injection self.attributes = tuple(injection
for injection in args for injection in args
if is_attribute_injection(injection)) if is_attribute_injection(injection))
"""Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
"""
self.methods = tuple(injection self.methods = tuple(injection
for injection in args for injection in args
if is_method_injection(injection)) if is_method_injection(injection))
"""Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
super(Factory, self).__init__() super(Factory, self).__init__()
@property
def injections(self):
"""Read-only tuple of all injections.
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
"""
return self.args + self.kwargs + self.attributes + self.methods
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
instance = self.provides(*_get_injectable_args(args, self.args), instance = self.provides(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs)) **_get_injectable_kwargs(kwargs, self.kwargs))
for attribute in self.attributes: for attribute in self.attributes:
@ -131,48 +301,145 @@ class Factory(Provider):
return instance return instance
@property
def injections(self):
"""Return tuple of all injections."""
return self.args + self.kwargs + self.attributes + self.methods
class Singleton(Provider): class Singleton(Provider):
"""Singleton provider. """:py:class:`Singleton` provider returns same instance on every call.
:py:class:`Singleton` provider creates instance once and return it on every
call. :py:class:`Singleton` uses :py:class:`Factory` for creation of
instance, so, please follow :py:class:`Factory` documentation to go inside
with injections syntax.
:py:class:`Singleton` is thread-safe and could be used in multithreading
environment without any negative impact.
Retrieving of provided instance can be performed via calling
:py:class:`Singleton` object:
.. code-block:: python
singleton = Singleton(SomeClass,
some_arg1=1,
some_arg2=2)
some_object = singleton()
Singleton provider will create instance once and return it on every call.
""" """
__slots__ = ('instance', 'factory') __slots__ = ('instance', 'factory')
def __init__(self, provides, *args, **kwargs): def __init__(self, provides, *args, **kwargs):
"""Initializer.""" """Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
self.instance = None self.instance = None
"""Read-only reference to singleton's instance.
:type: object
"""
self.factory = Factory(provides, *args, **kwargs) self.factory = Factory(provides, *args, **kwargs)
"""Singleton's factory object.
:type: :py:class:`Factory`
"""
super(Singleton, self).__init__() super(Singleton, self).__init__()
@property
def provides(self):
"""Class or other callable that provides object for creation.
:type: type | callable
"""
return self.factory.provides
@property
def args(self):
"""Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
"""
return self.factory.args
@property
def kwargs(self):
"""Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
"""
return self.factory.kwargs
@property
def attributes(self):
"""Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
"""
return self.factory.attributes
@property
def methods(self):
"""Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
return self.factory.methods
@property
def injections(self):
"""Read-only tuple of all injections.
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
"""
return self.factory.injections
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.instance = None
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
with GLOBAL_LOCK: with GLOBAL_LOCK:
if not self.instance: if not self.instance:
self.instance = self.factory(*args, **kwargs) self.instance = self.factory(*args, **kwargs)
return self.instance return self.instance
def reset(self):
"""Reset instance."""
self.instance = None
@property
def injections(self):
"""Return tuple of all injections."""
return self.factory.injections
class ExternalDependency(Provider): class ExternalDependency(Provider):
"""External dependency provider. """:py:class:`ExternalDependency` provider describes dependency interface.
Those provider is used when dependency obviously have to be overridden by This provider is used for description of dependency interface. That might
the client's code, but it's interface is known. be useful when dependency could be provided in the client's code only,
but it's interface is known. Such situations could happen when required
dependency has non-determenistic list of dependencies itself.
.. code-block:: python
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
database_provider.override(Factory(sqlite3.connect, ':memory:'))
database = database_provider()
""" """
__slots__ = ('instance_of',) __slots__ = ('instance_of',)
@ -183,10 +450,25 @@ class ExternalDependency(Provider):
raise Error('ExternalDependency provider expects to get class, ' + raise Error('ExternalDependency provider expects to get class, ' +
'got {0} instead'.format(str(instance_of))) 'got {0} instead'.format(str(instance_of)))
self.instance_of = instance_of self.instance_of = instance_of
"""Class of required dependency.
:type: type
"""
super(ExternalDependency, self).__init__() super(ExternalDependency, self).__init__()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: object
"""
if not self.is_overridden: if not self.is_overridden:
raise Error('Dependency is not defined') raise Error('Dependency is not defined')
@ -199,97 +481,207 @@ class ExternalDependency(Provider):
return instance return instance
def provided_by(self, provider): def provided_by(self, provider):
"""Set external dependency provider.""" """Set external dependency provider.
:param provider: Provider that provides required dependency.
:type provider: :py:class:`Provider`
:rtype: None
"""
return self.override(provider) return self.override(provider)
class StaticProvider(Provider): class StaticProvider(Provider):
"""Static provider. """:py:class:`StaticProvider` returns provided instance "as is".
Static provider is base implementation that provides exactly the same as :py:class:`StaticProvider` is base implementation that provides exactly
it got on input. the same as it got on input.
""" """
__slots__ = ('provides',) __slots__ = ('provides',)
def __init__(self, provides): def __init__(self, provides):
"""Initializer.""" """Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
self.provides = provides self.provides = provides
"""Value that have to be provided.
:type: object
"""
super(StaticProvider, self).__init__() super(StaticProvider, self).__init__()
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.provides return self.provides
class Class(StaticProvider): class Class(StaticProvider):
"""Class provider provides class.""" """:py:class:`Class` returns provided class "as is".
.. code-block:: python
cls_provider = Class(object)
object_cls = cls_provider()
"""
class Object(StaticProvider): class Object(StaticProvider):
"""Object provider provides object.""" """:py:class:`Object` returns provided object "as is".
.. code-block:: python
object_provider = Object(object())
object_instance = object_provider()
"""
class Function(StaticProvider): class Function(StaticProvider):
"""Function provider provides function.""" """:py:class:`Function` returns provided function "as is".
.. code-block:: python
function_provider = Function(len)
len_function = function_provider()
"""
class Value(StaticProvider): class Value(StaticProvider):
"""Value provider provides value.""" """:py:class:`Value` returns provided value "as is".
.. code-block:: python
value_provider = Value(31337)
value = value_provider()
"""
class Callable(Provider): class Callable(Provider):
"""Callable provider. """:py:class:`Callable` provider calls wrapped callable on every call.
Callable provider provides callable that is called on every provider call :py:class:`Callable` provider provides callable that is called on every
with some predefined dependency injections. provider call with some predefined dependency injections.
:py:class:`Callable` syntax of passing injections is the same like
:py:class:`Factory` one:
.. code-block:: python
some_function = Callable(some_function, 'arg1', 'arg2', arg3=3, arg4=4)
result = some_function()
""" """
__slots__ = ('callback', 'args', 'kwargs') __slots__ = ('callback', 'args', 'kwargs')
def __init__(self, callback, *args, **kwargs): def __init__(self, callback, *args, **kwargs):
"""Initializer.""" """Initializer.
:param callback: Wrapped callable.
:type callback: callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
if not callable(callback): if not callable(callback):
raise Error('Callable expected, got {0}'.format(str(callback))) raise Error('Callable expected, got {0}'.format(str(callback)))
self.callback = callback self.callback = callback
self.args = _parse_args_injections(args) """Wrapped callable.
self.kwargs = _parse_kwargs_injections(args, kwargs)
super(Callable, self).__init__()
def _provide(self, *args, **kwargs): :type: callable
"""Return provided instance.""" """
return self.callback(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs)) self.args = _parse_args_injections(args)
"""Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
"""
self.kwargs = _parse_kwargs_injections(args, kwargs)
"""Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
"""
super(Callable, self).__init__()
@property @property
def injections(self): def injections(self):
"""Return tuple of all injections.""" """Read-only tuple of all injections.
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
"""
return self.args + self.kwargs return self.args + self.kwargs
def _provide(self, *args, **kwargs):
"""Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.callback(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs))
class Config(Provider): class Config(Provider):
"""Config provider. """:py:class:`Config` provider provide dict values.
Config provider provides dict values. Also config provider creates :py:class:`Config` provider creates :py:class:`ChildConfig` objects for all
child config objects for all undefined attribute calls. It makes possible undefined attribute calls. It makes possible to create deferred config
to create deferred config value provider. value providers. It might be useful in cases where it is needed to
define / pass some configuration in declarative manner, while
configuration values will be loaded / updated in application's runtime.
""" """
__slots__ = ('value',) __slots__ = ('value',)
def __init__(self, value=None): def __init__(self, value=None):
"""Initializer.""" """Initializer.
:param value: Configuration dictionary.
:type value: dict[str, object]
"""
if not value: if not value:
value = dict() value = dict()
self.value = value self.value = value
super(Config, self).__init__() super(Config, self).__init__()
def __getattr__(self, item): def __getattr__(self, item):
"""Return instance of deferred config.""" """Return instance of deferred config.
:param item: Name of configuration option or section.
:type item: str
:rtype: :py:class:`ChildConfig`
"""
return ChildConfig(parents=(item,), root_config=self) return ChildConfig(parents=(item,), root_config=self)
def _provide(self, paths=None): def _provide(self, paths=None):
"""Return provided instance.""" """Return provided instance.
:param paths: Tuple of pieces of configuration option / section path.
:type args: tuple[str]
:rtype: object
"""
value = self.value value = self.value
if paths: if paths:
for path in paths: for path in paths:
@ -301,30 +693,69 @@ class Config(Provider):
return value return value
def update_from(self, value): def update_from(self, value):
"""Update current value from another one.""" """Update current value from another one.
:param value: Configuration dictionary.
:type value: dict[str, object]
:rtype: None
"""
self.value.update(value) self.value.update(value)
class ChildConfig(Provider): class ChildConfig(Provider):
"""Child config provider. """:py:class:`ChildConfig` provider provides value from :py:class:`Config`.
Child config provide an value from the root config object according to :py:class:`ChildConfig` provides value from the root config object
the current path in the config tree. according to the current path in the config tree.
""" """
__slots__ = ('parents', 'root_config') __slots__ = ('parents', 'root_config')
def __init__(self, parents, root_config): def __init__(self, parents, root_config):
"""Initializer.""" """Initializer.
:param parents: Tuple of pieces of configuration option / section
parent path.
:type parents: tuple[str]
:param root_config: Root configuration object.
:type root_config: :py:class:`Config`
"""
self.parents = parents self.parents = parents
"""Tuple of pieces of configuration option / section parent path.
:type: tuple[str]
"""
self.root_config = root_config self.root_config = root_config
"""Root configuration object.
:type: :py:class:`Config`
"""
super(ChildConfig, self).__init__() super(ChildConfig, self).__init__()
def __getattr__(self, item): def __getattr__(self, item):
"""Return instance of deferred config.""" """Return instance of deferred config.
:param item: Name of configuration option or section.
:type item: str
:rtype: :py:class:`ChildConfig`
"""
return ChildConfig(parents=self.parents + (item,), return ChildConfig(parents=self.parents + (item,),
root_config=self.root_config) root_config=self.root_config)
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.root_config(self.parents) return self.root_config(self.parents)

View File

@ -8,10 +8,20 @@ from .errors import Error
GLOBAL_LOCK = threading.RLock() GLOBAL_LOCK = threading.RLock()
"""Dependency injector global reentrant lock.
:type: :py:class:`threading.RLock`
"""
def is_provider(instance): def is_provider(instance):
"""Check if instance is provider instance.""" """Check if instance is provider instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_PROVIDER__') and hasattr(instance, '__IS_PROVIDER__') and
getattr(instance, '__IS_PROVIDER__') is True) getattr(instance, '__IS_PROVIDER__') is True)
@ -20,7 +30,13 @@ def is_provider(instance):
def ensure_is_provider(instance): def ensure_is_provider(instance):
"""Check if instance is provider instance and return it. """Check if instance is provider instance and return it.
:raise: Error if provided instance is not provider. :param instance: Instance to be checked.
:type instance: object
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
not provider.
:rtype: :py:class:`dependency_injector.providers.Provider`
""" """
if not is_provider(instance): if not is_provider(instance):
raise Error('Expected provider instance, ' raise Error('Expected provider instance, '
@ -29,14 +45,29 @@ def ensure_is_provider(instance):
def is_injection(instance): def is_injection(instance):
"""Check if instance is injection instance.""" """Check if instance is injection instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_INJECTION__') and hasattr(instance, '__IS_INJECTION__') and
getattr(instance, '__IS_INJECTION__') is True) getattr(instance, '__IS_INJECTION__') is True)
def ensure_is_injection(instance): def ensure_is_injection(instance):
"""Check if instance is injection instance, otherwise raise and error.""" """Check if instance is injection instance and return it.
:param instance: Instance to be checked.
:type instance: object
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
not injection.
:rtype: :py:class:`dependency_injector.injections.Injection`
"""
if not is_injection(instance): if not is_injection(instance):
raise Error('Expected injection instance, ' raise Error('Expected injection instance, '
'got {0}'.format(str(instance))) 'got {0}'.format(str(instance)))
@ -44,51 +75,99 @@ def ensure_is_injection(instance):
def is_arg_injection(instance): def is_arg_injection(instance):
"""Check if instance is positional argument injection instance.""" """Check if instance is positional argument injection instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_ARG_INJECTION__') and hasattr(instance, '__IS_ARG_INJECTION__') and
getattr(instance, '__IS_ARG_INJECTION__', False) is True) getattr(instance, '__IS_ARG_INJECTION__', False) is True)
def is_kwarg_injection(instance): def is_kwarg_injection(instance):
"""Check if instance is keyword argument injection instance.""" """Check if instance is keyword argument injection instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_KWARG_INJECTION__') and hasattr(instance, '__IS_KWARG_INJECTION__') and
getattr(instance, '__IS_KWARG_INJECTION__', False) is True) getattr(instance, '__IS_KWARG_INJECTION__', False) is True)
def is_attribute_injection(instance): def is_attribute_injection(instance):
"""Check if instance is attribute injection instance.""" """Check if instance is attribute injection instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_ATTRIBUTE_INJECTION__') and hasattr(instance, '__IS_ATTRIBUTE_INJECTION__') and
getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True) getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True)
def is_method_injection(instance): def is_method_injection(instance):
"""Check if instance is method injection instance.""" """Check if instance is method injection instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_METHOD_INJECTION__') and hasattr(instance, '__IS_METHOD_INJECTION__') and
getattr(instance, '__IS_METHOD_INJECTION__', False) is True) getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
def is_catalog(instance): def is_catalog(instance):
"""Check if instance is catalog instance.""" """Check if instance is catalog instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (hasattr(instance, '__IS_CATALOG__') and return (hasattr(instance, '__IS_CATALOG__') and
getattr(instance, '__IS_CATALOG__', False) is True) getattr(instance, '__IS_CATALOG__', False) is True)
def is_dynamic_catalog(instance): def is_dynamic_catalog(instance):
"""Check if instance is dynamic catalog instance.""" """Check if instance is dynamic catalog instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and is_catalog(instance)) return (not isinstance(instance, six.class_types) and is_catalog(instance))
def is_declarative_catalog(instance): def is_declarative_catalog(instance):
"""Check if instance is declarative catalog instance.""" """Check if instance is declarative catalog instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (isinstance(instance, six.class_types) and is_catalog(instance)) return (isinstance(instance, six.class_types) and is_catalog(instance))
def is_catalog_bundle(instance): def is_catalog_bundle(instance):
"""Check if instance is catalog bundle instance.""" """Check if instance is catalog bundle instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (not isinstance(instance, six.class_types) and return (not isinstance(instance, six.class_types) and
hasattr(instance, '__IS_CATALOG_BUNDLE__') and hasattr(instance, '__IS_CATALOG_BUNDLE__') and
getattr(instance, '__IS_CATALOG_BUNDLE__', False) is True) getattr(instance, '__IS_CATALOG_BUNDLE__', False) is True)
@ -97,7 +176,13 @@ def is_catalog_bundle(instance):
def ensure_is_catalog_bundle(instance): def ensure_is_catalog_bundle(instance):
"""Check if instance is catalog bundle instance and return it. """Check if instance is catalog bundle instance and return it.
:raise: Error if provided instance is not catalog bundle. :param instance: Instance to be checked.
:type instance: object
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance
is not catalog bundle.
:rtype: :py:class:`dependency_injector.catalogs.CatalogBundle`
""" """
if not is_catalog_bundle(instance): if not is_catalog_bundle(instance):
raise Error('Expected catalog bundle instance, ' raise Error('Expected catalog bundle instance, '

View File

@ -7,28 +7,35 @@ Current section of documentation describes advanced usage of
@inject decorator @inject decorator
----------------- -----------------
``@di.inject()`` decorator can be used for making *inline* dependency .. module:: dependency_injector.injections
:py:func:`inject` decorator is a part of
:py:mod:`dependency_injector.injections` module.
:py:func:`inject` decorator can be used for making *inline* dependency
injections. It *patches* decorated callable in such way that dependency injections. It *patches* decorated callable in such way that dependency
injection will be done during every call of decorated callable. injection will be done during every call of decorated callable.
``di.inject()`` takes a various number of positional and keyword arguments :py:func:`inject` takes a various number of positional and keyword arguments
that are used as decorated callable injections. Every time, when that are used as decorated callable injections. Every time, when
``di.inject()`` is called, positional and keyword argument injections would be :py:func:`inject` is called, positional and keyword argument injections would
passed as an callable arguments. be passed as an callable arguments.
Such behaviour is very similar to the standard Python ``functools.partial`` Such behaviour is very similar to the standard Python ``functools.partial``
object, except of one thing: all injectable values are provided object, except of one thing: all injectable values are provided
*"as is"*, except of providers (subclasses of ``di.Provider``). Providers *"as is"*, except of providers (subclasses of
:py:class:`dependency_injector.providers.Provider`). Providers
will be called every time, when injection needs to be done. For example, will be called every time, when injection needs to be done. For example,
if injectable value of injection is a ``di.Factory``, it will provide new one if injectable value of injection is a
:py:class:`dependency_injector.providers.Factory`, it will provide new one
instance (as a result of its call) every time, when injection needs to be done. instance (as a result of its call) every time, when injection needs to be done.
``di.inject()`` behaviour with context positional and keyword arguments is :py:func:`inject` behaviour with context positional and keyword arguments is
very like a standard Python ``functools.partial``: very like a standard Python ``functools.partial``:
- Positional context arguments will be appended after ``di.inject()`` - Positional context arguments will be appended after :py:func:`inject`
positional injections. positional injections.
- Keyword context arguments have priority on ``di.inject()`` keyword - Keyword context arguments have priority on :py:func:`inject` keyword
injections and will be merged over them. injections and will be merged over them.
Example: Example:
@ -36,7 +43,7 @@ Example:
.. literalinclude:: ../../examples/advanced_usage/inject_simple.py .. literalinclude:: ../../examples/advanced_usage/inject_simple.py
:language: python :language: python
Example of usage ``@di.inject()`` decorator with Flask: Example of usage :py:func:`inject` decorator with Flask:
.. literalinclude:: ../../examples/advanced_usage/inject_flask.py .. literalinclude:: ../../examples/advanced_usage/inject_flask.py
:language: python :language: python
@ -45,11 +52,12 @@ Example of usage ``@di.inject()`` decorator with Flask:
@inject decorator with classes @inject decorator with classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``@di.inject()`` could be applied for classes. In such case, it will look for :py:func:`inject` could be applied for classes. In such case, it will look for
class ``__init__()`` method and pass injection to it. If decorated class has class ``__init__()`` method and pass injection to it. If decorated class has
no ``__init__()`` method, appropriate ``di.Error`` will be raised. no ``__init__()`` method, appropriate
:py:exc:`dependency_injector.errors.Error` will be raised.
Example of usage ``@di.inject()`` with Flask class-based view: Example of usage :py:func:`inject` with Flask class-based view:
.. literalinclude:: ../../examples/advanced_usage/inject_flask_class_based.py .. literalinclude:: ../../examples/advanced_usage/inject_flask_class_based.py
:language: python :language: python

7
docs/api/catalogs.rst Normal file
View File

@ -0,0 +1,7 @@
``dependency_injector.catalogs``
--------------------------------
.. automodule:: dependency_injector.catalogs
:members:
:member-order: bysource
:special-members:

6
docs/api/errors.rst Normal file
View File

@ -0,0 +1,6 @@
``dependency_injector.errors``
------------------------------
.. automodule:: dependency_injector.errors
:members:
:member-order: bysource

12
docs/api/index.rst Normal file
View File

@ -0,0 +1,12 @@
API Documentation
=================
.. toctree::
:maxdepth: 2
top_level
providers
injections
catalogs
utils
errors

7
docs/api/injections.rst Normal file
View File

@ -0,0 +1,7 @@
``dependency_injector.injections``
----------------------------------
.. automodule:: dependency_injector.injections
:members:
:member-order: bysource
:inherited-members:

7
docs/api/providers.rst Normal file
View File

@ -0,0 +1,7 @@
``dependency_injector.providers``
---------------------------------
.. automodule:: dependency_injector.providers
:members:
:member-order: bysource
:inherited-members:

5
docs/api/top_level.rst Normal file
View File

@ -0,0 +1,5 @@
``dependency_injector``
-----------------------
.. automodule:: dependency_injector
:members: VERSION

6
docs/api/utils.rst Normal file
View File

@ -0,0 +1,6 @@
``dependency_injector.utils``
-----------------------------
.. automodule:: dependency_injector.utils
:members:
:member-order: bysource

View File

@ -1,23 +1,43 @@
Creating catalog provider bundles Catalog provider bundles
--------------------------------- ------------------------
``di.DeclarativeCatalog.Bundle`` is a limited collection of catalog providers. .. module:: dependency_injector.catalogs
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.
``di.DeclarativeCatalog.Bundle`` has exactly the same API as :py:class:`CatalogBundle` is a frozen, limited collection of catalog
``di.DeclarativeCatalog`` except of the limitations on getting providers. 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 - :py:class:`CatalogBundle` has API's parity with catalogs
``di.DeclarativeCatalog.Bundle``. For example, if some concrete catalog has name (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of
``SomeCatalog``, then its bundle class could be reached as retrieving the providers, but it is "frozen" in terms of modification
``SomeCatalog.Bundle``. 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 as positional arguments and will limit the scope of created bundle to this
list. 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: Example:
.. image:: /images/catalogs/bundles.png .. image:: /images/catalogs/bundles.png

View File

@ -0,0 +1,61 @@
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).
Declarative catalogs have to extend base declarative catalog class -
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
Declarative catalog's providers have to be defined like catalog's class
attributes. Every provider in catalog has name. This name should follow
``some_provider`` convention, that is standard naming convention for
attribute names in Python.
.. note::
Declarative catalogs have several features that could be useful
for some kind of operations on catalog's providers, please visit API
documentation for getting full list of features -
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
.. note::
It might be useful to add such
``""":type: dependency_injector.providers.Provider -> Object1"""``
docstrings just on the next line after provider's definition. It will
help code analyzing tools and IDE's to understand that variable above
contains some callable object, that returns particular instance as a
result of its call.
Here is an simple example of defining declarative catalog with several
factories:
.. image:: /images/catalogs/declarative.png
:width: 85%
:align: center
.. literalinclude:: ../../examples/catalogs/declarative.py
:language: python
Example of declarative catalogs inheritance:
.. image:: /images/catalogs/declarative_inheritance.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/catalogs/declarative_inheritance.py
:language: python
Example of declarative catalog's provider injections:
.. image:: /images/catalogs/declarative_injections.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/catalogs/declarative_injections.py
:language: python

29
docs/catalogs/dynamic.rst Normal file
View File

@ -0,0 +1,29 @@
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

View File

@ -16,10 +16,12 @@ Also, for both of these and some other cases, it might be useful to attach
some init / shutdown functionality or something else, that deals with group some init / shutdown functionality or something else, that deals with group
of providers. of providers.
Catalogs module API docs - :py:mod:`dependency_injector.catalogs`.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
writing declarative
operating dynamic
bundles bundles
overriding overriding

View File

@ -1,30 +0,0 @@
Operating with catalogs
-----------------------
``di.DeclarativeCatalog`` has several features that could be useful for some
kind of operations on catalog's providers:
- ``di.DeclarativeCatalog.providers`` is read-only attribute that contains
``dict`` of all catalog providers, including providers that are inherited
from parent catalogs, where key is the name of provider and value is
provider itself.
- ``di.DeclarativeCatalog.cls_providers`` is read-only attribute contains
``dict`` of current catalog providers, where key is the name of provider
and value is provider itself.
- ``di.DeclarativeCatalog.inherited_providers`` is read-only attribute
contains ``dict`` of all providers that are inherited from parent catalogs,
where key is the name of provider and value is provider itself.
- ``di.DeclarativeCatalog.filter(provider_type=di.Provider)`` is a class
method that could be used for filtering catalog providers by provider types
(for example, for getting all ``di.Factory`` providers).
``di.DeclarativeCatalog.filter()`` method use
``di.DeclarativeCatalog.providers``.
Example:
.. image:: /images/catalogs/operating_with_providers.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/catalogs/operating_with_providers.py
:language: python

View File

@ -1,35 +1,49 @@
Overriding of catalogs Overriding of catalogs
---------------------- ----------------------
.. module:: dependency_injector.catalogs
Catalogs can be overridden by other catalogs. This, actually, means that Catalogs can be overridden by other catalogs. This, actually, means that
all of the providers from overriding catalog will override providers with the all of the providers from overriding catalog will override providers with the
same names in overridden catalog. same names in overridden catalog.
There are two ways to override catalog by another catalog: There are two ways to override :py:class:`DeclarativeCatalog` with another
catalog:
- Use ``di.DeclarativeCatalog.override(AnotherCatalog)`` method. - Use :py:meth:`DeclarativeCatalog.override` method.
- Use ``@di.override(AnotherCatalog)`` class decorator. - Use :py:func:`override` class decorator.
Example of overriding catalog using ``di.DeclarativeCatalog.override()`` Example of overriding catalog using :py:meth:`DeclarativeCatalog.override`
method: method:
.. literalinclude:: ../../examples/catalogs/override.py .. literalinclude:: ../../examples/catalogs/override_declarative.py
:language: python :language: python
Example of overriding catalog using ``@di.override()`` decorator: Example of overriding catalog using :py:func:`override` decorator:
.. literalinclude:: ../../examples/catalogs/override_decorator.py .. literalinclude:: ../../examples/catalogs/override_declarative_decorator.py
:language: python :language: python
Also there are several useful methods and properties that help to work with Also there are several useful :py:class:`DeclarativeCatalog` methods and
catalog overridings: properties that help to work with catalog overridings:
- ``di.DeclarativeCatalog.is_overridden`` - read-only, evaluated in runtime, - :py:attr:`DeclarativeCatalog.is_overridden` - read-only property that is set
property that is set to True if catalog is overridden. to ``True`` if catalog is overridden.
- ``di.DeclarativeCatalog.last_overriding`` - reference to the last overriding - :py:attr:`DeclarativeCatalog.last_overriding` - read-only reference to
catalog, if any. the last overriding catalog, if any.
- ``di.DeclarativeCatalog.overridden_by`` - tuple of all overriding catalogs. - :py:attr:`DeclarativeCatalog.overridden_by` - tuple of all overriding
- ``di.DeclarativeCatalog.reset_last_overriding()`` - reset last overriding catalogs.
catalog. - :py:meth:`DeclarativeCatalog.reset_last_overriding()` - reset last
- ``di.DeclarativeCatalog.reset_override()`` - reset all overridings for all overriding catalog.
catalog providers. - :py:meth:`DeclarativeCatalog.reset_override()` - reset all overridings for
all catalog providers.
:py:class:`DynamicCatalog` has exactly the same functionality, except of
:py:func:`override` decorator. Also :py:class:`DynamicCatalog` can override
:py:class:`DeclarativeCatalog` and vise versa.
Example of overriding :py:class:`DeclarativeCatalog` by
:py:class:`DynamicCatalog`:
.. literalinclude:: ../../examples/catalogs/override_declarative_by_dynamic.py
:language: python

View File

@ -1,25 +0,0 @@
Writing catalogs
----------------
Catalogs have to extend base catalog class ``di.DeclarativeCatalog``.
Providers have to be defined like catalog's attributes. Every provider in
catalog has name. This name should follow ``some_provider`` convention,
that is standard naming convention for attribute names in Python.
.. note::
It might be useful to add such ``""":type: di.Provider -> Object1"""``
docstrings just on the next line after provider's definition. It will
help code analyzing tools and IDE's to understand that variable above
contains some callable object, that returns particular instance as a
result of its call.
Here is an simple example of catalog with several factories:
.. image:: /images/catalogs/writing_catalogs.png
:width: 85%
:align: center
.. literalinclude:: ../../examples/catalogs/writing_catalogs.py
:language: python

View File

@ -28,7 +28,7 @@ import re
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [] extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -55,5 +55,6 @@ Contents
providers/index providers/index
catalogs/index catalogs/index
advanced_usage/index advanced_usage/index
api/index
main/feedback main/feedback
main/changelog main/changelog

View File

@ -9,13 +9,31 @@ follows `Semantic versioning`_
Development version Development version
------------------- -------------------
- Rename ``di.AbstractCatalog`` to ``di.DeclarativeCatalog`` - No features.
0.11.0
------
- Rename ``AbstractCatalog`` to ``DeclarativeCatalog``
(with backward compatibility). (with backward compatibility).
- Rename ``catalog`` module to ``catalogs`` with backward compatibility.
- Implement dynamic binding of providers for ``DeclarativeCatalog``.
- Add ``DynamicCatalog``.
- Change restrictions for providers-to-catalogs bindings - provider could be
bound to several catalogs with different names.
- Restrict overriding of providers by themselves.
- Restrict overriding of catalogs by themselves.
- Make ``DeclarativeCatalog.last_overriding`` attribute to be ``None`` by
default.
- Make ``Provider.last_overriding`` attribute to be ``None`` by
default.
- Refactor catalogs and providers modules.
- Add API documentation
- Improve user's guides and examples.
0.10.5 0.10.5
------ ------
- Add more representable implementation for ``di.AbstractCatalog`` and - Add more representable implementation for ``AbstractCatalog`` and
``di.AbstractCatalog.Bundle``. ``AbstractCatalog.Bundle``.
0.10.4 0.10.4
------ ------
@ -35,22 +53,22 @@ Development version
0.10.0 0.10.0
------ ------
- Add functionality for creating ``di.AbstractCatalog`` provider bundles. - Add functionality for creating ``AbstractCatalog`` provider bundles.
- Improve ``di.AbstractCatalog`` inheritance. - Improve ``AbstractCatalog`` inheritance.
- Improve ``di.AbstractCatalog`` overriding. - Improve ``AbstractCatalog`` overriding.
- Add images for catalog "Writing catalogs" and "Operating with catalogs" - Add images for catalog "Writing catalogs" and "Operating with catalogs"
examples. examples.
- Add functionality for using positional argument injections with - Add functionality for using positional argument injections with
``di.Factory``, ``di.Singleton``, ``di.Callable`` providers and ``Factory``, ``Singleton``, ``Callable`` providers and
``di.inject`` decorator. ``inject`` decorator.
- Add functionality for decorating classes with ``@di.inject``. - Add functionality for decorating classes with ``@inject``.
- Add ``di.Singleton.injections`` attribute that represents a tuple of all - Add ``Singleton.injections`` attribute that represents a tuple of all
``di.Singleton`` injections (including args, kwargs, attributes and methods). ``Singleton`` injections (including args, kwargs, attributes and methods).
- Add ``di.Callable.injections`` attribute that represents a tuple of all - Add ``Callable.injections`` attribute that represents a tuple of all
``di.Callable`` injections (including args and kwargs). ``Callable`` injections (including args and kwargs).
- Add optimization for ``di.Injection.value`` property that will compute - Add optimization for ``Injection.value`` property that will compute
type of injection once, instead of doing this on every call. type of injection once, instead of doing this on every call.
- Add ``di.VERSION`` constant for verification of currently installed version. - Add ``VERSION`` constant for verification of currently installed version.
- Add support of Python 3.5. - Add support of Python 3.5.
- Add support of six 1.10.0. - Add support of six 1.10.0.
- Add minor refactorings and code style fixes. - Add minor refactorings and code style fixes.
@ -58,8 +76,8 @@ Development version
0.9.5 0.9.5
----- -----
- Change provider attributes scope to public. - Change provider attributes scope to public.
- Add ``di.Factory.injections`` attribute that represents a tuple of all - Add ``Factory.injections`` attribute that represents a tuple of all
``di.Factory`` injections (including kwargs, attributes and methods). ``Factory`` injections (including kwargs, attributes and methods).
0.9.4 0.9.4
----- -----

View File

@ -12,7 +12,7 @@ framework can be installed from PyPi_:
pip install dependency_injector pip install dependency_injector
# Installing particular version: # Installing particular version:
pip install dependency_injector==0.9.0 pip install dependency_injector==0.11.0
Sources can be cloned from GitHub_: Sources can be cloned from GitHub_:
@ -23,14 +23,14 @@ Sources can be cloned from GitHub_:
Also all *Dependency Injector* releases can be downloaded from Also all *Dependency Injector* releases can be downloaded from
`GitHub releases page`_. `GitHub releases page`_.
Verification of currently installed version could be done using ``di.VERSION`` Verification of currently installed version could be done using
constant: :py:obj:`dependency_injector.VERSION` constant:
.. code-block:: bash .. code-block:: bash
>>> import dependency_injector as di >>> from dependency_injector import VERSION
>>> di.VERSION >>> VERSION
'0.10.0' '0.11.0'
.. _PyPi: https://pypi.python.org/pypi/dependency_injector .. _PyPi: https://pypi.python.org/pypi/dependency_injector
.. _GitHub: https://github.com/rmk135/dependency_injector .. _GitHub: https://github.com/rmk135/dependency_injector

View File

@ -57,27 +57,6 @@ framework:
Main idea of *Dependency Injector* is to keep dependencies under control. Main idea of *Dependency Injector* is to keep dependencies under control.
Shortcuts
---------
*Dependency Injector* recommends to use such kind of import shortcut:
.. code-block:: python
import dependency_injector as di
- All *Dependency Injector* entities could be used just from top-level package
(like ``di.Factory``, ``di.inject``, ``di.AbstractCatalog`` and so on).
- Another one way is to use second level packages (like
``di.providers.Factory``, ``di.injections.inject``,
``di.catalog.AbstractCatalog`` and so on). It might be useful for improving
of readability in some cases.
.. note::
``import dependency_injector as di`` shortcut is used among current
documentation, images and examples.
Main entities Main entities
------------- -------------
@ -91,18 +70,21 @@ interaction with each other.
There are 3 main entities: There are 3 main entities:
- Providers. Providers are strategies of accesing objects. For example, - Providers. Providers are strategies of accesing objects. For example,
``di.providers.Factory`` creates new instance of provided :py:class:`dependency_injector.providers.Factory` creates new instance of
class every time it is called. ``di.providers.Singleton`` creates provided class every time it is called.
provided instance once and returns it on every next call. Providers :py:class:`dependency_injector.providers.Singleton` creates provided
could be overridden by another providers. Base class is - instance once and returns it on every next call. Providers could be
``di.providers.Provider``. overridden by another providers. Base class is -
:py:class:`dependency_injector.providers.Provider`.
- Injections. Injections are instructions for making dependency injections - Injections. Injections are instructions for making dependency injections
(there are several ways how they could be done). Injections are used mostly (there are several ways how they could be done). Injections are used mostly
by ``di.providers.Factory`` and ``di.providers.Singleton`` providers, but by :py:class:`dependency_injector.providers.Factory` and
these are not only cases. Base class is - ``di.injections.Injection``. :py:class:`dependency_injector.providers.Singleton` providers, but
these are not only cases. Base class is -
:py:class:`dependency_injector.injections.Injection`.
- Catalogs. Catalogs are collections of providers. They are used for grouping - Catalogs. Catalogs are collections of providers. They are used for grouping
of providers by some principles. Base class is - of providers by some principles. Base class is -
``di.catalog.AbstractCatalog``. :py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code .. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code

View File

@ -1,40 +1,43 @@
Callable providers Callable providers
------------------ ------------------
``di.Callable`` provider is a provider that wraps particular callable with .. module:: dependency_injector.providers
:py:class:`Callable` provider is a provider that wraps particular callable with
some injections. Every call of this provider returns result of call of initial some injections. Every call of this provider returns result of call of initial
callable. callable.
Callable providers and injections Callable providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Callable`` takes a various number of positional and keyword arguments :py:class:`Callable` takes a various number of positional and keyword
that are used as decorated callable injections. Every time, when arguments that are used as decorated callable injections. Every time, when
``di.Callable`` is called, positional and keyword argument injections would be :py:class:`Callable` is called, positional and keyword argument injections
passed as an callable arguments. would be passed as an callable arguments.
Such behaviour is very similar to the standard Python ``functools.partial`` Such behaviour is very similar to the standard Python ``functools.partial``
object, except of one thing: all injectable values are provided object, except of one thing: all injectable values are provided
*"as is"*, except of providers (subclasses of ``di.Provider``). Providers *"as is"*, except of providers (subclasses of :py:class:`Provider`). Providers
will be called every time, when injection needs to be done. For example, will be called every time, when injection needs to be done. For example,
if injectable value of injection is a ``di.Factory``, it will provide new one if injectable value of injection is a :py:class:`Factory`, it will provide
instance (as a result of its call) every time, when injection needs to be done. new one instance (as a result of its call) every time, when injection needs
to be done.
``di.Callable`` behaviour with context positional and keyword arguments is :py:class:`Callable` behaviour with context positional and keyword arguments
very like a standard Python ``functools.partial``: is very like a standard Python ``functools.partial``:
- Positional context arguments will be appended after ``di.Callable`` - Positional context arguments will be appended after :py:class:`Callable`
positional injections. positional injections.
- Keyword context arguments have priority on ``di.Callable`` keyword - Keyword context arguments have priority on :py:class:`Callable` keyword
injections and will be merged over them. injections and will be merged over them.
Example that shows usage of ``di.Callable`` with positional argument Example that shows usage of :py:class:`Callable` with positional argument
injections: injections:
.. literalinclude:: ../../examples/providers/callable_args.py .. literalinclude:: ../../examples/providers/callable_args.py
:language: python :language: python
Next one example shows usage of ``di.Callable`` with keyword argument Next one example shows usage of :py:class:`Callable` with keyword argument
injections: injections:
.. image:: /images/providers/callable.png .. image:: /images/providers/callable.png
@ -47,13 +50,13 @@ injections:
Callable providers delegation Callable providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Callable`` provider could be delegated to any other provider via any kind :py:class:`Callable` provider could be delegated to any other provider via any
of injection. Delegation of ``di.Callable`` providers is the same as kind of injection. Delegation of :py:class:`Callable` providers is the same as
``di.Factory`` and ``di.Singleton`` providers delegation, please follow :py:class:`Factory` and :py:class:`Singleton` providers delegation, please
*Factory providers delegation* section for example. follow *Factory providers delegation* section for example.
``di.Callable`` delegate could be created obviously using :py:class:`Callable` delegate could be created obviously using
``di.Delegate(di.Callable())`` or by calling ``di.Callable.delegate()`` method. ``Delegate(Callable(...))`` or by calling ``Callable(...).delegate()`` method.
Example: Example:

View File

@ -1,27 +1,28 @@
Writing custom providers Writing of custom providers
------------------------ ------------------------
.. module:: dependency_injector.providers
List of *Dependency Injector* providers could be widened with custom providers. List of *Dependency Injector* providers could be widened with custom providers.
Below are some tips and recommendations that have to be met: Below are some tips and recommendations that have to be met:
1. Every custom provider has to extend base provider class - 1. Every custom provider has to extend base provider class -
``di.Provider``. :py:class:`Provider`.
2. Cusom provider's ``__init__()`` could be overriden with only condition: 2. Cusom provider's ``__init__()`` could be overriden, but parent's
parent initializer (``di.Provider.__init__()``) has to be called. initializer (:py:meth:`Provider.__init__`) has to be called.
3. Providing strategy has to be implemented in custom provider's 3. Providing strategy has to be implemented in custom provider's
``_provide()`` method. All ``*args`` & ``**kwargs`` that will be :py:meth:`Provider._provide` method. All ``*args`` & ``**kwargs``
recieved by ``di.Provider.__call__()`` will be transefed to custom that will be recieved by :py:meth:`Provider.__call__` will be
provider's ``_provide()``. transefed to custom provider's :py:meth:`Provider._provide`.
4. If custom provider is based on some standard providers, it is better to 4. If custom provider is based on some standard providers, it is better to
use delegation of standard providers, then extending of them. use delegation of standard providers, then extending of them.
5. If custom provider defines any attributes, it is good to list them in 5. If custom provider defines any attributes, it is good to list them in
``__slots__`` attribute (as *Dependency Injector* does). It can save ``__slots__`` attribute (as *Dependency Injector* does). It can save
some memory. some memory.
6. If custom provider deals with injections (e.g. ``di.Factory``, 6. If custom provider deals with injections, it is strongly recommended
``di.Singleton`` providers), it is strongly recommended to be to be consistent with :py:class:`Factory`, :py:class:`Singleton` and
consistent with ``di.Factory``, ``di.Singleton`` and ``di.Callable`` :py:class:`Callable` providers style.
providers style.
Example: Example:

View File

@ -1,7 +1,9 @@
External dependency providers External dependency providers
----------------------------- -----------------------------
``di.ExternalDependency`` provider can be useful for development of .. module:: dependency_injector.providers
:py:class:`ExternalDependency` provider can be useful for development of
self-sufficient libraries / modules / applications that has required external self-sufficient libraries / modules / applications that has required external
dependencies. dependencies.

View File

@ -1,7 +1,10 @@
Factory providers Factory providers
----------------- -----------------
``di.Factory`` provider creates new instance of specified class on every call. .. module:: dependency_injector.providers
:py:class:`Factory` provider creates new instance of specified class on every
call.
Nothing could be better than brief example: Nothing could be better than brief example:
@ -15,21 +18,22 @@ Nothing could be better than brief example:
Factory providers and __init__ injections Factory providers and __init__ injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Factory`` takes a various number of positional and keyword arguments that :py:class:`Factory` takes a various number of positional and keyword arguments
are used as ``__init__()`` injections. Every time, when ``di.Factory`` that are used as ``__init__()`` injections. Every time, when
creates new one instance, positional and keyword argument injections would be :py:class:`Factory` creates new one instance, positional and keyword
passed as an instance's arguments. argument injections would be passed as an instance's arguments.
Such behaviour is very similar to the standard Python ``functools.partial`` Such behaviour is very similar to the standard Python ``functools.partial``
object, except of one thing: all injectable values are provided object, except of one thing: all injectable values are provided
*"as is"*, except of providers (subclasses of ``di.Provider``). Providers *"as is"*, except of providers (subclasses of :py:class:`Provider`). Providers
will be called every time, when injection needs to be done. For example, will be called every time, when injection needs to be done. For example,
if injectable value of injection is a ``di.Factory``, it will provide new one if injectable value of injection is a :py:class:`Factory`, it will provide
instance (as a result of its call) every time, when injection needs to be done. new one instance (as a result of its call) every time, when injection needs
to be done.
Example below is a little bit more complicated. It shows how to create Example below is a little bit more complicated. It shows how to create
``di.Factory`` of particular class with ``__init__()`` argument injections :py:class:`Factory` of particular class with ``__init__()`` argument
which injectable values are also provided by another factories: injections which injectable values are also provided by another factories:
.. note:: .. note::
@ -58,14 +62,14 @@ Example of usage keyword argument injections:
Factory providers and __init__ injections priority Factory providers and __init__ injections priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Next example shows how ``di.Factory`` provider deals with positional and Next example shows how :py:class:`Factory` provider deals with positional and
keyword ``__init__()`` context arguments. In few words, ``di.Factory`` keyword ``__init__()`` context arguments. In few words, :py:class:`Factory`
behaviour here is very like a standard Python ``functools.partial``: behaviour here is very like a standard Python ``functools.partial``:
- Positional context arguments will be appended after ``di.Factory`` - Positional context arguments will be appended after :py:class:`Factory`
positional injections. positional injections.
- Keyword context arguments have priority on ``di.Factory`` keyword injections - Keyword context arguments have priority on :py:class:`Factory` keyword
and will be merged over them. injections and will be merged over them.
So, please, follow the example below: So, please, follow the example below:
@ -82,34 +86,38 @@ Objects can take dependencies in different forms (some objects take init
arguments, other use attributes setting or method calls). It affects how arguments, other use attributes setting or method calls). It affects how
such objects are created and initialized. such objects are created and initialized.
``di.Factory`` provider takes various number of positional and keyword :py:class:`Factory` provider takes various number of positional and keyword
arguments, that define what kinds of dependency injections have to be used. arguments, that define what kinds of dependency injections have to be used.
All of those instructions are defined in ``di.injections`` module and are All of those instructions are defined in
subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There :py:mod:`dependency_injector.injections` module and are subclasses of
are several types of injections that are used by ``di.Factory`` provider: :py:class:`dependency_injector.injections.Injection`. There are several types
of injections that are used by :py:class:`Factory` provider:
+ ``di.Arg`` - injection is done by passing injectable value in object's + :py:class:`dependency_injector.injections.Arg` - injection is done by
``__init__()`` method in time of object's creation as positional argument. passing injectable value in object's ``__init__()`` method in time of
Takes injectable value only. object's creation as positional argument. Takes injectable value only.
+ ``di.KwArg`` - injection is done by passing injectable value in object's + :py:class:`dependency_injector.injections.KwArg` - injection is done by
``__init__()`` method in time of object's creation as keyword argument. passing injectable value in object's ``__init__()`` method in time of
Takes keyword name of ``__init__()`` argument and injectable value. object's creation as keyword argument. Takes keyword name of
+ ``di.Attribute`` - injection is done by setting specified attribute with ``__init__()`` argument and injectable value.
injectable value right after object's creation. Takes attribute's name + :py:class:`dependency_injector.injections.Attribute` - injection is done
and injectable value. by setting specified attribute with injectable value right after
+ ``di.Method`` - injection is done by calling of specified method with object's creation. Takes attribute's name and injectable value.
injectable value right after object's creation and attribute injections + :py:class:`dependency_injector.injections.Method` - injection is done by
are done. Takes method name and injectable value. calling of specified method with injectable value right after object's
creation and attribute injections are done. Takes method name and
injectable value.
All ``di.Injection``'s injectable values are provided *"as is"*, except of All :py:class:`dependency_injector.injections.Injection`'s injectable values
providers (subclasses of ``di.Provider``). Providers will be called every time, are provided *"as is"*, except of providers (subclasses of
when injection needs to be done. :py:class:`Provider`). Providers will be called every time, when injection
needs to be done.
Factory providers and attribute injections Factory providers and attribute injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example below shows how to create ``di.Factory`` of particular class with Example below shows how to create :py:class:`Factory` of particular class with
attribute injections. Those injections are done by setting specified attributes attribute injections. Those injections are done by setting specified attributes
with injectable values right after object's creation. with injectable values right after object's creation.
@ -123,10 +131,10 @@ Example:
Factory providers and method injections Factory providers and method injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Current example shows how to create ``di.Factory`` of particular class with Current example shows how to create :py:class:`Factory` of particular class
method injections. Those injections are done by calling of specified method with method injections. Those injections are done by calling of specified
with injectable value right after object's creation and attribute injections method with injectable value right after object's creation and attribute
are done. injections are done.
Method injections are not very popular in Python due Python best practices Method injections are not very popular in Python due Python best practices
(usage of public attributes instead of setter methods), but they may appear in (usage of public attributes instead of setter methods), but they may appear in
@ -142,21 +150,21 @@ Example:
Factory providers delegation Factory providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Factory`` provider could be delegated to any other provider via any kind :py:class:`Factory` provider could be delegated to any other provider via any
of injection. As it was mentioned earlier, if ``di.Factory`` is injectable kind of injection. As it was mentioned earlier, if :py:class:`Factory` is
value, it will be called every time when injection is done. ``di.Factory`` injectable value, it will be called every time when injection is done.
delegation is performed by wrapping delegated ``di.Factory`` into special :py:class:`Factory` delegation is performed by wrapping delegated
provider type - ``di.Delegate``, that just returns wrapped ``di.Factory``. :py:class:`Factory` into special provider type - :py:class:`Delegate`, that
Saying in other words, delegation of factories - is a way to inject factories just returns wrapped :py:class:`Factory`. Saying in other words, delegation
themselves, instead of results of their calls. of factories - is a way to inject factories themselves, instead of results
of their calls.
Actually, there are two ways of creating factory delegates: Actually, there are two ways of creating factory delegates:
+ ``di.Delegate(di.Factory(...))`` - obviously wrapping factory into + ``Delegate(Factory(...))`` - obviously wrapping factory into
``di.Delegate`` provider. :py:class:`Delegate` provider.
+ ``di.Factory(...).delegate()`` - calling factory ``delegate()`` method, that + ``Factory(...).delegate()`` - calling factory :py:meth:`Factory.delegate`
returns delegate wrapper for current factory. method, that returns delegate wrapper for current factory.
Example: Example:

View File

@ -4,17 +4,21 @@ Providers
Providers are strategies of accessing objects. They describe how particular Providers are strategies of accessing objects. They describe how particular
objects are provided. objects are provided.
Base providers class is - :py:class:`dependency_injector.providers.Provider`
Every provider is callable (implements ``__call__()``). Every call to provider Every provider is callable (implements ``__call__()``). Every call to provider
instance returns provided result, according to the providing strategy of instance returns provided result, according to the providing strategy of
particular provider. particular provider.
Current documentation section consists from description of standard providers Current documentation section consists from description of standard providers
library and some useful information like overriding of providers and writing library and some useful information like overriding of providers and writing
custom providers. of custom providers.
All providers are validated in multithreading environment and considered to All providers are validated in multithreading environment and considered to
be thread safe. be thread safe.
Providers module API docs - :py:mod:`dependency_injector.providers`
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -1,6 +1,8 @@
Overriding of providers Overriding of providers
----------------------- -----------------------
.. module:: dependency_injector.providers
Every provider could be overridden by another provider. Every provider could be overridden by another provider.
This gives opportunity to make system behaviour more flexible in some points. This gives opportunity to make system behaviour more flexible in some points.
@ -15,22 +17,22 @@ Provider overriding functionality has such interface:
:width: 45% :width: 45%
:align: center :align: center
+ ``di.Provider.override()`` - takes another provider that will be used + :py:meth:`Provider.override()` - takes another provider that will be used
instead of current provider. This method could be called several times. instead of current provider. This method could be called several times.
In such case, last passed provider would be used as overriding one. In such case, last passed provider would be used as overriding one.
+ ``di.Provider.reset_override()`` - resets all overriding providers. Provider + :py:meth:`Provider.reset_override()` - resets all overriding providers.
starts to behave itself like usual. Provider starts to behave itself like usual.
+ ``di.Provider.is_overridden`` - bool, ``True`` if provider is overridden. + :py:attr:`Provider.is_overridden` - bool, ``True`` if provider is overridden.
.. note:: .. note::
Actually, initial provider forms stack from overriding providers. There is Actually, initial provider forms stack from overriding providers. There is
some, not so common, but still usefull, functionality that could be used: some, not so common, but still usefull, functionality that could be used:
+ ``di.Provider.last_overriding`` - always keeps reference to last + :py:attr:`Provider.last_overriding` - always keeps reference to last
overriding provider. overriding provider.
+ ``di.Provider.reset_last_overriding()`` - remove last overriding provider + :py:meth:`Provider.reset_last_overriding()` - remove last overriding
from stack of overriding providers. provider from stack of overriding providers.
Example: Example:

View File

@ -1,8 +1,10 @@
Singleton providers Singleton providers
------------------- -------------------
``di.Singleton`` provider creates new instance of specified class on first call .. module:: dependency_injector.providers
and returns same instance on every next call.
:py:class:`Singleton` provider creates new instance of specified class on
first call and returns same instance on every next call.
Example: Example:
@ -16,9 +18,9 @@ Example:
Singleton providers and injections Singleton providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Singleton`` providers use ``di.Factory`` providers for first creation of :py:class:`Singleton` providers use :py:class:`Factory` providers for first
specified class instance, so, all of the rules about injections are the same, creation of specified class instance, so, all of the rules about injections
as for ``di.Factory`` providers. are the same, as for :py:class:`Factory` providers.
.. image:: /images/providers/singleton_internals.png .. image:: /images/providers/singleton_internals.png
:width: 80% :width: 80%
@ -26,29 +28,29 @@ as for ``di.Factory`` providers.
.. note:: .. note::
Due that ``di.Singleton`` provider creates specified class instance only on Due that :py:class:`Singleton` provider creates specified class instance
the first call, all injections are done once, during the first call, also. only on the first call, all injections are done once, during the first
Every next call, while instance has been already created and memorized, no call, also. Every next call, while instance has been already created
injections are done, ``di.Singleton`` provider just returns memorized and memorized, no injections are done, :py:class:`Singleton` provider just
earlier instance. returns memorized earlier instance.
This may cause some problems, for example, in case of trying to bind This may cause some problems, for example, in case of trying to bind
``di.Factory`` provider with ``di.Singleton`` provider (provided by :py:class:`Factory` provider with :py:class:`Singleton` provider (provided
dependent ``di.Factory`` instance will be injected only once, during the by dependent :py:class:`Factory` instance will be injected only once,
first call). Be aware that such behaviour was made with opened eyes and is during the first call). Be aware that such behaviour was made with opened
not a bug. eyes and is not a bug.
By the way, in such case, ``di.Delegate`` provider can be useful. It makes By the way, in such case, :py:class:`Delegate` provider can be useful. It
possible to inject providers *as is*. Please check out full example in makes possible to inject providers *as is*. Please check out full example
*Providers delegation* section. in *Providers delegation* section.
Singleton providers resetting Singleton providers resetting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Created and memorized by ``di.Singleton`` instance can be reset. Reset of Created and memorized by :py:class:`Singleton` instance can be reset. Reset of
``di.Singleton``'s memorized instance is done by clearing reference to it. :py:class:`Singleton`'s memorized instance is done by clearing reference to
Further lifecycle of memorized instance is out of ``di.Singleton`` provider's it. Further lifecycle of memorized instance is out of :py:class:`Singleton`
control. provider's control.
Example: Example:
@ -58,13 +60,13 @@ Example:
Singleton providers delegation Singleton providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Singleton`` provider could be delegated to any other provider via any :py:class:`Singleton` provider could be delegated to any other provider via
kind of injection. Delegation of ``di.Singleton`` providers is the same as any kind of injection. Delegation of :py:class:`Singleton` providers is the
``di.Factory`` providers delegation, please follow same as :py:class:`Factory` providers delegation, please follow *Factory
*Factory providers delegation* section for example. providers delegation* section for example.
``di.Singleton`` delegate could be created obviously using :py:class:`Singleton` delegate could be created obviously using
``di.Delegate(di.Singleton())`` or by calling ``di.Singleton.delegate()`` ``Delegate(Singleton(...))`` or by calling ``Singleton(...).delegate()``
method. method.
Example: Example:

View File

@ -1,16 +1,19 @@
Static providers Static providers
---------------- ----------------
.. module:: dependency_injector.providers
Static providers are family of providers that return their values "as is". Static providers are family of providers that return their values "as is".
There are four types of static providers: There are four types of static providers:
- ``di.Class`` - :py:class:`Class`
- ``di.Object`` - :py:class:`Object`
- ``di.Function`` - :py:class:`Function`
- ``di.Value`` - :py:class:`Value`
All of them have the same behaviour, but usage of anyone is predicted by All of them have the same behaviour (inherited from
readability and providing object's type. :py:class:`StaticProvider`), but usage of any is predicted by readability
and providing object's type.
Example: Example:

View File

@ -1,6 +1,7 @@
"""Config provider examples.""" """Config provider examples."""
import dependency_injector as di from dependency_injector import catalogs
from dependency_injector import providers
class ObjectA(object): class ObjectA(object):
@ -13,17 +14,17 @@ class ObjectA(object):
self.timezone = timezone self.timezone = timezone
class Catalog(di.AbstractCatalog): class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of providers.""" """Catalog of providers."""
config = di.Config() config = providers.Config()
""":type: di.Config""" """:type: providers.Config"""
object_a = di.Factory(ObjectA, object_a = providers.Factory(ObjectA,
fee=config.FEE, fee=config.FEE,
price=config.PRICE, price=config.PRICE,
timezone=config.GLOBAL.TIMEZONE) timezone=config.GLOBAL.TIMEZONE)
""":type: di.Provider -> ObjectA""" """:type: providers.Provider -> ObjectA"""
# Setting config value and making some tests. # Setting config value and making some tests.

View File

@ -1,22 +1,24 @@
"""`@di.inject()` decorator and Flask view example.""" """`inject()` decorator and Flask view example."""
import sqlite3 import sqlite3
import flask import flask
import dependency_injector as di
from dependency_injector import providers
from dependency_injector import injections
database = di.Singleton(sqlite3.connect, database = providers.Singleton(sqlite3.connect,
':memory:', ':memory:',
timeout=30, timeout=30,
detect_types=True, detect_types=True,
isolation_level='EXCLUSIVE') isolation_level='EXCLUSIVE')
app = flask.Flask(__name__) app = flask.Flask(__name__)
@app.route('/') @app.route('/')
@di.inject(database) @injections.inject(database)
@di.inject(flask.request) @injections.inject(flask.request)
def hello(request, database): def hello(request, database):
"""Example Flask view.""" """Example Flask view."""
print request print request

View File

@ -1,22 +1,24 @@
"""`@di.inject()` decorator with classes example.""" """`inject()` decorator with classes example."""
import sqlite3 import sqlite3
import flask import flask
import flask.views import flask.views
import dependency_injector as di
from dependency_injector import providers
from dependency_injector import injections
database = di.Singleton(sqlite3.Connection, database = providers.Singleton(sqlite3.Connection,
database=':memory:', database=':memory:',
timeout=30, timeout=30,
detect_types=True, detect_types=True,
isolation_level='EXCLUSIVE') isolation_level='EXCLUSIVE')
app = flask.Flask(__name__) app = flask.Flask(__name__)
@di.inject(database=database) @injections.inject(database=database)
@di.inject(some_setting=777) @injections.inject(some_setting=777)
class HelloView(flask.views.View): class HelloView(flask.views.View):
"""Example flask class-based view.""" """Example flask class-based view."""

View File

@ -1,14 +1,15 @@
"""`@di.inject()` decorator simple example.""" """`inject()` decorator simple example."""
import dependency_injector as di from dependency_injector import providers
from dependency_injector import injections
dependency_injector_factory = di.Factory(object) dependency_injector_factory = providers.Factory(object)
# Example of using `di.inject()` decorator keyword argument injections: # Example of using `di.inject()` decorator keyword argument injections:
@di.inject(new_object=dependency_injector_factory) @injections.inject(new_object=dependency_injector_factory)
@di.inject(some_setting=1334) @injections.inject(some_setting=1334)
def example_callback1(new_object, some_setting): def example_callback1(new_object, some_setting):
"""Example callback that does some asserts for input args.""" """Example callback that does some asserts for input args."""
assert isinstance(new_object, object) assert isinstance(new_object, object)
@ -16,7 +17,7 @@ def example_callback1(new_object, some_setting):
# Example of using `di.inject()` decorator with positional argument injections: # Example of using `di.inject()` decorator with positional argument injections:
@di.inject(dependency_injector_factory, 1334) @injections.inject(dependency_injector_factory, 1334)
def example_callback2(new_object, some_setting): def example_callback2(new_object, some_setting):
"""Example callback that does some asserts for input args.""" """Example callback that does some asserts for input args."""
assert isinstance(new_object, object) assert isinstance(new_object, object)

View File

@ -1,38 +1,40 @@
"""Catalog bundles example.""" """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 services
import views import views
# Declaring services catalog: # Declaring services catalog:
class Services(di.DeclarativeCatalog): class Services(catalogs.DeclarativeCatalog):
"""Example catalog of service providers.""" """Example catalog of service providers."""
users = di.Factory(services.UsersService) users = providers.Factory(services.Users)
""":type: di.Provider -> services.UsersService""" """:type: providers.Provider -> services.Users"""
auth = di.Factory(services.AuthService) auth = providers.Factory(services.Auth)
""":type: di.Provider -> services.AuthService""" """:type: providers.Provider -> services.Auth"""
photos = di.Factory(services.PhotosService) photos = providers.Factory(services.Photos)
""":type: di.Provider -> services.PhotosService""" """:type: providers.Provider -> services.Photos"""
# Declaring views catalog: # Declaring views catalog:
class Views(di.DeclarativeCatalog): class Views(catalogs.DeclarativeCatalog):
"""Example catalog of web views.""" """Example catalog of web views."""
auth = di.Factory(views.AuthView, auth = providers.Factory(views.Auth,
services=Services.Bundle(Services.users, services=Services.Bundle(Services.users,
Services.auth)) Services.auth))
""":type: di.Provider -> views.AuthView""" """:type: providers.Provider -> views.Auth"""
photos = di.Factory(views.PhotosView, photos = providers.Factory(views.Photos,
services=Services.Bundle(Services.users, services=Services.Bundle(Services.users,
Services.photos)) Services.photos))
""":type: di.Provider -> views.PhotosView""" """:type: providers.Provider -> views.Photos"""
# Creating example views: # Creating example views:
@ -47,7 +49,7 @@ assert auth_view.services.users is Services.users
assert auth_view.services.auth is Services.auth assert auth_view.services.auth is Services.auth
try: try:
auth_view.services.photos auth_view.services.photos
except di.Error: except errors.Error:
# `photos` service provider is not in scope of `auth_view` services bundle, # `photos` service provider is not in scope of `auth_view` services bundle,
# so `di.Error` will be raised. # so `di.Error` will be raised.
pass pass
@ -56,7 +58,7 @@ assert photos_view.services.users is Services.users
assert photos_view.services.photos is Services.photos assert photos_view.services.photos is Services.photos
try: try:
photos_view.services.auth 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` # `auth` service provider is not in scope of `photo_processing_view`
# services bundle, so `di.Error` will be raised. # services bundle, so `di.Error` will be raised.
pass pass

View File

@ -5,13 +5,13 @@ class BaseService(object):
"""Example base class of service.""" """Example base class of service."""
class UsersService(BaseService): class Users(BaseService):
"""Example users service.""" """Example users service."""
class AuthService(BaseService): class Auth(BaseService):
"""Example auth service.""" """Example auth service."""
class PhotosService(BaseService): class Photos(BaseService):
"""Example photo service.""" """Example photo service."""

View File

@ -7,15 +7,15 @@ class BaseWebView(object):
def __init__(self, services): def __init__(self, services):
"""Initializer. """Initializer.
:type services: catalogs.Services
:param services: Bundle of service providers :param services: Bundle of service providers
:type services: catalogs.Services
""" """
self.services = services self.services = services
class AuthView(BaseWebView): class Auth(BaseWebView):
"""Example auth web view.""" """Example auth web view."""
class PhotosView(BaseWebView): class Photos(BaseWebView):
"""Example photo processing web view.""" """Example photo processing web view."""

View File

@ -0,0 +1,24 @@
"""Declarative catalog simple example."""
from dependency_injector import catalogs
from dependency_injector import providers
# Defining declarative catalog:
class Catalog(catalogs.DeclarativeCatalog):
"""Providers catalog."""
factory1 = providers.Factory(object)
""":type: providers.Provider -> object"""
factory2 = providers.Factory(object)
""":type: providers.Provider -> 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)

View File

@ -1,20 +1,21 @@
"""Operating with catalog providers example.""" """Declarative catalogs inheritance example."""
import dependency_injector as di from dependency_injector import catalogs
from dependency_injector import providers
class CatalogA(di.DeclarativeCatalog): class CatalogA(catalogs.DeclarativeCatalog):
"""Example catalog A.""" """Example catalog A."""
provider1 = di.Factory(object) provider1 = providers.Factory(object)
""":type: di.Provider -> object""" """:type: providers.Provider -> object"""
class CatalogB(CatalogA): class CatalogB(CatalogA):
"""Example catalog B.""" """Example catalog B."""
provider2 = di.Singleton(object) provider2 = providers.Singleton(object)
""":type: di.Provider -> object""" """:type: providers.Provider -> object"""
# Making some asserts for `providers` attribute: # Making some asserts for `providers` attribute:
@ -29,7 +30,3 @@ assert CatalogB.cls_providers == dict(provider2=CatalogB.provider2)
# Making some asserts for `inherited_providers` attribute: # Making some asserts for `inherited_providers` attribute:
assert CatalogA.inherited_providers == dict() assert CatalogA.inherited_providers == dict()
assert CatalogB.inherited_providers == dict(provider1=CatalogA.provider1) assert CatalogB.inherited_providers == dict(provider1=CatalogA.provider1)
# Making some asserts for `filter()` method:
assert CatalogB.filter(di.Factory) == dict(provider1=CatalogA.provider1)
assert CatalogB.filter(di.Singleton) == dict(provider2=CatalogB.provider2)

View File

@ -0,0 +1,50 @@
"""Declarative catalog's provider injections example."""
import sqlite3
from dependency_injector import catalogs
from dependency_injector import providers
class UsersService(object):
"""Users service, that has dependency on database."""
def __init__(self, db):
"""Initializer."""
self.db = db
class AuthService(object):
"""Auth service, that has dependencies on users service and database."""
def __init__(self, db, users_service):
"""Initializer."""
self.db = db
self.users_service = users_service
class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
""":type: providers.Provider -> sqlite3.Connection"""
users = providers.Factory(UsersService,
db=database)
""":type: providers.Provider -> UsersService"""
auth = providers.Factory(AuthService,
db=database,
users_service=users)
""":type: providers.Provider -> AuthService"""
# Retrieving service providers from catalog:
users_service = Services.users()
auth_service = Services.auth()
# Making some asserts:
assert users_service.db is auth_service.db is Services.database()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users()
assert auth_service is not Services.auth()

View File

@ -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)

View File

@ -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)

View File

@ -1,44 +0,0 @@
"""Catalog overriding example."""
import collections
import dependency_injector as di
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Catalog(di.DeclarativeCatalog):
"""Providers catalog."""
object1_factory = di.Factory(Object1,
arg1=1,
arg2=2)
""":type: di.Provider -> Object1"""
object2_factory = di.Factory(Object2,
object1=object1_factory)
""":type: di.Provider -> Object2"""
class AnotherCatalog(di.DeclarativeCatalog):
"""Another providers catalog."""
object2_factory = di.Factory(ExtendedObject2)
""":type: di.Provider -> ExtendedObject2"""
# Overriding `Catalog` with `AnotherCatalog`:
Catalog.override(AnotherCatalog)
# Creating some objects using overridden catalog:
object2_1 = Catalog.object2_factory()
object2_2 = Catalog.object2_factory()
# Making some asserts:
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -0,0 +1,48 @@
"""Declarative catalog overriding example."""
import collections
from dependency_injector import catalogs
from dependency_injector import providers
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of some providers."""
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(ExtendedObject2)
""":type: providers.Provider -> ExtendedObject2"""
# Overriding `Catalog` with `AnotherCatalog`:
Catalog.override(AnotherCatalog)
# Creating some objects using overridden catalog:
object2_1 = Catalog.object2_factory()
object2_2 = Catalog.object2_factory()
# Making some asserts:
assert Catalog.is_overridden
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -0,0 +1,43 @@
"""Declarative catalog overriding by dynamic catalog example."""
import collections
from dependency_injector import catalogs
from dependency_injector import providers
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of some providers."""
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
# Overriding `Catalog` with some `DynamicCatalog` instance:
overriding_catalog = catalogs.DynamicCatalog(
object2_factory=providers.Factory(ExtendedObject2))
Catalog.override(overriding_catalog)
# Creating some objects using overridden catalog:
object2_1 = Catalog.object2_factory()
object2_2 = Catalog.object2_factory()
# Making some asserts:
assert Catalog.is_overridden
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -0,0 +1,46 @@
"""Declarative catalog overriding using `@override()` decorator example."""
import collections
from dependency_injector import catalogs
from dependency_injector import providers
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of some providers."""
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
# Overriding `Catalog` with `AnotherCatalog`:
@catalogs.override(Catalog)
class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(ExtendedObject2)
""":type: providers.Provider -> ExtendedObject2"""
# Creating some objects using overridden catalog:
object2_1 = Catalog.object2_factory()
object2_2 = Catalog.object2_factory()
# Making some asserts:
assert Catalog.is_overridden
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -1,43 +0,0 @@
"""Catalog overriding using `@di.override()` decorator example."""
import collections
import dependency_injector as di
# Creating some example classes:
Object1 = collections.namedtuple('Object1', ['arg1', 'arg2'])
Object2 = collections.namedtuple('Object2', ['object1'])
ExtendedObject2 = collections.namedtuple('ExtendedObject2', [])
class Catalog(di.DeclarativeCatalog):
"""Providers catalog."""
object1_factory = di.Factory(Object1,
arg1=1,
arg2=2)
""":type: di.Provider -> Object1"""
object2_factory = di.Factory(Object2,
object1=object1_factory)
""":type: di.Provider -> Object2"""
# Overriding `Catalog` with `AnotherCatalog`:
@di.override(Catalog)
class AnotherCatalog(di.DeclarativeCatalog):
"""Another providers catalog."""
object2_factory = di.Factory(ExtendedObject2)
""":type: di.Provider -> ExtendedObject2"""
# Creating some objects using overridden catalog:
object2_1 = Catalog.object2_factory()
object2_2 = Catalog.object2_factory()
# Making some asserts:
assert object2_1 is not object2_2
assert isinstance(object2_1, ExtendedObject2)
assert isinstance(object2_2, ExtendedObject2)

View File

@ -1,22 +0,0 @@
"""Catalog example."""
import dependency_injector as di
class Catalog(di.DeclarativeCatalog):
"""Providers catalog."""
factory1 = di.Factory(object)
""":type: di.Provider -> object"""
factory2 = di.Factory(object)
""":type: di.Provider -> 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)

View File

@ -1,7 +1,10 @@
"""Concept example of `Dependency Injector`.""" """Concept example of `Dependency Injector`."""
import sqlite3 import sqlite3
import dependency_injector as di
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class UsersService(object): class UsersService(object):
@ -21,20 +24,20 @@ class AuthService(object):
self.users_service = users_service self.users_service = users_service
class Services(di.DeclarativeCatalog): class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers.""" """Catalog of service providers."""
database = di.Singleton(sqlite3.connect, ':memory:') database = providers.Singleton(sqlite3.connect, ':memory:')
""":type: di.Provider -> sqlite3.Connection""" """:type: providers.Provider -> sqlite3.Connection"""
users = di.Factory(UsersService, users = providers.Factory(UsersService,
db=database) db=database)
""":type: di.Provider -> UsersService""" """:type: providers.Provider -> UsersService"""
auth = di.Factory(AuthService, auth = providers.Factory(AuthService,
db=database, db=database,
users_service=users) users_service=users)
""":type: di.Provider -> AuthService""" """:type: providers.Provider -> AuthService"""
# Retrieving catalog providers: # Retrieving catalog providers:
@ -49,9 +52,9 @@ assert auth_service is not Services.auth()
# Making some "inline" injections: # Making some "inline" injections:
@di.inject(users_service=Services.users) @injections.inject(users_service=Services.users)
@di.inject(auth_service=Services.auth) @injections.inject(auth_service=Services.auth)
@di.inject(database=Services.database) @injections.inject(database=Services.database)
def example(users_service, auth_service, database): def example(users_service, auth_service, database):
"""Example callback.""" """Example callback."""
assert users_service.db is auth_service.db assert users_service.db is auth_service.db

View File

@ -1,11 +1,11 @@
"""`di.Callable` providers with positional arguments example.""" """`Callable` providers with positional arguments example."""
import dependency_injector as di from dependency_injector import providers
# Creating even and odd filter providers: # Creating even and odd filter providers:
even_filter = di.Callable(filter, lambda x: x % 2 == 0) even_filter = providers.Callable(filter, lambda x: x % 2 == 0)
odd_filter = di.Callable(filter, lambda x: x % 2 != 0) odd_filter = providers.Callable(filter, lambda x: x % 2 != 0)
# Creating even and odd ranges using xrange() and filter providers: # Creating even and odd ranges using xrange() and filter providers:
even_range = even_filter(xrange(1, 10)) even_range = even_filter(xrange(1, 10))

View File

@ -1,13 +1,14 @@
"""`di.Callable` providers delegation example.""" """`Callable` providers delegation example."""
import sys import sys
import dependency_injector as di
from dependency_injector import providers
# Creating some callable provider and few delegates of it: # Creating some callable provider and few delegates of it:
callable_provider = di.Callable(sys.exit) callable_provider = providers.Callable(sys.exit)
callable_provider_delegate1 = callable_provider.delegate() callable_provider_delegate1 = callable_provider.delegate()
callable_provider_delegate2 = di.Delegate(callable_provider) callable_provider_delegate2 = providers.Delegate(callable_provider)
# Making some asserts: # Making some asserts:
assert callable_provider_delegate1() is callable_provider assert callable_provider_delegate1() is callable_provider

View File

@ -1,14 +1,16 @@
"""`di.Callable` providers with keyword arguments example.""" """`Callable` providers with keyword arguments example."""
import passlib.hash import passlib.hash
import dependency_injector as di
from dependency_injector import providers
# Password hasher and verifier providers (hash function could be changed # Password hasher and verifier providers (hash function could be changed
# anytime (for example, to sha512) without any changes in client's code): # anytime (for example, to sha512) without any changes in client's code):
password_hasher = di.Callable(passlib.hash.sha256_crypt.encrypt, password_hasher = providers.Callable(passlib.hash.sha256_crypt.encrypt,
salt_size=16, salt_size=16,
rounds=10000) rounds=10000)
password_verifier = di.Callable(passlib.hash.sha256_crypt.verify) password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
# Making some asserts: # Making some asserts:
hashed_password = password_hasher('super secret') hashed_password = password_hasher('super secret')

View File

@ -1,24 +1,33 @@
"""Custom `di.Factory` example.""" """Custom `Factory` example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
"""Example class User.""" """Example class User."""
class UsersFactory(di.Provider): class UsersFactory(providers.Provider):
"""Example users factory.""" """Example users factory."""
__slots__ = ('_factory',) __slots__ = ('_factory',)
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer."""
self._factory = di.Factory(User) self._factory = providers.Factory(User)
super(UsersFactory, self).__init__() super(UsersFactory, self).__init__()
def _provide(self, *args, **kwargs): def _provide(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance.
:param args: tuple of context positional arguments
:type args: tuple[object]
:param kwargs: dictionary of context keyword arguments
:type kwargs: dict[str, object]
:rtype: object
"""
return self._factory(*args, **kwargs) return self._factory(*args, **kwargs)

View File

@ -1,8 +1,9 @@
"""`di.ExternalDependency` providers example.""" """`ExternalDependency` providers example."""
import sqlite3 import sqlite3
import contextlib import contextlib
import dependency_injector as di
from dependency_injector import providers
class UserService(object): class UserService(object):
@ -14,7 +15,8 @@ class UserService(object):
def __init__(self, database): def __init__(self, database):
"""Initializer. """Initializer.
Database dependency need to be injected via init arg. :param database: Database connection.
:type database: sqlite3.dbapi2.Connection
""" """
self.database = database self.database = database
self.database.row_factory = sqlite3.dbapi2.Row self.database.row_factory = sqlite3.dbapi2.Row
@ -43,18 +45,18 @@ class UserService(object):
# Database and UserService providers: # Database and UserService providers:
database = di.ExternalDependency(instance_of=sqlite3.dbapi2.Connection) database = providers.ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = di.Factory(UserService, users_service_factory = providers.Factory(UserService,
database=database) database=database)
# Out of library's scope. # Out of library's scope.
# #
# Setting database provider: # Setting database provider:
database.provided_by(di.Singleton(sqlite3.dbapi2.Connection, database.provided_by(providers.Singleton(sqlite3.dbapi2.Connection,
database=':memory:', database=':memory:',
timeout=30, timeout=30,
detect_types=True, detect_types=True,
isolation_level='EXCLUSIVE')) isolation_level='EXCLUSIVE'))
# Creating UserService instance: # Creating UserService instance:
users_service = users_service_factory() users_service = users_service_factory()

View File

@ -1,13 +1,13 @@
"""`di.Factory` providers example.""" """`Factory` providers example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
"""Example class User.""" """Example class User."""
# Factory provider creates new instance of specified class on every call. # Factory provider creates new instance of specified class on every call.
users_factory = di.Factory(User) users_factory = providers.Factory(User)
# Creating several User objects: # Creating several User objects:
user1 = users_factory() user1 = users_factory()

View File

@ -1,6 +1,7 @@
"""`di.Factory` providers with attribute injections example.""" """`Factory` providers with attribute injections example."""
import dependency_injector as di from dependency_injector import providers
from dependency_injector import injections
class User(object): class User(object):
@ -20,11 +21,13 @@ class CreditCard(object):
"""Example class CreditCard.""" """Example class CreditCard."""
# User, Photo and CreditCard factories: # User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard) credit_cards_factory = providers.Factory(CreditCard)
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, users_factory = providers.Factory(User,
di.Attribute('main_photo', photos_factory), injections.Attribute('main_photo',
di.Attribute('credit_card', credit_cards_factory)) photos_factory),
injections.Attribute('credit_card',
credit_cards_factory))
# Creating several User objects: # Creating several User objects:
user1 = users_factory() user1 = users_factory()

View File

@ -1,6 +1,6 @@
"""`di.Factory` providers delegation example.""" """`Factory` providers delegation example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
@ -9,7 +9,7 @@ class User(object):
def __init__(self, photos_factory): def __init__(self, photos_factory):
"""Initializer. """Initializer.
:param photos_factory: (di.Factory) -> Photo :param photos_factory: providers.Factory -> Photo
""" """
self.photos_factory = photos_factory self.photos_factory = photos_factory
self._main_photo = None self._main_photo = None
@ -27,9 +27,9 @@ class Photo(object):
"""Example class Photo.""" """Example class Photo."""
# User and Photo factories: # User and Photo factories:
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, users_factory = providers.Factory(User,
photos_factory=di.Delegate(photos_factory)) photos_factory=photos_factory.delegate())
# Creating several User objects: # Creating several User objects:
user1 = users_factory() user1 = users_factory()

View File

@ -1,6 +1,6 @@
"""`di.Factory` providers with init positional injections example.""" """`Factory` providers with init positional injections example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
@ -16,8 +16,8 @@ class Photo(object):
"""Example class Photo.""" """Example class Photo."""
# User and Photo factories: # User and Photo factories:
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, photos_factory) users_factory = providers.Factory(User, photos_factory)
# Creating several User objects: # Creating several User objects:
user1 = users_factory() # Same as: user1 = User(Photo()) user1 = users_factory() # Same as: user1 = User(Photo())

View File

@ -1,6 +1,6 @@
"""`di.Factory` providers with init injections priority example.""" """`Factory` providers with init injections priority example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
@ -30,11 +30,11 @@ class CreditCard(object):
"""Example class CreditCard.""" """Example class CreditCard."""
# User, Photo and CreditCard factories: # User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard) credit_cards_factory = providers.Factory(CreditCard)
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, users_factory = providers.Factory(User,
main_photo=photos_factory, main_photo=photos_factory,
credit_card=credit_cards_factory) credit_card=credit_cards_factory)
# Creating several User objects: # Creating several User objects:
user1 = users_factory(1) user1 = users_factory(1)

View File

@ -1,6 +1,6 @@
"""`di.Factory` providers with init keyword injections example.""" """`Factory` providers with init keyword injections example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
@ -16,8 +16,8 @@ class Photo(object):
"""Example class Photo.""" """Example class Photo."""
# User and Photo factories: # User and Photo factories:
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, main_photo=photos_factory) users_factory = providers.Factory(User, main_photo=photos_factory)
# Creating several User objects: # Creating several User objects:
user1 = users_factory() # Same as: user1 = User(main_photo=Photo()) user1 = users_factory() # Same as: user1 = User(main_photo=Photo())

View File

@ -1,6 +1,7 @@
"""`di.Factory` providers with method injections example.""" """`Factory` providers with method injections example."""
import dependency_injector as di from dependency_injector import providers
from dependency_injector import injections
class User(object): class User(object):
@ -28,11 +29,13 @@ class CreditCard(object):
"""Example class CreditCard.""" """Example class CreditCard."""
# User, Photo and CreditCard factories: # User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard) credit_cards_factory = providers.Factory(CreditCard)
photos_factory = di.Factory(Photo) photos_factory = providers.Factory(Photo)
users_factory = di.Factory(User, users_factory = providers.Factory(User,
di.Method('set_main_photo', photos_factory), injections.Method('set_main_photo',
di.Method('set_credit_card', credit_cards_factory)) photos_factory),
injections.Method('set_credit_card',
credit_cards_factory))
# Creating several User objects: # Creating several User objects:
user1 = users_factory() user1 = users_factory()

View File

@ -1,13 +1,13 @@
"""Simple providers overriding example.""" """Simple providers overriding example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
"""Example class User.""" """Example class User."""
# Users factory: # Users factory:
users_factory = di.Factory(User) users_factory = providers.Factory(User)
# Creating several User objects: # Creating several User objects:
user1 = users_factory() user1 = users_factory()
@ -23,7 +23,7 @@ class SuperUser(User):
"""Example class SuperUser.""" """Example class SuperUser."""
# Overriding users factory: # Overriding users factory:
users_factory.override(di.Factory(SuperUser)) users_factory.override(providers.Factory(SuperUser))
# Creating some more User objects using overridden users factory: # Creating some more User objects using overridden users factory:
user3 = users_factory() user3 = users_factory()

View File

@ -1,6 +1,6 @@
"""Overriding user's model example.""" """Overriding user's model example."""
import dependency_injector as di from dependency_injector import providers
class User(object): class User(object):
@ -26,8 +26,7 @@ class UserService(object):
return self.user_cls(id=id, password='secret' + str(id)) return self.user_cls(id=id, password='secret' + str(id))
# Users factory and UserService provider: # Users factory and UserService provider:
users_service = di.Factory(UserService, users_service = providers.Factory(UserService, user_cls=User)
user_cls=User)
# Getting several users and making some asserts: # Getting several users and making some asserts:
user1 = users_service().get_by_id(1) user1 = users_service().get_by_id(1)
@ -71,8 +70,8 @@ class ExtendedUserService(UserService):
return user return user
# Overriding users_service provider: # Overriding users_service provider:
extended_users_service = di.Factory(ExtendedUserService, extended_users_service = providers.Factory(ExtendedUserService,
user_cls=ExtendedUser) user_cls=ExtendedUser)
users_service.override(extended_users_service) users_service.override(extended_users_service)
# Getting few other users users and making some asserts: # Getting few other users users and making some asserts:

View File

@ -1,6 +1,6 @@
"""`di.Singleton` providers example.""" """`Singleton` providers example."""
import dependency_injector as di from dependency_injector import providers
class UserService(object): class UserService(object):
@ -8,7 +8,7 @@ class UserService(object):
# Singleton provider creates new instance of specified class on first call and # Singleton provider creates new instance of specified class on first call and
# returns same instance on every next call. # returns same instance on every next call.
users_service_provider = di.Singleton(UserService) users_service_provider = providers.Singleton(UserService)
# Retrieving several UserService objects: # Retrieving several UserService objects:
user_service1 = users_service_provider() user_service1 = users_service_provider()

View File

@ -1,12 +1,12 @@
"""`di.Singleton` providers delegation example.""" """`Singleton` providers delegation example."""
import dependency_injector as di from dependency_injector import providers
# Some singleton provider and few delegates of it: # Some singleton provider and few delegates of it:
singleton_provider = di.Singleton(object) singleton_provider = providers.Singleton(object)
singleton_provider_delegate1 = singleton_provider.delegate() singleton_provider_delegate1 = singleton_provider.delegate()
singleton_provider_delegate2 = di.Delegate(singleton_provider) singleton_provider_delegate2 = providers.Delegate(singleton_provider)
# Making some asserts: # Making some asserts:
assert singleton_provider_delegate1() is singleton_provider assert singleton_provider_delegate1() is singleton_provider

View File

@ -1,13 +1,13 @@
"""`di.Singleton` providers resetting example.""" """`Singleton` providers resetting example."""
import dependency_injector as di from dependency_injector import providers
class UserService(object): class UserService(object):
"""Example class UserService.""" """Example class UserService."""
# Users service singleton provider: # Users service singleton provider:
users_service_provider = di.Singleton(UserService) users_service_provider = providers.Singleton(UserService)
# Retrieving several UserService objects: # Retrieving several UserService objects:
user_service1 = users_service_provider() user_service1 = users_service_provider()

View File

@ -1,20 +1,20 @@
"""Static providers example.""" """Static providers example."""
import dependency_injector as di from dependency_injector import providers
# Provides class - `object`: # Provides class - `object`:
cls_provider = di.Class(object) cls_provider = providers.Class(object)
assert cls_provider() is object assert cls_provider() is object
# Provides object - `object()`: # Provides object - `object()`:
object_provider = di.Object(object()) object_provider = providers.Object(object())
assert isinstance(object_provider(), object) assert isinstance(object_provider(), object)
# Provides function - `len`: # Provides function - `len`:
function_provider = di.Function(len) function_provider = providers.Function(len)
assert function_provider() is len assert function_provider() is len
# Provides value - `123`: # Provides value - `123`:
value_provider = di.Value(123) value_provider = providers.Value(123)
assert value_provider() == 123 assert value_provider() == 123

View File

@ -1 +1,13 @@
"""Dependency injector unittests.""" """Dependency injector unittests."""
import unittest2 as unittest
from dependency_injector import VERSION
class VersionTest(unittest.TestCase):
"""Version constant tests."""
def test_version_follows_semantic_versioning(self):
"""Test that version follows semantic versioning."""
self.assertEquals(len(VERSION.split('.')))

View File

@ -282,11 +282,9 @@ class DeclarativeCatalogTests(unittest.TestCase):
self.assertIs(CatalogA.px, px) self.assertIs(CatalogA.px, px)
self.assertIs(CatalogA.get_provider('px'), px) self.assertIs(CatalogA.get_provider('px'), px)
self.assertIs(CatalogA.catalog.px, px)
self.assertIs(CatalogA.py, py) self.assertIs(CatalogA.py, py)
self.assertIs(CatalogA.get_provider('py'), py) self.assertIs(CatalogA.get_provider('py'), py)
self.assertIs(CatalogA.catalog.py, py)
del CatalogA.px del CatalogA.px
del CatalogA.py del CatalogA.py
@ -300,11 +298,9 @@ class DeclarativeCatalogTests(unittest.TestCase):
self.assertIs(CatalogB.px, px) self.assertIs(CatalogB.px, px)
self.assertIs(CatalogB.get_provider('px'), px) self.assertIs(CatalogB.get_provider('px'), px)
self.assertIs(CatalogB.catalog.px, px)
self.assertIs(CatalogB.py, py) self.assertIs(CatalogB.py, py)
self.assertIs(CatalogB.get_provider('py'), py) self.assertIs(CatalogB.get_provider('py'), py)
self.assertIs(CatalogB.catalog.py, py)
del CatalogB.px del CatalogB.px
del CatalogB.py del CatalogB.py
@ -319,11 +315,9 @@ class DeclarativeCatalogTests(unittest.TestCase):
self.assertIs(CatalogB.px, px) self.assertIs(CatalogB.px, px)
self.assertIs(CatalogB.get_provider('px'), px) self.assertIs(CatalogB.get_provider('px'), px)
self.assertIs(CatalogB.catalog.px, px)
self.assertIs(CatalogB.py, py) self.assertIs(CatalogB.py, py)
self.assertIs(CatalogB.get_provider('py'), py) self.assertIs(CatalogB.get_provider('py'), py)
self.assertIs(CatalogB.catalog.py, py)
del CatalogB.px del CatalogB.px
del CatalogB.py del CatalogB.py
@ -480,6 +474,23 @@ class OverrideTests(unittest.TestCase):
self.assertEqual(CatalogA.p12(), 2) self.assertEqual(CatalogA.p12(), 2)
self.assertEqual(len(CatalogA.overridden_by), 1) self.assertEqual(len(CatalogA.overridden_by), 1)
def test_override_declarative_catalog_with_itself(self):
"""Test catalog overriding of declarative catalog with itself."""
with self.assertRaises(errors.Error):
CatalogA.override(CatalogA)
def test_override_declarative_catalog_with_subclass(self):
"""Test catalog overriding of declarative catalog with subclass."""
with self.assertRaises(errors.Error):
CatalogB.override(CatalogA)
def test_override_dynamic_catalog_with_itself(self):
"""Test catalog overriding of dynamic catalog with itself."""
catalog = catalogs.DynamicCatalog(p11=providers.Value(1),
p12=providers.Value(2))
with self.assertRaises(errors.Error):
catalog.override(catalog)
def test_overriding_with_dynamic_catalog(self): def test_overriding_with_dynamic_catalog(self):
"""Test catalog overriding with another dynamic catalog.""" """Test catalog overriding with another dynamic catalog."""
CatalogA.override(catalogs.DynamicCatalog(p11=providers.Value(1), CatalogA.override(catalogs.DynamicCatalog(p11=providers.Value(1),
@ -512,8 +523,7 @@ class OverrideTests(unittest.TestCase):
def test_last_overriding_on_not_overridden(self): def test_last_overriding_on_not_overridden(self):
"""Test catalog last_overriding property on not overridden catalog.""" """Test catalog last_overriding property on not overridden catalog."""
with self.assertRaises(errors.Error): self.assertIsNone(CatalogA.last_overriding)
CatalogA.last_overriding
def test_reset_last_overriding(self): def test_reset_last_overriding(self):
"""Test resetting last overriding catalog.""" """Test resetting last overriding catalog."""
@ -561,3 +571,14 @@ class OverrideTests(unittest.TestCase):
self.assertFalse(CatalogA.p11.is_overridden) self.assertFalse(CatalogA.p11.is_overridden)
self.assertFalse(CatalogA.p12.is_overridden) self.assertFalse(CatalogA.p12.is_overridden)
class CatalogModuleBackwardCompatibility(unittest.TestCase):
"""Backward compatibility test of catalog module."""
def test_import_catalog(self):
"""Test that module `catalog` is the same as `catalogs`."""
from dependency_injector import catalog
from dependency_injector import catalogs
self.assertIs(catalog, catalogs)

View File

@ -90,13 +90,7 @@ class ProviderTests(unittest.TestCase):
def test_last_overriding_of_not_overridden_provider(self): def test_last_overriding_of_not_overridden_provider(self):
"""Test getting last overriding from not overridden provider.""" """Test getting last overriding from not overridden provider."""
try: self.assertIsNone(self.provider.last_overriding)
self.provider.last_overriding
except errors.Error:
pass
else:
self.fail('Got en error in {}'.format(
str(self.test_last_overriding_of_not_overridden_provider)))
def test_reset_last_overriding(self): def test_reset_last_overriding(self):
"""Test reseting of last overriding provider.""" """Test reseting of last overriding provider."""
@ -129,13 +123,7 @@ class ProviderTests(unittest.TestCase):
self.provider.reset_override() self.provider.reset_override()
self.assertFalse(self.provider.is_overridden) self.assertFalse(self.provider.is_overridden)
try: self.assertIsNone(self.provider.last_overriding)
self.provider.last_overriding
except errors.Error:
pass
else:
self.fail('Got en error in {}'.format(
str(self.test_last_overriding_of_not_overridden_provider)))
class DelegateTests(unittest.TestCase): class DelegateTests(unittest.TestCase):
@ -564,6 +552,35 @@ class SingletonTests(unittest.TestCase):
self.assertIsInstance(instance1, object) self.assertIsInstance(instance1, object)
self.assertIsInstance(instance2, object) self.assertIsInstance(instance2, object)
def test_provides_attr(self):
"""Test provides attribute."""
provider = providers.Singleton(Example)
self.assertIs(provider.provides, Example)
def test_args_attr(self):
"""Test args attribute."""
provider = providers.Singleton(Example, 1, 2)
self.assertEquals(len(provider.args), 2)
def test_kwargs_attr(self):
"""Test kwargs attribute."""
provider = providers.Singleton(Example, init_arg1=1, init_arg2=2)
self.assertEquals(len(provider.kwargs), 2)
def test_attributes_attr(self):
"""Test attributes attribute."""
provider = providers.Singleton(Example,
injections.Attribute('attribute1', 1),
injections.Attribute('attribute2', 2))
self.assertEquals(len(provider.attributes), 2)
def test_methods_attr(self):
"""Test methods attribute."""
provider = providers.Singleton(Example,
injections.Method('method1', 1),
injections.Method('method2', 2))
self.assertEquals(len(provider.methods), 2)
def test_injections(self): def test_injections(self):
"""Test getting a full list of injections using injections property.""" """Test getting a full list of injections using injections property."""
provider = providers.Singleton(Example, provider = providers.Singleton(Example,