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

View File

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

View File

@ -7,40 +7,79 @@ from .errors import UndefinedProviderError
from .utils import is_provider
from .utils import is_catalog
from .utils import is_declarative_catalog
from .utils import ensure_is_provider
from .utils import ensure_is_catalog_bundle
@six.python_2_unicode_compatible
class CatalogBundle(object):
"""Bundle of catalog providers."""
"""Bundle of catalog providers.
:py:class:`CatalogBundle` is a frozen, limited collection of catalog
providers. While catalog could be used as a centralized place for
particular providers group, such bundles of catalog providers can be used
for creating several frozen, limited scopes that could be passed to
different subsystems.
:py:class:`CatalogBundle` has API's parity with catalogs
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of
retrieving the providers, but it is "frozen" in terms of modification
provider's list.
:py:class:`CatalogBundle` is considered to be dependable on catalogs
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by
its design.
"""
catalog = None
""":type: DeclarativeCatalog"""
"""Bundle's catalog.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`
"""
__IS_CATALOG_BUNDLE__ = True
__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):
"""Initializer."""
"""Initializer.
:param providers: Tuple of catalog's bundle providers.
:type providers: tuple[
:py:class:`dependency_injector.providers.Provider`]
"""
self.providers = dict()
"""Dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
for provider in providers:
provider_name = self.catalog.get_provider_bind_name(provider)
self.providers[provider_name] = provider
self.__dict__.update(self.providers)
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):
"""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:
return self.providers[name]
except KeyError:
@ -48,18 +87,33 @@ class CatalogBundle(object):
self))
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
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('__'):
return super(CatalogBundle, self).__getattr__(item)
raise UndefinedProviderError('Provider "{0}" is not a part '
'of {1}'.format(item, self))
def __repr__(self):
"""Return string representation of catalog bundle."""
"""Return string representation of catalog's bundle.
:rtype: str
"""
return '<{0}.Bundle({1})>'.format(
self.catalog.name, ', '.join(six.iterkeys(self.providers)))
@ -68,7 +122,22 @@ class CatalogBundle(object):
@six.python_2_unicode_compatible
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
__slots__ = ('name', 'providers', 'provider_names', 'overridden_by',
@ -77,64 +146,132 @@ class DynamicCatalog(object):
def __init__(self, **providers):
"""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.__class__.__name__))
self.providers = dict()
self.provider_names = dict()
self.overridden_by = tuple()
"""Catalog's name.
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)
super(DynamicCatalog, self).__init__()
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
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):
raise Error('Can not find bind name for {0} in catalog {1}'.format(
provider, self))
return self.provider_names[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
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)
for name, provider in six.iteritems(self.providers)
if isinstance(provider, provider_type))
@property
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)
@property
def last_overriding(self):
"""Return last overriding catalog."""
try:
return self.overridden_by[-1]
except (TypeError, IndexError):
raise Error('Catalog {0} is not overridden'.format(self))
"""Read-only reference to the last overriding catalog, if any.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
None
"""
return self.overridden_by[-1] if self.overridden_by else None
def override(self, overriding):
"""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,)
for name, provider in six.iteritems(overriding.providers):
self.get_provider(name).override(provider)
def reset_last_overriding(self):
"""Reset last overriding catalog."""
"""Reset last overriding catalog.
:rtype: None
"""
if not self.is_overridden:
raise Error('Catalog {0} is not overridden'.format(self))
self.overridden_by = self.overridden_by[:-1]
@ -142,13 +279,25 @@ class DynamicCatalog(object):
provider.reset_last_overriding()
def reset_override(self):
"""Reset all overridings for all catalog providers."""
"""Reset all overridings for all catalog providers.
:rtype: None
"""
self.overridden_by = tuple()
for provider in six.itervalues(self.providers):
provider.reset_override()
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:
return self.providers[name]
except KeyError:
@ -156,7 +305,18 @@ class DynamicCatalog(object):
'name - {1}'.format(self, name))
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)
if name in self.providers:
@ -170,29 +330,66 @@ class DynamicCatalog(object):
self.provider_names[provider] = name
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):
self.bind_provider(name, provider)
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
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)
del self.providers[name]
del self.provider_names[provider]
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)
def __setattr__(self, name, value):
"""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 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):
return self.bind_provider(name, value)
@ -202,12 +399,20 @@ class DynamicCatalog(object):
"""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 correctly.
provider, this provider will be unbound from catalog.
:param name: Attribute's name.
:type name: str
:rtype: None
"""
self.unbind_provider(name)
def __repr__(self):
"""Return Python representation of catalog."""
"""Return Python representation of catalog.
:rtype: str
"""
return '<{0}({1})>'.format(self.name,
', '.join(six.iterkeys(self.providers)))
@ -233,44 +438,70 @@ class DeclarativeCatalogMetaClass(type):
cls = type.__new__(mcs, class_name, bases, attributes)
cls.catalog = DynamicCatalog()
cls.catalog.name = '.'.join((cls.__module__, cls.__name__))
cls.catalog.bind_providers(dict(providers))
cls._catalog = DynamicCatalog()
cls._catalog.name = '.'.join((cls.__module__, cls.__name__))
cls._catalog.bind_providers(dict(providers))
cls.cls_providers = dict(cls_providers)
cls.inherited_providers = dict(inherited_providers)
cls.Bundle = cls.catalog.Bundle
cls.Bundle = cls._catalog.Bundle
return cls
@property
def name(cls):
"""Return catalog's name."""
return cls.catalog.name
"""Read-only property that represents catalog's name.
Catalog's name is catalog's module + catalog's class name.
:type: str
"""
return cls._catalog.name
@property
def providers(cls):
"""Return dict of catalog's providers."""
return cls.catalog.providers
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._catalog.providers
@property
def overridden_by(cls):
"""Return tuple of overriding catalogs."""
return cls.catalog.overridden_by
"""Tuple of overriding catalogs.
:type: tuple[
:py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`]
"""
return cls._catalog.overridden_by
@property
def is_overridden(cls):
"""Check if catalog is overridden by another catalog."""
return cls.catalog.is_overridden
"""Read-only property that is set to ``True`` if catalog is overridden.
:rtype: bool
"""
return cls._catalog.is_overridden
@property
def last_overriding(cls):
"""Return last overriding catalog."""
return cls.catalog.last_overriding
"""Read-only reference to the last overriding catalog, if any.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
None
"""
return cls._catalog.last_overriding
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 '
'catalog {1}'.format(name, cls))
@ -278,24 +509,41 @@ class DeclarativeCatalogMetaClass(type):
"""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 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):
setattr(cls.catalog, name, value)
setattr(cls._catalog, name, value)
return super(DeclarativeCatalogMetaClass, cls).__setattr__(name, value)
def __delattr__(cls, name):
"""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 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)):
delattr(cls.catalog, name)
delattr(cls._catalog, name)
return super(DeclarativeCatalogMetaClass, cls).__delattr__(name)
def __repr__(cls):
"""Return string representation of the catalog."""
"""Return string representation of the catalog.
:rtype: str
"""
return '<{0}({1})>'.format(cls.name,
', '.join(six.iterkeys(cls.providers)))
@ -304,121 +552,274 @@ class DeclarativeCatalogMetaClass(type):
@six.add_metaclass(DeclarativeCatalogMetaClass)
class DeclarativeCatalog(object):
"""Declarative catalog catalog of providers.
"""Declarative catalog of providers.
:type name: str
:param name: Catalog's name
: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).
:type catalog: DynamicCatalog
:param catalog: Instance of dynamic catalog
.. code-block:: python
:type Bundle: CatalogBundle
:param Bundle: Catalog's bundle class
class Services(DeclarativeCatalog):
:type providers: dict[str, dependency_injector.Provider]
:param providers: Dict of all catalog providers, including inherited from
parent catalogs
auth = providers.Factory(AuthService)
:type cls_providers: dict[str, dependency_injector.Provider]
:param cls_providers: Dict of current catalog providers
users = providers.Factory(UsersService)
:type inherited_providers: dict[str, dependency_injector.Provider]
: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
users_service = Services.users()
"""
Bundle = CatalogBundle
"""Catalog's bundle class.
:type: :py:class:`CatalogBundle`
"""
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()
"""Read-only dictionary of current catalog providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
inherited_providers = dict()
"""Read-only dictionary of inherited providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
providers = dict()
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
overridden_by = tuple()
is_overridden = bool
last_overriding = None
"""Tuple of overriding catalogs.
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
@classmethod
def is_bundle_owner(cls, bundle):
"""Check if catalog is bundle owner."""
return cls.catalog.is_bundle_owner(bundle)
"""Check if catalog is bundle owner.
:param bundle: Catalog's bundle instance.
:type bundle: :py:class:`CatalogBundle`
:rtype: bool
"""
return cls._catalog.is_bundle_owner(bundle)
@classmethod
def get_provider_bind_name(cls, provider):
"""Return provider's name in catalog."""
return cls.catalog.get_provider_bind_name(provider)
"""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
"""
return cls._catalog.get_provider_bind_name(provider)
@classmethod
def is_provider_bound(cls, provider):
"""Check if provider is bound to the catalog."""
return cls.catalog.is_provider_bound(provider)
"""Check if provider is bound to the catalog.
:param provider: Provider instance.
:type provider: :py:class:`dependency_injector.providers.Provider`
:rtype: bool
"""
return cls._catalog.is_provider_bound(provider)
@classmethod
def filter(cls, provider_type):
"""Return dict of providers, that are instance of provided type."""
return cls.catalog.filter(provider_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 cls._catalog.filter(provider_type)
@classmethod
def override(cls, overriding):
"""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
def reset_last_overriding(cls):
"""Reset last overriding catalog."""
cls.catalog.reset_last_overriding()
"""Reset last overriding catalog.
:rtype: None
"""
cls._catalog.reset_last_overriding()
@classmethod
def reset_override(cls):
"""Reset all overridings for all catalog providers."""
cls.catalog.reset_override()
"""Reset all overridings for all catalog providers.
:rtype: None
"""
cls._catalog.reset_override()
@classmethod
def get_provider(cls, name):
"""Return provider with specified name or raise an error."""
return cls.catalog.get_provider(name)
"""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`
"""
return cls._catalog.get_provider(name)
get = get_provider # Backward compatibility for versions < 0.11.*
@classmethod
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)
@classmethod
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):
setattr(cls, name, provider)
@classmethod
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)
has = has_provider # Backward compatibility for versions < 0.11.*
@classmethod
def unbind_provider(cls, name):
"""Remove provider binding."""
"""Remove provider binding.
:param name: Provider's name.
:type name: str
:rtype: None
"""
delattr(cls, name)
get = get_provider # Backward compatibility for versions < 0.11.*
has = has_provider # Backward compatibility for versions < 0.11.*
@classmethod
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.*
@ -426,7 +827,14 @@ AbstractCatalog = DeclarativeCatalog
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):
"""Overriding decorator."""
catalog.override(overriding_catalog)

View File

@ -2,8 +2,21 @@
class Error(Exception):
"""Base error."""
"""Base error.
All dependency injector errors extend this error class.
"""
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
IS_PYPY = '__pypy__' in sys.builtin_module_names
if IS_PYPY or six.PY3: # pragma: no cover
OBJECT_INIT = six.get_unbound_function(object.__init__)
_IS_PYPY = '__pypy__' in sys.builtin_module_names
if _IS_PYPY or six.PY3: # pragma: no cover
_OBJECT_INIT = six.get_unbound_function(object.__init__)
else: # pragma: no cover
OBJECT_INIT = None
_OBJECT_INIT = None
class Injection(object):
"""Base injection class."""
"""Base injection class.
All injections extend this class.
"""
__IS_INJECTION__ = True
__slots__ = ('injectable', 'is_provider')
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
"""Injectable value, could be provider or any other object.
:type: object | :py:class:`dependency_injector.providers.Provider`
"""
self.is_provider = is_provider(injectable)
"""Flag that is set to ``True`` if injectable value is provider.
:type: bool
"""
super(Injection, self).__init__()
@property
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:
return self.injectable()
return self.injectable
class NamedInjection(Injection):
class _NamedInjection(Injection):
"""Base class of named injections."""
__slots__ = ('name',)
@ -47,7 +74,7 @@ class NamedInjection(Injection):
def __init__(self, name, injectable):
"""Initializer."""
self.name = name
super(NamedInjection, self).__init__(injectable)
super(_NamedInjection, self).__init__(injectable)
class Arg(Injection):
@ -56,19 +83,19 @@ class Arg(Injection):
__IS_ARG_INJECTION__ = True
class KwArg(NamedInjection):
class KwArg(_NamedInjection):
"""Keyword argument injection."""
__IS_KWARG_INJECTION__ = True
class Attribute(NamedInjection):
class Attribute(_NamedInjection):
"""Attribute injection."""
__IS_ATTRIBUTE_INJECTION__ = True
class Method(NamedInjection):
class Method(_NamedInjection):
"""Method injection."""
__IS_METHOD_INJECTION__ = True
@ -77,7 +104,48 @@ class Method(NamedInjection):
def inject(*args, **kwargs):
"""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)
kwarg_injections = _parse_kwargs_injections(args, kwargs)
@ -88,7 +156,7 @@ def inject(*args, **kwargs):
cls = callback_or_cls
try:
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):
raise Error(
'Class {0}.{1} has no __init__() '.format(cls.__module__,

View File

@ -16,7 +16,45 @@ from .errors import Error
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
__slots__ = ('overridden_by',)
@ -24,9 +62,23 @@ class Provider(object):
def __init__(self):
"""Initializer."""
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):
"""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:
return self.last_overriding(*args, **kwargs)
return self._provide(*args, **kwargs)
@ -40,12 +92,30 @@ class Provider(object):
"""
raise NotImplementedError()
def delegate(self):
"""Return provider's delegate."""
return Delegate(self)
@property
def is_overridden(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):
"""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:
raise Error('Provider {0} could not be overridden '
'with itself'.format(self))
@ -54,74 +124,174 @@ class Provider(object):
else:
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):
"""Reset last overriding provider."""
if not self.is_overridden:
"""Reset last overriding provider.
: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)))
self.overridden_by = self.overridden_by[:-1]
def reset_override(self):
"""Reset all overriding providers."""
"""Reset all overriding providers.
:rtype: None
"""
self.overridden_by = None
def delegate(self):
"""Return provider's delegate.
:rtype: :py:class:`Delegate`
"""
return Delegate(self)
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',)
def __init__(self, delegated):
"""Initializer.
:type delegated: Provider
:provider delegated: Delegated provider.
:type delegated: :py:class:`Provider`
"""
self.delegated = ensure_is_provider(delegated)
super(Delegate, self).__init__()
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
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')
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):
raise Error('Factory provider expects to get callable, ' +
'got {0} instead'.format(str(provides)))
self.provides = provides
"""Class or other callable that provides object for creation.
:type: type | callable
"""
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`]
"""
self.attributes = tuple(injection
for injection in args
if is_attribute_injection(injection))
"""Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
"""
self.methods = tuple(injection
for injection in args
if is_method_injection(injection))
"""Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
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):
"""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),
**_get_injectable_kwargs(kwargs, self.kwargs))
for attribute in self.attributes:
@ -131,48 +301,145 @@ class Factory(Provider):
return instance
@property
def injections(self):
"""Return tuple of all injections."""
return self.args + self.kwargs + self.attributes + self.methods
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')
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
"""Read-only reference to singleton's instance.
:type: object
"""
self.factory = Factory(provides, *args, **kwargs)
"""Singleton's factory object.
:type: :py:class:`Factory`
"""
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):
"""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:
if not self.instance:
self.instance = self.factory(*args, **kwargs)
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):
"""External dependency provider.
""":py:class:`ExternalDependency` provider describes dependency interface.
Those provider is used when dependency obviously have to be overridden by
the client's code, but it's interface is known.
This provider is used for description of dependency interface. That might
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',)
@ -183,10 +450,25 @@ class ExternalDependency(Provider):
raise Error('ExternalDependency provider expects to get class, ' +
'got {0} instead'.format(str(instance_of)))
self.instance_of = instance_of
"""Class of required dependency.
:type: type
"""
super(ExternalDependency, self).__init__()
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:
raise Error('Dependency is not defined')
@ -199,97 +481,207 @@ class ExternalDependency(Provider):
return instance
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)
class StaticProvider(Provider):
"""Static provider.
""":py:class:`StaticProvider` returns provided instance "as is".
Static provider is base implementation that provides exactly the same as
it got on input.
:py:class:`StaticProvider` is base implementation that provides exactly
the same as it got on input.
"""
__slots__ = ('provides',)
def __init__(self, provides):
"""Initializer."""
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
self.provides = provides
"""Value that have to be provided.
:type: object
"""
super(StaticProvider, self).__init__()
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
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):
"""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):
"""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):
"""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):
"""Callable provider.
""":py:class:`Callable` provider calls wrapped callable on every call.
Callable provider provides callable that is called on every provider call
with some predefined dependency injections.
:py:class:`Callable` provider provides callable that is called on every
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')
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):
raise Error('Callable expected, got {0}'.format(str(callback)))
self.callback = callback
self.args = _parse_args_injections(args)
self.kwargs = _parse_kwargs_injections(args, kwargs)
super(Callable, self).__init__()
"""Wrapped callable.
def _provide(self, *args, **kwargs):
"""Return provided instance."""
return self.callback(*_get_injectable_args(args, self.args),
**_get_injectable_kwargs(kwargs, self.kwargs))
:type: callable
"""
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
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
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):
"""Config provider.
""":py:class:`Config` provider provide dict values.
Config provider provides dict values. Also config provider creates
child config objects for all undefined attribute calls. It makes possible
to create deferred config value provider.
:py:class:`Config` provider creates :py:class:`ChildConfig` objects for all
undefined attribute calls. It makes possible to create deferred config
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',)
def __init__(self, value=None):
"""Initializer."""
"""Initializer.
:param value: Configuration dictionary.
:type value: dict[str, object]
"""
if not value:
value = dict()
self.value = value
super(Config, self).__init__()
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)
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
if paths:
for path in paths:
@ -301,30 +693,69 @@ class Config(Provider):
return 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)
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
the current path in the config tree.
:py:class:`ChildConfig` provides value from the root config object
according to the current path in the config tree.
"""
__slots__ = ('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
"""Tuple of pieces of configuration option / section parent path.
:type: tuple[str]
"""
self.root_config = root_config
"""Root configuration object.
:type: :py:class:`Config`
"""
super(ChildConfig, self).__init__()
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,),
root_config=self.root_config)
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)

View File

@ -8,10 +8,20 @@ from .errors import Error
GLOBAL_LOCK = threading.RLock()
"""Dependency injector global reentrant lock.
:type: :py:class:`threading.RLock`
"""
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
hasattr(instance, '__IS_PROVIDER__') and
getattr(instance, '__IS_PROVIDER__') is True)
@ -20,7 +30,13 @@ def is_provider(instance):
def ensure_is_provider(instance):
"""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):
raise Error('Expected provider instance, '
@ -29,14 +45,29 @@ def ensure_is_provider(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
hasattr(instance, '__IS_INJECTION__') and
getattr(instance, '__IS_INJECTION__') is True)
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):
raise Error('Expected injection instance, '
'got {0}'.format(str(instance)))
@ -44,51 +75,99 @@ def ensure_is_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
hasattr(instance, '__IS_ARG_INJECTION__') and
getattr(instance, '__IS_ARG_INJECTION__', False) is True)
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
hasattr(instance, '__IS_KWARG_INJECTION__') and
getattr(instance, '__IS_KWARG_INJECTION__', False) is True)
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
hasattr(instance, '__IS_ATTRIBUTE_INJECTION__') and
getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True)
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
hasattr(instance, '__IS_METHOD_INJECTION__') and
getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
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
getattr(instance, '__IS_CATALOG__', False) is True)
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))
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))
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
hasattr(instance, '__IS_CATALOG_BUNDLE__') and
getattr(instance, '__IS_CATALOG_BUNDLE__', False) is True)
@ -97,7 +176,13 @@ def is_catalog_bundle(instance):
def ensure_is_catalog_bundle(instance):
"""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):
raise Error('Expected catalog bundle instance, '

View File

@ -7,28 +7,35 @@ Current section of documentation describes advanced usage of
@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
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
``di.inject()`` is called, positional and keyword argument injections would be
passed as an callable arguments.
:py:func:`inject` is called, positional and keyword argument injections would
be passed as an callable arguments.
Such behaviour is very similar to the standard Python ``functools.partial``
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,
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.
``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``:
- Positional context arguments will be appended after ``di.inject()``
- Positional context arguments will be appended after :py:func:`inject`
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.
Example:
@ -36,7 +43,7 @@ Example:
.. literalinclude:: ../../examples/advanced_usage/inject_simple.py
: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
:language: python
@ -45,11 +52,12 @@ Example of usage ``@di.inject()`` decorator with Flask:
@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
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
: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.
While catalog could be used as a centralized place for particular providers
group, such bundles of catalog providers can be used for creating several
limited scopes that could be passed to different subsystems.
.. module:: dependency_injector.catalogs
``di.DeclarativeCatalog.Bundle`` has exactly the same API as
``di.DeclarativeCatalog`` except of the limitations on getting providers.
:py:class:`CatalogBundle` is a frozen, limited collection of catalog
providers. While catalog could be used as a centralized place for
particular providers group, such bundles of catalog providers can be used
for creating several frozen, limited scopes that could be passed to different
subsystems.
Each ``di.DeclarativeCatalog`` has a reference to its bundle class -
``di.DeclarativeCatalog.Bundle``. For example, if some concrete catalog has name
``SomeCatalog``, then its bundle class could be reached as
``SomeCatalog.Bundle``.
:py:class:`CatalogBundle` has API's parity with catalogs
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of
retrieving the providers, but it is "frozen" in terms of modification
provider's list.
``di.DeclarativeCatalog.Bundle`` expects to get the list of its catalog providers
:py:class:`CatalogBundle` is considered to be dependable on catalogs
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by
its design.
Each catalog (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`)
has a reference to its bundle class - :py:attr:`DeclarativeCatalog.Bundle`
(or :py:attr:`DynamicCatalog.Bundle` consequently). For example, subclass of
:py:class:`CatalogBundle` for some concrete declarative catalog
``SomeCatalog`` could be reached as ``SomeCatalog.Bundle``.
:py:class:`CatalogBundle` expects to get the list of its catalog providers
as positional arguments and will limit the scope of created bundle to this
list.
.. note::
Some notes about :py:class:`CatalogBundle` design.
Design and syntax of :py:class:`CatalogBundle` was developed with the idea
of keeping full functionalities of type-hinting and introspection of
modern IDE's. This design came from some practical experience of using
:py:class:`CatalogBundle` and considered to be the most comfortable for
developer.
Example:
.. image:: /images/catalogs/bundles.png

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
of providers.
Catalogs module API docs - :py:mod:`dependency_injector.catalogs`.
.. toctree::
:maxdepth: 2
writing
operating
declarative
dynamic
bundles
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
----------------------
.. module:: dependency_injector.catalogs
Catalogs can be overridden by other catalogs. This, actually, means that
all of the providers from overriding catalog will override providers with the
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 ``@di.override(AnotherCatalog)`` class decorator.
- Use :py:meth:`DeclarativeCatalog.override` method.
- Use :py:func:`override` class decorator.
Example of overriding catalog using ``di.DeclarativeCatalog.override()``
Example of overriding catalog using :py:meth:`DeclarativeCatalog.override`
method:
.. literalinclude:: ../../examples/catalogs/override.py
.. literalinclude:: ../../examples/catalogs/override_declarative.py
: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
Also there are several useful methods and properties that help to work with
catalog overridings:
Also there are several useful :py:class:`DeclarativeCatalog` methods and
properties that help to work with catalog overridings:
- ``di.DeclarativeCatalog.is_overridden`` - read-only, evaluated in runtime,
property that is set to True if catalog is overridden.
- ``di.DeclarativeCatalog.last_overriding`` - reference to the last overriding
catalog, if any.
- ``di.DeclarativeCatalog.overridden_by`` - tuple of all overriding catalogs.
- ``di.DeclarativeCatalog.reset_last_overriding()`` - reset last overriding
catalog.
- ``di.DeclarativeCatalog.reset_override()`` - reset all overridings for all
catalog providers.
- :py:attr:`DeclarativeCatalog.is_overridden` - read-only property that is set
to ``True`` if catalog is overridden.
- :py:attr:`DeclarativeCatalog.last_overriding` - read-only reference to
the last overriding catalog, if any.
- :py:attr:`DeclarativeCatalog.overridden_by` - tuple of all overriding
catalogs.
- :py:meth:`DeclarativeCatalog.reset_last_overriding()` - reset last
overriding catalog.
- :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
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
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
catalogs/index
advanced_usage/index
api/index
main/feedback
main/changelog

View File

@ -9,13 +9,31 @@ follows `Semantic versioning`_
Development version
-------------------
- Rename ``di.AbstractCatalog`` to ``di.DeclarativeCatalog``
- No features.
0.11.0
------
- Rename ``AbstractCatalog`` to ``DeclarativeCatalog``
(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
------
- Add more representable implementation for ``di.AbstractCatalog`` and
``di.AbstractCatalog.Bundle``.
- Add more representable implementation for ``AbstractCatalog`` and
``AbstractCatalog.Bundle``.
0.10.4
------
@ -35,22 +53,22 @@ Development version
0.10.0
------
- Add functionality for creating ``di.AbstractCatalog`` provider bundles.
- Improve ``di.AbstractCatalog`` inheritance.
- Improve ``di.AbstractCatalog`` overriding.
- Add functionality for creating ``AbstractCatalog`` provider bundles.
- Improve ``AbstractCatalog`` inheritance.
- Improve ``AbstractCatalog`` overriding.
- Add images for catalog "Writing catalogs" and "Operating with catalogs"
examples.
- Add functionality for using positional argument injections with
``di.Factory``, ``di.Singleton``, ``di.Callable`` providers and
``di.inject`` decorator.
- Add functionality for decorating classes with ``@di.inject``.
- Add ``di.Singleton.injections`` attribute that represents a tuple of all
``di.Singleton`` injections (including args, kwargs, attributes and methods).
- Add ``di.Callable.injections`` attribute that represents a tuple of all
``di.Callable`` injections (including args and kwargs).
- Add optimization for ``di.Injection.value`` property that will compute
``Factory``, ``Singleton``, ``Callable`` providers and
``inject`` decorator.
- Add functionality for decorating classes with ``@inject``.
- Add ``Singleton.injections`` attribute that represents a tuple of all
``Singleton`` injections (including args, kwargs, attributes and methods).
- Add ``Callable.injections`` attribute that represents a tuple of all
``Callable`` injections (including args and kwargs).
- Add optimization for ``Injection.value`` property that will compute
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 six 1.10.0.
- Add minor refactorings and code style fixes.
@ -58,8 +76,8 @@ Development version
0.9.5
-----
- Change provider attributes scope to public.
- Add ``di.Factory.injections`` attribute that represents a tuple of all
``di.Factory`` injections (including kwargs, attributes and methods).
- Add ``Factory.injections`` attribute that represents a tuple of all
``Factory`` injections (including kwargs, attributes and methods).
0.9.4
-----

View File

@ -12,7 +12,7 @@ framework can be installed from PyPi_:
pip install dependency_injector
# Installing particular version:
pip install dependency_injector==0.9.0
pip install dependency_injector==0.11.0
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
`GitHub releases page`_.
Verification of currently installed version could be done using ``di.VERSION``
constant:
Verification of currently installed version could be done using
:py:obj:`dependency_injector.VERSION` constant:
.. code-block:: bash
>>> import dependency_injector as di
>>> di.VERSION
'0.10.0'
>>> from dependency_injector import VERSION
>>> VERSION
'0.11.0'
.. _PyPi: https://pypi.python.org/pypi/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.
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
-------------
@ -91,18 +70,21 @@ interaction with each other.
There are 3 main entities:
- Providers. Providers are strategies of accesing objects. For example,
``di.providers.Factory`` creates new instance of provided
class every time it is called. ``di.providers.Singleton`` creates
provided instance once and returns it on every next call. Providers
could be overridden by another providers. Base class is -
``di.providers.Provider``.
:py:class:`dependency_injector.providers.Factory` creates new instance of
provided class every time it is called.
:py:class:`dependency_injector.providers.Singleton` creates provided
instance once and returns it on every next call. Providers could be
overridden by another providers. Base class is -
:py:class:`dependency_injector.providers.Provider`.
- Injections. Injections are instructions for making dependency injections
(there are several ways how they could be done). Injections are used mostly
by ``di.providers.Factory`` and ``di.providers.Singleton`` providers, but
these are not only cases. Base class is - ``di.injections.Injection``.
by :py:class:`dependency_injector.providers.Factory` and
: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
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

View File

@ -1,40 +1,43 @@
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
callable.
Callable providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Callable`` takes a various number of positional and keyword arguments
that are used as decorated callable injections. Every time, when
``di.Callable`` is called, positional and keyword argument injections would be
passed as an callable arguments.
:py:class:`Callable` takes a various number of positional and keyword
arguments that are used as decorated callable injections. Every time, when
:py:class:`Callable` is called, positional and keyword argument injections
would be passed as an callable arguments.
Such behaviour is very similar to the standard Python ``functools.partial``
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,
if injectable value of injection is a ``di.Factory``, it will provide new one
instance (as a result of its call) every time, when injection needs to be done.
if injectable value of injection is a :py:class:`Factory`, it will provide
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
very like a standard Python ``functools.partial``:
:py:class:`Callable` behaviour with context positional and keyword arguments
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.
- 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.
Example that shows usage of ``di.Callable`` with positional argument
Example that shows usage of :py:class:`Callable` with positional argument
injections:
.. literalinclude:: ../../examples/providers/callable_args.py
: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:
.. image:: /images/providers/callable.png
@ -47,13 +50,13 @@ injections:
Callable providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Callable`` provider could be delegated to any other provider via any kind
of injection. Delegation of ``di.Callable`` providers is the same as
``di.Factory`` and ``di.Singleton`` providers delegation, please follow
*Factory providers delegation* section for example.
:py:class:`Callable` provider could be delegated to any other provider via any
kind of injection. Delegation of :py:class:`Callable` providers is the same as
:py:class:`Factory` and :py:class:`Singleton` providers delegation, please
follow *Factory providers delegation* section for example.
``di.Callable`` delegate could be created obviously using
``di.Delegate(di.Callable())`` or by calling ``di.Callable.delegate()`` method.
:py:class:`Callable` delegate could be created obviously using
``Delegate(Callable(...))`` or by calling ``Callable(...).delegate()`` method.
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.
Below are some tips and recommendations that have to be met:
1. Every custom provider has to extend base provider class -
``di.Provider``.
2. Cusom provider's ``__init__()`` could be overriden with only condition:
parent initializer (``di.Provider.__init__()``) has to be called.
:py:class:`Provider`.
2. Cusom provider's ``__init__()`` could be overriden, but parent's
initializer (:py:meth:`Provider.__init__`) has to be called.
3. Providing strategy has to be implemented in custom provider's
``_provide()`` method. All ``*args`` & ``**kwargs`` that will be
recieved by ``di.Provider.__call__()`` will be transefed to custom
provider's ``_provide()``.
:py:meth:`Provider._provide` method. All ``*args`` & ``**kwargs``
that will be recieved by :py:meth:`Provider.__call__` will be
transefed to custom provider's :py:meth:`Provider._provide`.
4. If custom provider is based on some standard providers, it is better to
use delegation of standard providers, then extending of them.
5. If custom provider defines any attributes, it is good to list them in
``__slots__`` attribute (as *Dependency Injector* does). It can save
some memory.
6. If custom provider deals with injections (e.g. ``di.Factory``,
``di.Singleton`` providers), it is strongly recommended to be
consistent with ``di.Factory``, ``di.Singleton`` and ``di.Callable``
providers style.
6. If custom provider deals with injections, it is strongly recommended
to be consistent with :py:class:`Factory`, :py:class:`Singleton` and
:py:class:`Callable` providers style.
Example:

View File

@ -1,7 +1,9 @@
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
dependencies.

View File

@ -1,7 +1,10 @@
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:
@ -15,21 +18,22 @@ Nothing could be better than brief example:
Factory providers and __init__ injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Factory`` takes a various number of positional and keyword arguments that
are used as ``__init__()`` injections. Every time, when ``di.Factory``
creates new one instance, positional and keyword argument injections would be
passed as an instance's arguments.
:py:class:`Factory` takes a various number of positional and keyword arguments
that are used as ``__init__()`` injections. Every time, when
:py:class:`Factory` creates new one instance, positional and keyword
argument injections would be passed as an instance's arguments.
Such behaviour is very similar to the standard Python ``functools.partial``
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,
if injectable value of injection is a ``di.Factory``, it will provide new one
instance (as a result of its call) every time, when injection needs to be done.
if injectable value of injection is a :py:class:`Factory`, it will provide
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
``di.Factory`` of particular class with ``__init__()`` argument injections
which injectable values are also provided by another factories:
:py:class:`Factory` of particular class with ``__init__()`` argument
injections which injectable values are also provided by another factories:
.. note::
@ -58,14 +62,14 @@ Example of usage keyword argument injections:
Factory providers and __init__ injections priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Next example shows how ``di.Factory`` provider deals with positional and
keyword ``__init__()`` context arguments. In few words, ``di.Factory``
Next example shows how :py:class:`Factory` provider deals with positional and
keyword ``__init__()`` context arguments. In few words, :py:class:`Factory`
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.
- Keyword context arguments have priority on ``di.Factory`` keyword injections
and will be merged over them.
- Keyword context arguments have priority on :py:class:`Factory` keyword
injections and will be merged over them.
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
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.
All of those instructions are defined in ``di.injections`` module and are
subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There
are several types of injections that are used by ``di.Factory`` provider:
All of those instructions are defined in
:py:mod:`dependency_injector.injections` module and are subclasses of
: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
``__init__()`` method in time of object's creation as positional argument.
Takes injectable value only.
+ ``di.KwArg`` - injection is done by passing injectable value in object's
``__init__()`` method in time of object's creation as keyword argument.
Takes keyword name of ``__init__()`` argument and injectable value.
+ ``di.Attribute`` - injection is done by setting specified attribute with
injectable value right after object's creation. Takes attribute's name
and injectable value.
+ ``di.Method`` - injection is done by calling of specified method with
injectable value right after object's creation and attribute injections
are done. Takes method name and injectable value.
+ :py:class:`dependency_injector.injections.Arg` - injection is done by
passing injectable value in object's ``__init__()`` method in time of
object's creation as positional argument. Takes injectable value only.
+ :py:class:`dependency_injector.injections.KwArg` - injection is done by
passing injectable value in object's ``__init__()`` method in time of
object's creation as keyword argument. Takes keyword name of
``__init__()`` argument and injectable value.
+ :py:class:`dependency_injector.injections.Attribute` - injection is done
by setting specified attribute with injectable value right after
object's creation. Takes attribute's name and injectable value.
+ :py:class:`dependency_injector.injections.Method` - injection is done by
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
providers (subclasses of ``di.Provider``). Providers will be called every time,
when injection needs to be done.
All :py:class:`dependency_injector.injections.Injection`'s injectable values
are provided *"as is"*, except of providers (subclasses of
:py:class:`Provider`). Providers will be called every time, when injection
needs to be done.
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
with injectable values right after object's creation.
@ -123,10 +131,10 @@ Example:
Factory providers and method injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Current example shows how to create ``di.Factory`` of particular class with
method injections. Those injections are done by calling of specified method
with injectable value right after object's creation and attribute injections
are done.
Current example shows how to create :py:class:`Factory` of particular class
with method injections. Those injections are done by calling of specified
method with injectable value right after object's creation and attribute
injections are done.
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
@ -142,21 +150,21 @@ Example:
Factory providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Factory`` provider could be delegated to any other provider via any kind
of injection. As it was mentioned earlier, if ``di.Factory`` is injectable
value, it will be called every time when injection is done. ``di.Factory``
delegation is performed by wrapping delegated ``di.Factory`` into special
provider type - ``di.Delegate``, that just returns wrapped ``di.Factory``.
Saying in other words, delegation of factories - is a way to inject factories
themselves, instead of results of their calls.
:py:class:`Factory` provider could be delegated to any other provider via any
kind of injection. As it was mentioned earlier, if :py:class:`Factory` is
injectable value, it will be called every time when injection is done.
:py:class:`Factory` delegation is performed by wrapping delegated
:py:class:`Factory` into special provider type - :py:class:`Delegate`, that
just returns wrapped :py:class:`Factory`. Saying in other words, delegation
of factories - is a way to inject factories themselves, instead of results
of their calls.
Actually, there are two ways of creating factory delegates:
+ ``di.Delegate(di.Factory(...))`` - obviously wrapping factory into
``di.Delegate`` provider.
+ ``di.Factory(...).delegate()`` - calling factory ``delegate()`` method, that
returns delegate wrapper for current factory.
+ ``Delegate(Factory(...))`` - obviously wrapping factory into
:py:class:`Delegate` provider.
+ ``Factory(...).delegate()`` - calling factory :py:meth:`Factory.delegate`
method, that returns delegate wrapper for current factory.
Example:

View File

@ -4,17 +4,21 @@ Providers
Providers are strategies of accessing objects. They describe how particular
objects are provided.
Base providers class is - :py:class:`dependency_injector.providers.Provider`
Every provider is callable (implements ``__call__()``). Every call to provider
instance returns provided result, according to the providing strategy of
particular provider.
Current documentation section consists from description of standard providers
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
be thread safe.
Providers module API docs - :py:mod:`dependency_injector.providers`
.. toctree::
:maxdepth: 2

View File

@ -1,6 +1,8 @@
Overriding of providers
-----------------------
.. module:: dependency_injector.providers
Every provider could be overridden by another provider.
This gives opportunity to make system behaviour more flexible in some points.
@ -15,22 +17,22 @@ Provider overriding functionality has such interface:
:width: 45%
: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.
In such case, last passed provider would be used as overriding one.
+ ``di.Provider.reset_override()`` - resets all overriding providers. Provider
starts to behave itself like usual.
+ ``di.Provider.is_overridden`` - bool, ``True`` if provider is overridden.
+ :py:meth:`Provider.reset_override()` - resets all overriding providers.
Provider starts to behave itself like usual.
+ :py:attr:`Provider.is_overridden` - bool, ``True`` if provider is overridden.
.. note::
Actually, initial provider forms stack from overriding providers. There is
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.
+ ``di.Provider.reset_last_overriding()`` - remove last overriding provider
from stack of overriding providers.
+ :py:meth:`Provider.reset_last_overriding()` - remove last overriding
provider from stack of overriding providers.
Example:

View File

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

View File

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

View File

@ -1,6 +1,7 @@
"""Config provider examples."""
import dependency_injector as di
from dependency_injector import catalogs
from dependency_injector import providers
class ObjectA(object):
@ -13,17 +14,17 @@ class ObjectA(object):
self.timezone = timezone
class Catalog(di.AbstractCatalog):
class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of providers."""
config = di.Config()
""":type: di.Config"""
config = providers.Config()
""":type: providers.Config"""
object_a = di.Factory(ObjectA,
fee=config.FEE,
price=config.PRICE,
timezone=config.GLOBAL.TIMEZONE)
""":type: di.Provider -> ObjectA"""
object_a = providers.Factory(ObjectA,
fee=config.FEE,
price=config.PRICE,
timezone=config.GLOBAL.TIMEZONE)
""":type: providers.Provider -> ObjectA"""
# 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 flask
import dependency_injector as di
from dependency_injector import providers
from dependency_injector import injections
database = di.Singleton(sqlite3.connect,
':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE')
database = providers.Singleton(sqlite3.connect,
':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE')
app = flask.Flask(__name__)
@app.route('/')
@di.inject(database)
@di.inject(flask.request)
@injections.inject(database)
@injections.inject(flask.request)
def hello(request, database):
"""Example Flask view."""
print request

View File

@ -1,22 +1,24 @@
"""`@di.inject()` decorator with classes example."""
"""`inject()` decorator with classes example."""
import sqlite3
import flask
import flask.views
import dependency_injector as di
from dependency_injector import providers
from dependency_injector import injections
database = di.Singleton(sqlite3.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE')
database = providers.Singleton(sqlite3.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE')
app = flask.Flask(__name__)
@di.inject(database=database)
@di.inject(some_setting=777)
@injections.inject(database=database)
@injections.inject(some_setting=777)
class HelloView(flask.views.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:
@di.inject(new_object=dependency_injector_factory)
@di.inject(some_setting=1334)
@injections.inject(new_object=dependency_injector_factory)
@injections.inject(some_setting=1334)
def example_callback1(new_object, some_setting):
"""Example callback that does some asserts for input args."""
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:
@di.inject(dependency_injector_factory, 1334)
@injections.inject(dependency_injector_factory, 1334)
def example_callback2(new_object, some_setting):
"""Example callback that does some asserts for input args."""
assert isinstance(new_object, object)

View File

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

View File

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

View File

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

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."""
provider1 = di.Factory(object)
""":type: di.Provider -> object"""
provider1 = providers.Factory(object)
""":type: providers.Provider -> object"""
class CatalogB(CatalogA):
"""Example catalog B."""
provider2 = di.Singleton(object)
""":type: di.Provider -> object"""
provider2 = providers.Singleton(object)
""":type: providers.Provider -> object"""
# 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:
assert CatalogA.inherited_providers == dict()
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`."""
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):
@ -21,20 +24,20 @@ class AuthService(object):
self.users_service = users_service
class Services(di.DeclarativeCatalog):
class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
database = di.Singleton(sqlite3.connect, ':memory:')
""":type: di.Provider -> sqlite3.Connection"""
database = providers.Singleton(sqlite3.connect, ':memory:')
""":type: providers.Provider -> sqlite3.Connection"""
users = di.Factory(UsersService,
db=database)
""":type: di.Provider -> UsersService"""
users = providers.Factory(UsersService,
db=database)
""":type: providers.Provider -> UsersService"""
auth = di.Factory(AuthService,
db=database,
users_service=users)
""":type: di.Provider -> AuthService"""
auth = providers.Factory(AuthService,
db=database,
users_service=users)
""":type: providers.Provider -> AuthService"""
# Retrieving catalog providers:
@ -49,9 +52,9 @@ assert auth_service is not Services.auth()
# Making some "inline" injections:
@di.inject(users_service=Services.users)
@di.inject(auth_service=Services.auth)
@di.inject(database=Services.database)
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
@injections.inject(database=Services.database)
def example(users_service, auth_service, database):
"""Example callback."""
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:
even_filter = di.Callable(filter, lambda x: x % 2 == 0)
odd_filter = di.Callable(filter, lambda x: x % 2 != 0)
even_filter = providers.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:
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 dependency_injector as di
from dependency_injector import providers
# 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_delegate2 = di.Delegate(callable_provider)
callable_provider_delegate2 = providers.Delegate(callable_provider)
# Making some asserts:
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 dependency_injector as di
from dependency_injector import providers
# Password hasher and verifier providers (hash function could be changed
# anytime (for example, to sha512) without any changes in client's code):
password_hasher = di.Callable(passlib.hash.sha256_crypt.encrypt,
salt_size=16,
rounds=10000)
password_verifier = di.Callable(passlib.hash.sha256_crypt.verify)
password_hasher = providers.Callable(passlib.hash.sha256_crypt.encrypt,
salt_size=16,
rounds=10000)
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
# Making some asserts:
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):
"""Example class User."""
class UsersFactory(di.Provider):
class UsersFactory(providers.Provider):
"""Example users factory."""
__slots__ = ('_factory',)
def __init__(self):
"""Initializer."""
self._factory = di.Factory(User)
self._factory = providers.Factory(User)
super(UsersFactory, self).__init__()
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)

View File

@ -1,8 +1,9 @@
"""`di.ExternalDependency` providers example."""
"""`ExternalDependency` providers example."""
import sqlite3
import contextlib
import dependency_injector as di
from dependency_injector import providers
class UserService(object):
@ -14,7 +15,8 @@ class UserService(object):
def __init__(self, database):
"""Initializer.
Database dependency need to be injected via init arg.
:param database: Database connection.
:type database: sqlite3.dbapi2.Connection
"""
self.database = database
self.database.row_factory = sqlite3.dbapi2.Row
@ -43,18 +45,18 @@ class UserService(object):
# Database and UserService providers:
database = di.ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = di.Factory(UserService,
database=database)
database = providers.ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = providers.Factory(UserService,
database=database)
# Out of library's scope.
#
# Setting database provider:
database.provided_by(di.Singleton(sqlite3.dbapi2.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE'))
database.provided_by(providers.Singleton(sqlite3.dbapi2.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE'))
# Creating UserService instance:
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):
"""Example class User."""
# 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:
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):
@ -20,11 +21,13 @@ class CreditCard(object):
"""Example class CreditCard."""
# User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard)
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User,
di.Attribute('main_photo', photos_factory),
di.Attribute('credit_card', credit_cards_factory))
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
injections.Attribute('main_photo',
photos_factory),
injections.Attribute('credit_card',
credit_cards_factory))
# Creating several User objects:
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):
@ -9,7 +9,7 @@ class User(object):
def __init__(self, photos_factory):
"""Initializer.
:param photos_factory: (di.Factory) -> Photo
:param photos_factory: providers.Factory -> Photo
"""
self.photos_factory = photos_factory
self._main_photo = None
@ -27,9 +27,9 @@ class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User,
photos_factory=di.Delegate(photos_factory))
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
photos_factory=photos_factory.delegate())
# Creating several User objects:
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):
@ -16,8 +16,8 @@ class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User, photos_factory)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User, photos_factory)
# Creating several User objects:
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):
@ -30,11 +30,11 @@ class CreditCard(object):
"""Example class CreditCard."""
# User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard)
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User,
main_photo=photos_factory,
credit_card=credit_cards_factory)
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
main_photo=photos_factory,
credit_card=credit_cards_factory)
# Creating several User objects:
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):
@ -16,8 +16,8 @@ class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User, main_photo=photos_factory)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User, main_photo=photos_factory)
# Creating several User objects:
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):
@ -28,11 +29,13 @@ class CreditCard(object):
"""Example class CreditCard."""
# User, Photo and CreditCard factories:
credit_cards_factory = di.Factory(CreditCard)
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User,
di.Method('set_main_photo', photos_factory),
di.Method('set_credit_card', credit_cards_factory))
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
injections.Method('set_main_photo',
photos_factory),
injections.Method('set_credit_card',
credit_cards_factory))
# Creating several User objects:
user1 = users_factory()

View File

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

View File

@ -1,6 +1,6 @@
"""Overriding user's model example."""
import dependency_injector as di
from dependency_injector import providers
class User(object):
@ -26,8 +26,7 @@ class UserService(object):
return self.user_cls(id=id, password='secret' + str(id))
# Users factory and UserService provider:
users_service = di.Factory(UserService,
user_cls=User)
users_service = providers.Factory(UserService, user_cls=User)
# Getting several users and making some asserts:
user1 = users_service().get_by_id(1)
@ -71,8 +70,8 @@ class ExtendedUserService(UserService):
return user
# Overriding users_service provider:
extended_users_service = di.Factory(ExtendedUserService,
user_cls=ExtendedUser)
extended_users_service = providers.Factory(ExtendedUserService,
user_cls=ExtendedUser)
users_service.override(extended_users_service)
# 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):
@ -8,7 +8,7 @@ class UserService(object):
# Singleton provider creates new instance of specified class on first call and
# returns same instance on every next call.
users_service_provider = di.Singleton(UserService)
users_service_provider = providers.Singleton(UserService)
# Retrieving several UserService objects:
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:
singleton_provider = di.Singleton(object)
singleton_provider = providers.Singleton(object)
singleton_provider_delegate1 = singleton_provider.delegate()
singleton_provider_delegate2 = di.Delegate(singleton_provider)
singleton_provider_delegate2 = providers.Delegate(singleton_provider)
# Making some asserts:
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):
"""Example class UserService."""
# Users service singleton provider:
users_service_provider = di.Singleton(UserService)
users_service_provider = providers.Singleton(UserService)
# Retrieving several UserService objects:
user_service1 = users_service_provider()

View File

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

View File

@ -1 +1,13 @@
"""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.get_provider('px'), px)
self.assertIs(CatalogA.catalog.px, px)
self.assertIs(CatalogA.py, py)
self.assertIs(CatalogA.get_provider('py'), py)
self.assertIs(CatalogA.catalog.py, py)
del CatalogA.px
del CatalogA.py
@ -300,11 +298,9 @@ class DeclarativeCatalogTests(unittest.TestCase):
self.assertIs(CatalogB.px, px)
self.assertIs(CatalogB.get_provider('px'), px)
self.assertIs(CatalogB.catalog.px, px)
self.assertIs(CatalogB.py, py)
self.assertIs(CatalogB.get_provider('py'), py)
self.assertIs(CatalogB.catalog.py, py)
del CatalogB.px
del CatalogB.py
@ -319,11 +315,9 @@ class DeclarativeCatalogTests(unittest.TestCase):
self.assertIs(CatalogB.px, px)
self.assertIs(CatalogB.get_provider('px'), px)
self.assertIs(CatalogB.catalog.px, px)
self.assertIs(CatalogB.py, py)
self.assertIs(CatalogB.get_provider('py'), py)
self.assertIs(CatalogB.catalog.py, py)
del CatalogB.px
del CatalogB.py
@ -480,6 +474,23 @@ class OverrideTests(unittest.TestCase):
self.assertEqual(CatalogA.p12(), 2)
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):
"""Test catalog overriding with another dynamic catalog."""
CatalogA.override(catalogs.DynamicCatalog(p11=providers.Value(1),
@ -512,8 +523,7 @@ class OverrideTests(unittest.TestCase):
def test_last_overriding_on_not_overridden(self):
"""Test catalog last_overriding property on not overridden catalog."""
with self.assertRaises(errors.Error):
CatalogA.last_overriding
self.assertIsNone(CatalogA.last_overriding)
def test_reset_last_overriding(self):
"""Test resetting last overriding catalog."""
@ -561,3 +571,14 @@ class OverrideTests(unittest.TestCase):
self.assertFalse(CatalogA.p11.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):
"""Test getting last overriding from not overridden provider."""
try:
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)))
self.assertIsNone(self.provider.last_overriding)
def test_reset_last_overriding(self):
"""Test reseting of last overriding provider."""
@ -129,13 +123,7 @@ class ProviderTests(unittest.TestCase):
self.provider.reset_override()
self.assertFalse(self.provider.is_overridden)
try:
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)))
self.assertIsNone(self.provider.last_overriding)
class DelegateTests(unittest.TestCase):
@ -564,6 +552,35 @@ class SingletonTests(unittest.TestCase):
self.assertIsInstance(instance1, 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):
"""Test getting a full list of injections using injections property."""
provider = providers.Singleton(Example,