Merge remote-tracking branch 'origin/2.0'

This commit is contained in:
Roman Mogilatov 2016-06-09 19:45:09 +03:00
commit 1c47f73610
158 changed files with 2307 additions and 5794 deletions

View File

@ -23,9 +23,6 @@ Status
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Latest Version |
| | .. image:: https://img.shields.io/pypi/dm/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Downloads |
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: License |
@ -55,61 +52,133 @@ Installation
Example
-------
Brief example below demonstrates usage of *Dependency Injector* containers and
providers for definition of several IoC containers for some microservice
system that consists from several business and platform services:
.. code-block:: python
"""Dependency Injector example."""
"""Example of several Dependency Injector IoC containers."""
import sys
import sqlite3
import boto.s3.connection
import example.services
from boto.s3.connection import S3Connection
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
from example import services
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Platform(catalogs.DeclarativeCatalog):
"""Catalog of platform service providers."""
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
s3 = providers.Singleton(S3Connection,
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(catalogs.DeclarativeCatalog):
"""Catalog of business service providers."""
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(services.Users,
users = providers.Factory(example.services.Users,
db=Platform.database)
photos = providers.Factory(services.Photos,
db=Platform.database,
s3=Platform.s3)
auth = providers.Factory(services.Auth,
auth = providers.Factory(example.services.Auth,
db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.Photos,
db=Platform.database,
s3=Platform.s3)
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
@injections.inject(photos_service=Services.photos)
def main(argv, users_service, auth_service, photos_service):
Next example demonstrates usage of ``@inject`` decorator with IoC containers
defined above:
.. code-block:: python
"""Dependency Injector @inject decorator example."""
import application
import dependency_injector.injections as injections
@injections.inject(users_service=application.Services.users)
@injections.inject(auth_service=application.Services.auth)
@injections.inject(photos_service=application.Services.photos)
def main(users_service, auth_service, photos_service):
"""Main function."""
login, password, photo_path = argv[1:]
user = users_service.get_user(login)
auth_service.authenticate(user, password)
photos_service.upload_photo(user['id'], photo_path)
user = users_service.get_user('user')
auth_service.authenticate(user, 'secret')
photos_service.upload_photo(user['id'], 'photo.jpg')
if __name__ == '__main__':
main(sys.argv)
main()
Alternative definition styles
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Dependecy Injector* supports few other styles of dependency injections
definition.
IoC containers from previous example could look like these:
.. code-block:: python
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
database = providers.Singleton(sqlite3.connect) \
.add_args(':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection) \
.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(example.services.Users) \
.add_kwargs(db=Platform.database)
auth = providers.Factory(example.services.Auth) \
.add_kwargs(db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.Photos) \
.add_kwargs(db=Platform.database,
s3=Platform.s3)
or like this these:
.. code-block:: python
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
database = providers.Singleton(sqlite3.connect)
database.add_args(':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection)
s3.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(example.services.Users)
users.add_kwargs(db=Platform.database)
auth = providers.Factory(example.services.Auth)
auth.add_kwargs(db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.Photos)
photos.add_kwargs(db=Platform.database,
s3=Platform.s3)
You can get more *Dependency Injector* examples in ``/examples`` directory on
GitHub:

View File

@ -1,126 +1,7 @@
"""Dependency injector."""
"""Dependency injector top-level package."""
from dependency_injector.catalogs import (
DeclarativeCatalog,
AbstractCatalog,
DynamicCatalog,
CatalogBundle,
override,
)
from dependency_injector.providers import (
Provider,
Delegate,
Callable,
DelegatedCallable,
Factory,
DelegatedFactory,
Singleton,
DelegatedSingleton,
ExternalDependency,
StaticProvider,
Class,
Object,
Function,
Value,
Config,
)
from dependency_injector.injections import (
Injection,
Arg,
KwArg,
Attribute,
Method,
inject,
)
from dependency_injector.utils import (
is_provider,
ensure_is_provider,
is_delegated_provider,
is_injection,
ensure_is_injection,
is_arg_injection,
is_kwarg_injection,
is_attribute_injection,
is_method_injection,
is_catalog,
is_dynamic_catalog,
is_declarative_catalog,
is_catalog_bundle,
ensure_is_catalog_bundle,
)
from dependency_injector.errors import (
Error,
UndefinedProviderError,
)
# Backward compatibility for versions < 0.11.*
from dependency_injector import catalogs
catalog = catalogs
VERSION = '1.17.0'
VERSION = '2.0.0'
"""Version number that follows semantic versioning.
:type: str
"""
__all__ = (
# Catalogs
'DeclarativeCatalog',
'AbstractCatalog',
'DynamicCatalog',
'CatalogBundle',
'override',
# Providers
'Provider',
'Delegate',
'Callable',
'DelegatedCallable',
'Factory',
'DelegatedFactory',
'Singleton',
'DelegatedSingleton',
'ExternalDependency',
'StaticProvider',
'Class',
'Object',
'Function',
'Value',
'Config',
# Injections
'Injection',
'Arg',
'KwArg',
'Attribute',
'Method',
'inject',
# Utils
'is_provider',
'ensure_is_provider',
'is_delegated_provider',
'is_injection',
'ensure_is_injection',
'is_arg_injection',
'is_kwarg_injection',
'is_attribute_injection',
'is_method_injection',
'is_catalog',
'is_dynamic_catalog',
'is_declarative_catalog',
'is_catalog_bundle',
'ensure_is_catalog_bundle',
# Errors
'Error',
'UndefinedProviderError',
# Version
'VERSION'
)

View File

@ -1,24 +0,0 @@
"""Dependency injector catalogs package."""
from dependency_injector.catalogs.bundle import CatalogBundle
from dependency_injector.catalogs.dynamic import DynamicCatalog
from dependency_injector.catalogs.declarative import (
DeclarativeCatalogMetaClass,
DeclarativeCatalog,
AbstractCatalog,
)
from dependency_injector.catalogs.utils import (
copy,
override
)
__all__ = (
'CatalogBundle',
'DynamicCatalog',
'DeclarativeCatalogMetaClass',
'DeclarativeCatalog',
'AbstractCatalog',
'copy',
'override',
)

View File

@ -1,118 +0,0 @@
"""Dependency injector catalogs bundle module."""
import six
from dependency_injector.errors import (
Error,
UndefinedProviderError,
)
@six.python_2_unicode_compatible
class CatalogBundle(object):
"""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.
.. py:attribute:: catalog
Bundle's catalog.
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`
.. py:attribute:: providers
Dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
catalog = None
__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.
:param providers: Tuple of catalog's bundle providers.
:type providers: tuple[
:py:class:`dependency_injector.providers.Provider`]
"""
self.providers = dict((self.catalog.get_provider_bind_name(provider),
provider)
for provider in providers)
self.__dict__.update(self.providers)
super(CatalogBundle, self).__init__()
def get_provider(self, 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`
"""
try:
return self.providers[name]
except KeyError:
raise Error('Provider "{0}" is not a part of {1}'.format(name,
self))
def has_provider(self, 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):
"""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's bundle.
:rtype: str
"""
return '<{0}.Bundle({1})>'.format(
self.catalog.name, ', '.join(six.iterkeys(self.providers)))
__str__ = __repr__

View File

@ -1,491 +0,0 @@
"""Dependency injector declarative catalog module."""
import six
from dependency_injector.catalogs.dynamic import DynamicCatalog
from dependency_injector.catalogs.bundle import CatalogBundle
from dependency_injector.utils import (
is_provider,
is_catalog,
is_declarative_catalog,
)
from dependency_injector.errors import (
Error,
UndefinedProviderError,
)
@six.python_2_unicode_compatible
class DeclarativeCatalogMetaClass(type):
"""Declarative catalog meta class."""
def __new__(mcs, class_name, bases, attributes):
"""Declarative catalog class factory."""
cls_providers = tuple((name, provider)
for name, provider in six.iteritems(attributes)
if is_provider(provider))
inherited_providers = tuple((name, provider)
for base in bases if is_catalog(base)
for name, provider in six.iteritems(
base.providers))
providers = cls_providers + inherited_providers
cls = type.__new__(mcs, class_name, bases, attributes)
if cls.provider_type:
cls._catalog = type('DynamicCatalog',
(DynamicCatalog,),
dict(provider_type=cls.provider_type))()
else:
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
return cls
@property
def name(cls):
"""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):
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._catalog.providers
@property
def cls_providers(cls):
"""Read-only dictionary of current catalog providers.
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._cls_providers
@property
def inherited_providers(cls):
"""Read-only dictionary of inherited providers.
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return cls._inherited_providers
@property
def overridden_by(cls):
"""Tuple of overriding catalogs.
:type: tuple[
:py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`]
"""
return cls._catalog.overridden_by
@property
def is_overridden(cls):
"""Read-only property that is set to ``True`` if catalog is overridden.
:rtype: bool
"""
return cls._catalog.is_overridden
@property
def last_overriding(cls):
"""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.
: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))
def __setattr__(cls, 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.
: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):
cls.bind_provider(name, value, _set_as_attribute=False)
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.
:param name: Attribute's name.
:type name: str
:rtype: None
"""
if is_provider(getattr(cls, name)):
delattr(cls._catalog, name)
return super(DeclarativeCatalogMetaClass, cls).__delattr__(name)
def __repr__(cls):
"""Return string representation of the catalog.
:rtype: str
"""
return '<{0}({1})>'.format(cls.name,
', '.join(six.iterkeys(cls.providers)))
__str__ = __repr__
@six.add_metaclass(DeclarativeCatalogMetaClass)
class DeclarativeCatalog(object):
"""Declarative catalog of providers.
: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).
.. code-block:: python
class Services(DeclarativeCatalog):
auth = providers.Factory(AuthService)
users = providers.Factory(UsersService)
users_service = Services.users()
.. py:attribute:: Bundle
Catalog's bundle class.
:type: :py:class:`CatalogBundle`
.. py:attribute:: provider_type
If provider type is defined, :py:class:`DeclarativeCatalog` checks that
all of its providers are instances of
:py:attr:`DeclarativeCatalog.provider_type`.
:type: type | None
"""
Bundle = CatalogBundle
provider_type = None
_catalog = DynamicCatalog
_cls_providers = dict()
_inherited_providers = dict()
__IS_CATALOG__ = True
@property
def name(self):
"""Read-only property that represents catalog's name.
Catalog's name is catalog's module + catalog's class name.
:rtype: str
"""
return self.__class__.name
@property
def providers(self):
"""Read-only dictionary of all providers.
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return self.__class__.providers
@property
def cls_providers(self):
"""Read-only dictionary of current catalog providers.
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return self.__class__.cls_providers
@property
def inherited_providers(self):
"""Read-only dictionary of inherited providers.
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
return self.__class__.inherited_providers
@property
def overridden_by(self):
"""Tuple of overriding catalogs.
:rtype: tuple[:py:class:`DeclarativeCatalog` |
:py:class:`DynamicCatalog`]
"""
return self.__class__.overridden_by
@property
def is_overridden(self):
"""Read-only property that is set to ``True`` if catalog is overridden.
:rtype: bool
"""
return self.__class__.is_overridden
@property
def last_overriding(self):
"""Read-only reference to the last overriding catalog, if any.
:rtype: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
None
"""
return self.__class__.last_overriding
@classmethod
def is_bundle_owner(cls, 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.
: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.
: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 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.
: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
"""
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.
:rtype: None
"""
cls._catalog.reset_last_overriding()
@classmethod
def reset_override(cls):
"""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.
: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, force=False,
_set_as_attribute=True):
"""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`
:param force: Force binding of provider.
:type force: bool
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
if cls._catalog.is_provider_bound(provider):
bindind_name = cls._catalog.get_provider_bind_name(provider)
if bindind_name == name and not force:
return
cls._catalog.bind_provider(name, provider, force)
cls.cls_providers[name] = provider
if _set_as_attribute:
setattr(cls, name, provider)
@classmethod
def bind_providers(cls, providers, force=False):
"""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`]
:param force: Force binding of providers.
:type force: bool
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
for name, provider in six.iteritems(providers):
cls.bind_provider(name, provider, force=force)
@classmethod
def has_provider(cls, 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.
:param name: Provider's name.
:type name: str
:rtype: None
"""
delattr(cls, name)
del cls.cls_providers[name]
@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.*
AbstractCatalog = DeclarativeCatalog

View File

@ -1,338 +0,0 @@
"""Dependency injector dynamic catalog module."""
import six
from dependency_injector.catalogs.bundle import CatalogBundle
from dependency_injector.utils import (
is_provider,
ensure_is_provider,
ensure_is_catalog_bundle,
)
from dependency_injector.errors import (
Error,
UndefinedProviderError,
)
@six.python_2_unicode_compatible
class DynamicCatalog(object):
"""Dynamic catalog of providers.
:py:class:`DynamicCatalog` is a catalog of providers that could be created
in application's runtime. It should cover most of the cases when list of
providers that would be included in catalog is non-deterministic in terms
of apllication code (catalog's structure could be determined just after
application will be started and will do some initial work, like parsing
list of catalog's providers from the configuration).
.. code-block:: python
services = DynamicCatalog(auth=providers.Factory(AuthService),
users=providers.Factory(UsersService))
users_service = services.users()
.. py:attribute:: Bundle
Catalog's bundle class.
:type: :py:class:`CatalogBundle`
.. py:attribute:: name
Catalog's name.
By default, it is catalog's module + catalog's class name.
:type: str
.. py:attribute:: providers
Dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
.. py:attribute:: overridden_by
Tuple of overriding catalogs.
:type: tuple[
:py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`]
.. py:attribute:: provider_type
If provider type is defined, :py:class:`DynamicCatalog` checks that
all of its providers are instances of
:py:attr:`DynamicCatalog.provider_type`.
:type: type | None
"""
provider_type = None
__IS_CATALOG__ = True
__slots__ = ('name', 'providers', 'provider_names', 'overridden_by',
'Bundle')
def __init__(self, **providers):
"""Initializer.
:param providers: Dictionary of catalog providers.
:type providers:
dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
self.Bundle = CatalogBundle.sub_cls_factory(self)
self.name = '.'.join((self.__class__.__module__,
self.__class__.__name__))
self.providers = dict()
self.provider_names = dict()
self.overridden_by = tuple()
self.bind_providers(providers)
super(DynamicCatalog, self).__init__()
def is_bundle_owner(self, bundle):
"""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.
: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.
: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 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):
"""Read-only property that is set to ``True`` if catalog is overridden.
:rtype: bool
"""
return bool(self.overridden_by)
@property
def last_overriding(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.
: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.
:rtype: None
"""
if not self.is_overridden:
raise Error('Catalog {0} is not overridden'.format(self))
self.overridden_by = self.overridden_by[:-1]
for provider in six.itervalues(self.providers):
provider.reset_last_overriding()
def reset_override(self):
"""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.
: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:
raise UndefinedProviderError('{0} has no provider with such '
'name - {1}'.format(self, name))
def bind_provider(self, name, provider, force=False):
"""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`
:param force: Force binding of provider.
:type force: bool
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
provider = ensure_is_provider(provider)
if (self.__class__.provider_type and
not isinstance(provider, self.__class__.provider_type)):
raise Error('{0} can contain only {1} instances'.format(
self, self.__class__.provider_type))
if not force:
if name in self.providers:
raise Error('Catalog {0} already has provider with '
'such name - {1}'.format(self, name))
if provider in self.provider_names:
raise Error('Catalog {0} already has such provider '
'instance - {1}'.format(self, provider))
self.providers[name] = provider
self.provider_names[provider] = name
def bind_providers(self, providers, force=False):
"""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`]
:param force: Force binding of providers.
:type force: bool
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: None
"""
for name, provider in six.iteritems(providers):
self.bind_provider(name, provider, force)
def has_provider(self, 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.
: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.
: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.
: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)
return super(DynamicCatalog, self).__setattr__(name, value)
def __delattr__(self, 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.
:param name: Attribute's name.
:type name: str
:rtype: None
"""
self.unbind_provider(name)
def __repr__(self):
"""Return Python representation of catalog.
:rtype: str
"""
return '<{0}({1})>'.format(self.name,
', '.join(six.iterkeys(self.providers)))
__str__ = __repr__

View File

@ -1,62 +0,0 @@
"""Dependency injector catalog utils."""
import six
from dependency_injector.utils import _copy_providers
from dependency_injector.errors import UndefinedProviderError
def copy(catalog):
""":py:class:`DeclarativeCatalog` copying decorator.
This decorator copy all providers from provided catalog to decorated one.
If one of the decorated catalog providers matches to source catalog
providers by name, it would be replaced by reference.
:param catalog: Catalog that should be copied by decorated catalog.
:type catalog: :py:class:`DeclarativeCatalog`
:return: Declarative catalog's copying decorator.
:rtype:
callable(:py:class:`DeclarativeCatalog`)
"""
def decorator(copied_catalog):
"""Copying decorator.
:param copied_catalog: Decorated catalog.
:type copied_catalog: :py:class:`DeclarativeCatalog`
:return: Decorated catalog.
:rtype:
:py:class:`DeclarativeCatalog`
"""
memo = dict()
for name, provider in six.iteritems(copied_catalog.cls_providers):
try:
source_provider = catalog.get_provider(name)
except UndefinedProviderError:
pass
else:
memo[id(source_provider)] = provider
copied_catalog.bind_providers(_copy_providers(catalog.providers, memo),
force=True)
return copied_catalog
return decorator
def override(catalog):
""":py:class:`DeclarativeCatalog` overriding decorator.
:param catalog: Catalog that should be overridden by decorated catalog.
:type catalog: :py:class:`DeclarativeCatalog`
:return: Declarative catalog's overriding decorator.
:rtype: callable(:py:class:`DeclarativeCatalog`)
"""
def decorator(overriding_catalog):
"""Overriding decorator."""
catalog.override(overriding_catalog)
return overriding_catalog
return decorator

View File

@ -0,0 +1,363 @@
"""Dependency injector IoC containers module."""
import six
from dependency_injector import (
providers,
utils,
errors,
)
class DynamicContainer(object):
"""Dynamic inversion of control container.
.. code-block:: python
services = DynamicContainer()
services.auth = providers.Factory(AuthService)
services.users = providers.Factory(UsersService,
auth_service=services.auth)
.. py:attribute:: providers
Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
.. py:attribute:: overridden
Tuple of overriding containers.
:type: tuple[:py:class:`DynamicContainer`]
.. py:attribute:: provider_type
Type of providers that could be placed in container.
:type: type
"""
__IS_CONTAINER__ = True
def __init__(self):
"""Initializer.
:rtype: None
"""
self.provider_type = providers.Provider
self.providers = dict()
self.overridden = tuple()
super(DynamicContainer, self).__init__()
def __setattr__(self, name, value):
"""Set instance attribute.
If value of attribute is provider, it will be added into providers
dictionary.
:param name: Attribute's name
:type name: str
:param value: Attribute's value
:type value: object
:rtype: None
"""
if utils.is_provider(value):
_check_provider_type(self, value)
self.providers[name] = value
super(DynamicContainer, self).__setattr__(name, value)
def __delattr__(self, name):
"""Delete instance attribute.
If value of attribute is provider, it will be deleted from providers
dictionary.
:param name: Attribute's name
:type name: str
:rtype: None
"""
if name in self.providers:
del self.providers[name]
super(DynamicContainer, self).__delattr__(name)
def override(self, overriding):
"""Override current container by overriding container.
:param overriding: Overriding container.
:type overriding: :py:class:`DynamicContainer`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
override container by itself
:rtype: None
"""
if overriding is self:
raise errors.Error('Container {0} could not be overridden '
'with itself'.format(self))
self.overridden += (overriding,)
for name, provider in six.iteritems(overriding.providers):
try:
getattr(self, name).override(provider)
except AttributeError:
pass
def reset_last_overriding(self):
"""Reset last overriding provider for each container providers.
:rtype: None
"""
if not self.overridden:
raise errors.Error('Container {0} is not overridden'.format(self))
self.overridden = self.overridden[:-1]
for provider in six.itervalues(self.providers):
provider.reset_last_overriding()
def reset_override(self):
"""Reset all overridings for each container providers.
:rtype: None
"""
self.overridden = tuple()
for provider in six.itervalues(self.providers):
provider.reset_override()
class DeclarativeContainerMetaClass(type):
"""Declarative inversion of control container meta class."""
def __new__(mcs, class_name, bases, attributes):
"""Declarative container class factory."""
cls_providers = tuple((name, provider)
for name, provider in six.iteritems(attributes)
if utils.is_provider(provider))
inherited_providers = tuple((name, provider)
for base in bases if utils.is_container(
base) and base is not DynamicContainer
for name, provider in six.iteritems(
base.cls_providers))
attributes['cls_providers'] = dict(cls_providers)
attributes['inherited_providers'] = dict(inherited_providers)
attributes['providers'] = dict(cls_providers + inherited_providers)
cls = type.__new__(mcs, class_name, bases, attributes)
for provider in six.itervalues(cls.providers):
_check_provider_type(cls, provider)
return cls
def __setattr__(cls, name, value):
"""Set class attribute.
If value of attribute is provider, it will be added into providers
dictionary.
:param name: Attribute's name
:type name: str
:param value: Attribute's value
:type value: object
:rtype: None
"""
if utils.is_provider(value):
_check_provider_type(cls, value)
cls.providers[name] = value
cls.cls_providers[name] = value
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
def __delattr__(cls, name):
"""Delete class attribute.
If value of attribute is provider, it will be deleted from providers
dictionary.
:param name: Attribute's name
:type name: str
:rtype: None
"""
if name in cls.providers and name in cls.cls_providers:
del cls.providers[name]
del cls.cls_providers[name]
super(DeclarativeContainerMetaClass, cls).__delattr__(name)
@six.add_metaclass(DeclarativeContainerMetaClass)
class DeclarativeContainer(object):
"""Declarative inversion of control container.
.. code-block:: python
class Services(DeclarativeContainer):
auth = providers.Factory(AuthService)
users = providers.Factory(UsersService,
auth_service=auth)
"""
__IS_CONTAINER__ = True
provider_type = providers.Provider
"""Type of providers that could be placed in container.
:type: type
"""
instance_type = DynamicContainer
"""Type of container that is returned on instantiating declarative
container.
:type: type
"""
providers = dict()
"""Read-only dictionary of all providers.
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
"""
cls_providers = dict()
"""Read-only dictionary of current container 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`]
"""
overridden = tuple()
"""Tuple of overriding containers.
:type: tuple[:py:class:`DeclarativeContainer`]
"""
def __new__(cls, *args, **kwargs):
"""Constructor.
:return: Dynamic container with copy of all providers.
:rtype: :py:class:`DynamicContainer`
"""
container = cls.instance_type(*args, **kwargs)
container.provider_type = cls.provider_type
for name, provider in six.iteritems(utils.deepcopy(cls.providers)):
setattr(container, name, provider)
return container
@classmethod
def override(cls, overriding):
"""Override current container by overriding container.
:param overriding: Overriding container.
:type overriding: :py:class:`DeclarativeContainer`
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
override container by itself or its subclasses
:rtype: None
"""
if issubclass(cls, overriding):
raise errors.Error('Container {0} could not be overridden '
'with itself or its subclasses'.format(cls))
cls.overridden += (overriding,)
for name, provider in six.iteritems(overriding.cls_providers):
try:
getattr(cls, name).override(provider)
except AttributeError:
pass
@classmethod
def reset_last_overriding(cls):
"""Reset last overriding provider for each container providers.
:rtype: None
"""
if not cls.overridden:
raise errors.Error('Container {0} is not overridden'.format(cls))
cls.overridden = cls.overridden[:-1]
for provider in six.itervalues(cls.providers):
provider.reset_last_overriding()
@classmethod
def reset_override(cls):
"""Reset all overridings for each container providers.
:rtype: None
"""
cls.overridden = tuple()
for provider in six.itervalues(cls.providers):
provider.reset_override()
def override(container):
""":py:class:`DeclarativeContainer` overriding decorator.
:param container: Container that should be overridden by decorated
container.
:type container: :py:class:`DeclarativeContainer`
:return: Declarative container's overriding decorator.
:rtype: callable(:py:class:`DeclarativeContainer`)
"""
def _decorator(overriding_container):
"""Overriding decorator."""
container.override(overriding_container)
return overriding_container
return _decorator
def copy(container):
""":py:class:`DeclarativeContainer` copying decorator.
This decorator copy all providers from provided container to decorated one.
If one of the decorated container providers matches to source container
providers by name, it would be replaced by reference.
:param container: Container that should be copied by decorated container.
:type container: :py:class:`DeclarativeContainer`
:return: Declarative container's copying decorator.
:rtype: callable(:py:class:`DeclarativeContainer`)
"""
def _decorator(copied_container):
memo = dict()
for name, provider in six.iteritems(copied_container.cls_providers):
try:
source_provider = getattr(container, name)
except AttributeError:
pass
else:
memo[id(source_provider)] = provider
providers_copy = utils.deepcopy(container.providers, memo)
for name, provider in six.iteritems(providers_copy):
setattr(copied_container, name, provider)
return copied_container
return _decorator
def _check_provider_type(cls, provider):
if not isinstance(provider, cls.provider_type):
raise errors.Error('{0} can contain only {1} '
'instances'.format(cls, cls.provider_type))

View File

@ -1,4 +1,4 @@
"""Errors module."""
"""Dependency injector errors module."""
class Error(Exception):
@ -6,17 +6,3 @@ class Error(Exception):
All dependency injector errors extend this error class.
"""
class UndefinedProviderError(Error, AttributeError):
"""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

@ -1,169 +1,13 @@
"""Injections module."""
"""Dependency injector injections module."""
import six
from dependency_injector.utils import (
is_provider,
is_delegated_provider,
is_injection,
is_arg_injection,
is_kwarg_injection,
fetch_cls_init,
from dependency_injector.providers.base import (
_parse_positional_injections,
_parse_keyword_injections,
)
from dependency_injector.errors import Error
@six.python_2_unicode_compatible
class Injection(object):
"""Base injection class.
All injections extend this class.
.. py:attribute:: injectable
Injectable value, could be provider or any other object.
:type: object | :py:class:`dependency_injector.providers.Provider`
.. py:attribute:: call_injectable
Flag that is set to ``True`` if it is needed to call injectable.
Injectable needs to be called if it is not delegated provider.
:type: bool
"""
__IS_INJECTION__ = True
__slots__ = ('injectable', 'call_injectable')
def __init__(self, injectable):
"""Initializer.
:param injectable: Injectable value, could be provider or any
other object.
:type injectable: object |
:py:class:`dependency_injector.providers.Provider`
"""
self.injectable = injectable
self.call_injectable = (is_provider(injectable) and
not is_delegated_provider(injectable))
super(Injection, self).__init__()
@property
def value(self):
"""Read-only property that represents injectable value.
Injectable values and delegated providers are provided "as is".
Other providers will be called every time, when injection needs to
be done.
:rtype: object
"""
if self.call_injectable:
return self.injectable.provide()
return self.injectable
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return '<{injection}({injectable}) at {address}>'.format(
injection='.'.join((self.__class__.__module__,
self.__class__.__name__)),
injectable=repr(self.injectable),
address=hex(id(self)))
__repr__ = __str__
class Arg(Injection):
"""Positional argument injection."""
__IS_ARG_INJECTION__ = True
@six.python_2_unicode_compatible
class _NamedInjection(Injection):
"""Base class of named injections.
.. py:attribute:: name
Injection target's name (keyword argument, attribute, method).
:type: str
"""
__slots__ = ('name',)
def __init__(self, name, injectable):
"""Initializer.
:param name: Injection target's name.
:type name: str
:param injectable: Injectable value, could be provider or any
other object.
:type injectable: object |
:py:class:`dependency_injector.providers.Provider`
"""
self.name = name
super(_NamedInjection, self).__init__(injectable)
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return '<{injection}({name}, {injectable}) at {address}>'.format(
name=repr(self.name),
injection='.'.join((self.__class__.__module__,
self.__class__.__name__)),
injectable=repr(self.injectable),
address=hex(id(self)))
__repr__ = __str__
class KwArg(_NamedInjection):
"""Keyword argument injection.
.. py:attribute:: name
Keyword argument's name.
:type: str
"""
__IS_KWARG_INJECTION__ = True
class Attribute(_NamedInjection):
"""Attribute injection.
.. py:attribute:: name
Attribute's name.
:type: str
"""
__IS_ATTRIBUTE_INJECTION__ = True
class Method(_NamedInjection):
"""Method injection.
.. py:attribute:: name
Method's name.
:type: str
"""
__IS_METHOD_INJECTION__ = True
from dependency_injector import utils
from dependency_injector import errors
def inject(*args, **kwargs):
@ -178,24 +22,18 @@ def inject(*args, **kwargs):
.. code-block:: python
# Positional arguments injections (simplified syntax):
# Positional arguments injections:
@inject(1, 2)
def some_function(arg1, arg2):
pass
# Keyword arguments injections (simplified syntax):
# Keyword arguments injections:
@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):
# Keyword arguments injections into class init:
@inject(arg1=1)
@inject(arg2=2)
class SomeClass(object):
@ -212,16 +50,16 @@ def inject(*args, **kwargs):
:return: Class / callable decorator
:rtype: (callable) -> (type | callable)
"""
arg_injections = _parse_args_injections(args)
kwarg_injections = _parse_kwargs_injections(args, kwargs)
arg_injections = _parse_positional_injections(args)
kwarg_injections = _parse_keyword_injections(kwargs)
def decorator(callback_or_cls):
"""Dependency injection decorator."""
if isinstance(callback_or_cls, six.class_types):
cls = callback_or_cls
cls_init = fetch_cls_init(cls)
cls_init = utils.fetch_cls_init(cls)
if not cls_init:
raise Error(
raise errors.Error(
'Class {0}.{1} has no __init__() '.format(cls.__module__,
cls.__name__) +
'method and could not be decorated with @inject decorator')
@ -232,19 +70,19 @@ def inject(*args, **kwargs):
if hasattr(callback, '__INJECT_DECORATED__'):
callback.args += arg_injections
callback.kwargs += kwarg_injections
callback.injections += arg_injections + kwarg_injections
callback.kwargs.update(kwarg_injections)
return callback
@six.wraps(callback)
def decorated(*args, **kwargs):
"""Decorated with dependency injection callback."""
if decorated.args:
args = tuple(arg.value for arg in decorated.args) + args
args = tuple(arg.provide_injection()
for arg in decorated.args) + args
for kwarg in decorated.kwargs:
if kwarg.name not in kwargs:
kwargs[kwarg.name] = kwarg.value
for name, arg in six.iteritems(decorated.kwargs):
if name not in kwargs:
kwargs[name] = arg.provide_injection()
return callback(*args, **kwargs)
@ -252,25 +90,6 @@ def inject(*args, **kwargs):
decorated.origin = callback
decorated.args = arg_injections
decorated.kwargs = kwarg_injections
decorated.injections = arg_injections + kwarg_injections
return decorated
return decorator
def _parse_args_injections(args):
"""Parse positional argument injections according to current syntax."""
return tuple(Arg(arg) if not is_injection(arg) else arg
for arg in args
if not is_injection(arg) or is_arg_injection(arg))
def _parse_kwargs_injections(args, kwargs):
"""Parse keyword argument injections according to current syntax."""
kwarg_injections = tuple(injection
for injection in args
if is_kwarg_injection(injection))
if kwargs:
kwarg_injections += tuple(KwArg(name, value)
for name, value in six.iteritems(kwargs))
return kwarg_injections

View File

@ -3,9 +3,10 @@
from dependency_injector.providers.base import (
Provider,
Delegate,
Static,
StaticProvider,
Object,
ExternalDependency,
OverridingContext,
override,
)
from dependency_injector.providers.callable import (
Callable,
@ -17,28 +18,17 @@ from dependency_injector.providers.creational import (
Singleton,
DelegatedSingleton,
)
from dependency_injector.providers.static import (
Object,
Value,
Class,
Function,
)
from dependency_injector.providers.config import (
Config,
ChildConfig,
)
from dependency_injector.providers.utils import (
OverridingContext,
override,
)
__all__ = (
'Provider',
'Delegate',
'Static', 'StaticProvider',
'Object',
'ExternalDependency',
'OverridingContext',
'override',
'Callable',
'DelegatedCallable',
@ -46,15 +36,4 @@ __all__ = (
'DelegatedFactory',
'Singleton',
'DelegatedSingleton',
'Object',
'Value',
'Class',
'Function',
'Config',
'ChildConfig',
'OverridingContext',
'override',
)

View File

@ -2,7 +2,6 @@
import six
from dependency_injector.providers.utils import OverridingContext
from dependency_injector.errors import Error
from dependency_injector.utils import (
is_provider,
@ -52,7 +51,7 @@ class Provider(object):
All providers should extend this class.
.. py:attribute:: overridden_by
.. py:attribute:: overridden
Tuple of overriding providers, if any.
@ -61,11 +60,11 @@ class Provider(object):
__IS_PROVIDER__ = True
__OPTIMIZED_CALLS__ = True
__slots__ = ('overridden_by', 'provide', '__call__')
__slots__ = ('overridden', 'provide', '__call__')
def __init__(self):
"""Initializer."""
self.overridden_by = None
self.overridden = tuple()
super(Provider, self).__init__()
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
@ -82,23 +81,16 @@ class Provider(object):
def _call_last_overriding(self, *args, **kwargs):
"""Call last overriding provider and return result."""
return self.last_overriding(*args, **kwargs)
return (self.overridden[-1](*args, **kwargs)
if self.overridden
else None)
@property
def is_overridden(self):
"""Read-only property that is set to ``True`` if provider is overridden.
def provide_injection(self):
"""Injection strategy implementation.
:rtype: bool
:rtype: object
"""
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
return self.provide()
def override(self, provider):
"""Override provider with another provider.
@ -116,12 +108,9 @@ class Provider(object):
'with itself'.format(self))
if not is_provider(provider):
provider = Static(provider)
provider = Object(provider)
if not self.is_overridden:
self.overridden_by = (ensure_is_provider(provider),)
else:
self.overridden_by += (ensure_is_provider(provider),)
self.overridden += (ensure_is_provider(provider),)
# Disable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
@ -137,11 +126,12 @@ class Provider(object):
:rtype: None
"""
if not self.overridden_by:
if not self.overridden:
raise Error('Provider {0} is not overridden'.format(str(self)))
self.overridden_by = self.overridden_by[:-1]
if not self.is_overridden:
self.overridden = self.overridden[:-1]
if not self.overridden:
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._provide
@ -151,7 +141,7 @@ class Provider(object):
:rtype: None
"""
self.overridden_by = None
self.overridden = tuple()
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
@ -229,11 +219,8 @@ class Delegate(Provider):
@six.python_2_unicode_compatible
class Static(Provider):
""":py:class:`Static` provider returns provided instance "as is".
:py:class:`Static` provider is base implementation that provides exactly
the same as it got on input.
class Object(Provider):
""":py:class:`Object` provider returns provided instance "as is".
.. py:attribute:: provides
@ -251,7 +238,7 @@ class Static(Provider):
:type provides: object
"""
self.provides = provides
super(Static, self).__init__()
super(Object, self).__init__()
def _provide(self, *args, **kwargs):
"""Return provided instance.
@ -276,10 +263,6 @@ class Static(Provider):
__repr__ = __str__
StaticProvider = Static
# Backward compatibility for versions < 1.11.1
@six.python_2_unicode_compatible
class ExternalDependency(Provider):
""":py:class:`ExternalDependency` provider describes dependency interface.
@ -312,6 +295,7 @@ class ExternalDependency(Provider):
raise Error('ExternalDependency provider expects to get class, ' +
'got {0} instead'.format(str(instance_of)))
self.instance_of = instance_of
self.provide = self.__call__
super(ExternalDependency, self).__init__()
def __call__(self, *args, **kwargs):
@ -327,10 +311,10 @@ class ExternalDependency(Provider):
:rtype: object
"""
if not self.is_overridden:
if not self.overridden:
raise Error('Dependency is not defined')
instance = self.last_overriding(*args, **kwargs)
instance = self._call_last_overriding(*args, **kwargs)
if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) +
@ -356,3 +340,77 @@ class ExternalDependency(Provider):
return represent_provider(provider=self, provides=self.instance_of)
__repr__ = __str__
class OverridingContext(object):
"""Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
implemeting ``with`` contexts. When :py:class:`OverridingContext` is
closed, overriding that was created in this context is dropped also.
.. code-block:: python
with provider.override(another_provider):
assert provider.overridden
assert not provider.overridden
"""
def __init__(self, overridden, overriding):
"""Initializer.
:param overridden: Overridden provider.
:type overridden: :py:class:`Provider`
:param overriding: Overriding provider.
:type overriding: :py:class:`Provider`
"""
self.overridden = overridden
self.overriding = overriding
def __enter__(self):
"""Do nothing."""
return self.overriding
def __exit__(self, *_):
"""Exit overriding context."""
self.overridden.reset_last_overriding()
def override(overridden):
"""Decorator for overriding providers.
This decorator overrides ``overridden`` provider by decorated one.
.. code-block:: python
@Factory
class SomeClass(object):
pass
@override(SomeClass)
@Factory
class ExtendedSomeClass(SomeClass.cls):
pass
:param overridden: Provider that should be overridden.
:type overridden: :py:class:`Provider`
:return: Overriding provider.
:rtype: :py:class:`Provider`
"""
def decorator(overriding):
overridden.override(overriding)
return overriding
return decorator
def _parse_positional_injections(args):
return tuple(arg if is_provider(arg) else Object(arg)
for arg in args)
def _parse_keyword_injections(kwargs):
return dict((name, arg if is_provider(arg) else Object(arg))
for name, arg in six.iteritems(kwargs))

View File

@ -2,10 +2,10 @@
import six
from dependency_injector.providers.base import Provider
from dependency_injector.injections import (
_parse_args_injections,
_parse_kwargs_injections,
from dependency_injector.providers.base import (
Provider,
_parse_positional_injections,
_parse_keyword_injections,
)
from dependency_injector.utils import represent_provider
from dependency_injector.errors import Error
@ -13,45 +13,27 @@ from dependency_injector.errors import Error
@six.python_2_unicode_compatible
class Callable(Provider):
""":py:class:`Callable` provider calls wrapped callable on every call.
r""":py:class:`Callable` provider calls wrapped callable on every call.
: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:
:py:class:`Callable` supports positional and keyword argument injections:
.. code-block:: python
# simplified syntax for passing positional and keyword argument
# injections:
some_function = Callable(some_function, 'arg1', 'arg2', arg3=3, arg4=4)
# extended (full) syntax for passing positional and keyword argument
# injections:
some_function = Callable(some_function,
injections.Arg(1),
injections.Arg(2),
injections.KwArg('some_arg', 3),
injections.KwArg('other_arg', 4))
'positional_arg1', 'positional_arg2',
keyword_argument1=3, keyword_argument=4)
.. py:attribute:: provides
# or
Provided callable.
some_function = Callable(some_function) \
.add_args('positional_arg1', 'positional_arg2') \
.add_kwargs(keyword_argument1=3, keyword_argument=4)
:type: callable
# or
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
some_function = Callable(some_function)
some_function.add_args('positional_arg1', 'positional_arg2')
some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
"""
__slots__ = ('provides', 'args', 'kwargs')
@ -61,12 +43,6 @@ class Callable(Provider):
:param provides: Wrapped callable.
:type provides: callable
:param args: Tuple of injections.
:type args: tuple
:param kwargs: Dictionary of injections.
:type kwargs: dict
"""
if not callable(provides):
raise Error('Provider {0} expected to get callable, '
@ -77,31 +53,34 @@ class Callable(Provider):
self.provides = provides
self.args = tuple()
self.kwargs = tuple()
self.kwargs = dict()
self.add_injections(*args, **kwargs)
self.add_args(*args)
self.add_kwargs(**kwargs)
super(Callable, 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
def add_injections(self, *args, **kwargs):
"""Add provider injections.
def add_args(self, *args):
"""Add postional argument injections.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.args += _parse_positional_injections(args)
return self
def add_kwargs(self, **kwargs):
"""Add keyword argument injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.args += _parse_args_injections(args)
self.kwargs += _parse_kwargs_injections(args, kwargs)
self.kwargs.update(_parse_keyword_injections(kwargs))
return self
def _provide(self, *args, **kwargs):
"""Return provided instance.
@ -115,11 +94,11 @@ class Callable(Provider):
:rtype: object
"""
if self.args:
args = tuple(arg.value for arg in self.args) + args
args = tuple(arg.provide_injection() for arg in self.args) + args
for kwarg in self.kwargs:
if kwarg.name not in kwargs:
kwargs[kwarg.name] = kwarg.value
for name, arg in six.iteritems(self.kwargs):
if name not in kwargs:
kwargs[name] = arg.provide_injection()
return self.provides(*args, **kwargs)
@ -138,24 +117,11 @@ class DelegatedCallable(Callable):
:py:class:`DelegatedCallable` is a :py:class:`Callable`, that is injected
"as is".
.. py:attribute:: provides
Provided callable.
:type: callable
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
"""
__IS_DELEGATED__ = True
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self

View File

@ -1,138 +0,0 @@
"""Dependency injector config providers."""
import six
from dependency_injector.providers.base import Provider
from dependency_injector.utils import represent_provider
from dependency_injector.errors import Error
@six.python_2_unicode_compatible
class Config(Provider):
""":py:class:`Config` provider provide dict values.
: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.
: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.
: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.
: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:
try:
value = value[path]
except KeyError:
raise Error('Config key '
'"{0}" is undefined'.format('.'.join(paths)))
return value
def update_from(self, value):
"""Update current value from another one.
:param value: Configuration dictionary.
:type value: dict[str, object]
:rtype: None
"""
self.value.update(value)
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.value)
__repr__ = __str__
@six.python_2_unicode_compatible
class ChildConfig(Provider):
""":py:class:`ChildConfig` provider provides value from :py:class:`Config`.
: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.
:param parents: Tuple of pieces of configuration option / section
parent path.
:type parents: tuple[str]
:param root_config: Root configuration object.
:type root_config: :py:class:`Config`
"""
self.parents = parents
self.root_config = root_config
super(ChildConfig, self).__init__()
def __getattr__(self, item):
"""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.
: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)
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self,
provides='.'.join(self.parents))
__repr__ = __str__

View File

@ -1,41 +1,53 @@
"""Dependency injector creational providers."""
import six
from dependency_injector.providers.callable import Callable
from dependency_injector.utils import (
is_attribute_injection,
is_method_injection,
GLOBAL_LOCK,
)
from dependency_injector.providers.base import _parse_keyword_injections
from dependency_injector.utils import GLOBAL_LOCK
from dependency_injector.errors import Error
class Factory(Callable):
""":py:class:`Factory` provider creates new instance on every call.
r""":py:class:`Factory` provider creates new instance on every call.
:py:class:`Factory` supports different syntaxes of passing injections:
:py:class:`Factory` supports positional & keyword argument injections,
as well as attribute injections.
Positional and keyword argument injections could be defined like this:
.. 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))
'positional_arg1', 'positional_arg2',
keyword_argument1=3, keyword_argument=4)
# or
factory = Factory(SomeClass) \
.add_args('positional_arg1', 'positional_arg2') \
.add_kwargs(keyword_argument1=3, keyword_argument=4)
# or
factory = Factory(SomeClass)
factory.add_args('positional_arg1', 'positional_arg2')
factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
Attribute injections are defined by using :py:meth:`Factory.attributes`:
.. code-block:: python
factory = Factory(SomeClass) \
.add_attributes(attribute1=1, attribute2=2)
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)
factory = Factory(SomeClass)
some_object = factory()
.. py:attribute:: provided_type
@ -46,47 +58,17 @@ class Factory(Callable):
:type: type | None
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
provided_type = None
__slots__ = ('cls', 'attributes', 'methods')
__slots__ = ('cls', 'attributes')
def __init__(self, provides, *args, **kwargs):
"""Initializer.
@ -94,51 +76,28 @@ class Factory(Callable):
: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 (self.__class__.provided_type and
not issubclass(provides, self.__class__.provided_type)):
raise Error('{0} can provide only {1} instances'.format(
self.__class__, self.__class__.provided_type))
self.attributes = tuple()
self.methods = tuple()
self.attributes = dict()
super(Factory, self).__init__(provides, *args, **kwargs)
self.cls = self.provides
@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 add_injections(self, *args, **kwargs):
"""Add provider injections.
:param args: Tuple of injections.
:type args: tuple
def add_attributes(self, **kwargs):
"""Add attribute injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.attributes += tuple(injection
for injection in args
if is_attribute_injection(injection))
self.methods += tuple(injection
for injection in args
if is_method_injection(injection))
super(Factory, self).add_injections(*args, **kwargs)
self.attributes.update(_parse_keyword_injections(kwargs))
return self
def _provide(self, *args, **kwargs):
"""Return provided instance.
@ -151,19 +110,10 @@ class Factory(Callable):
:rtype: object
"""
if self.args:
args = tuple(arg.value for arg in self.args) + args
instance = super(Factory, self)._provide(*args, **kwargs)
for kwarg in self.kwargs:
if kwarg.name not in kwargs:
kwargs[kwarg.name] = kwarg.value
instance = self.provides(*args, **kwargs)
for attribute in self.attributes:
setattr(instance, attribute.name, attribute.value)
for method in self.methods:
getattr(instance, method.name)(method.value)
for name, arg in six.iteritems(self.attributes):
setattr(instance, name, arg.provide_injection())
return instance
@ -182,45 +132,20 @@ class DelegatedFactory(Factory):
:type: type | None
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__IS_DELEGATED__ = True
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self
class Singleton(Factory):
@ -238,9 +163,7 @@ class Singleton(Factory):
.. code-block:: python
singleton = Singleton(SomeClass,
some_arg1=1,
some_arg2=2)
singleton = Singleton(SomeClass)
some_object = singleton()
.. py:attribute:: provided_type
@ -251,48 +174,12 @@ class Singleton(Factory):
:type: type | None
.. py:attribute:: instance
Read-only reference to singleton's instance.
:type: object
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__slots__ = ('instance',)
@ -303,12 +190,6 @@ class Singleton(Factory):
: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
super(Singleton, self).__init__(provides, *args, **kwargs)
@ -331,12 +212,10 @@ class Singleton(Factory):
:rtype: object
"""
if self.instance:
return self.instance
with GLOBAL_LOCK:
self.instance = super(Singleton, self)._provide(*args, **kwargs)
if self.instance is None:
self.instance = super(Singleton, self)._provide(*args,
**kwargs)
return self.instance
@ -354,48 +233,17 @@ class DelegatedSingleton(Singleton):
:type: type | None
.. py:attribute:: instance
Read-only reference to singleton's instance.
:type: object
.. py:attribute:: provides
Class or other callable that provides object.
:type: type | callable
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
.. py:attribute:: args
Tuple of positional argument injections.
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
.. py:attribute:: kwargs
Tuple of keyword argument injections.
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
.. py:attribute:: attributes
Tuple of attribute injections.
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
.. py:attribute:: methods
Tuple of method injections.
:type: tuple[:py:class:`dependency_injector.injections.Method`]
"""
__IS_DELEGATED__ = True
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self

View File

@ -1,43 +0,0 @@
"""Dependency injector static providers."""
from dependency_injector.providers.base import Static
class Class(Static):
""":py:class:`Class` returns provided class "as is".
.. code-block:: python
cls_provider = Class(object)
object_cls = cls_provider()
"""
class Object(Static):
""":py:class:`Object` returns provided object "as is".
.. code-block:: python
object_provider = Object(object())
object_instance = object_provider()
"""
class Function(Static):
""":py:class:`Function` returns provided function "as is".
.. code-block:: python
function_provider = Function(len)
len_function = function_provider()
"""
class Value(Static):
""":py:class:`Value` returns provided value "as is".
.. code-block:: python
value_provider = Value(31337)
value = value_provider()
"""

View File

@ -1,65 +0,0 @@
"""Dependency injector provider utils."""
class OverridingContext(object):
"""Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
implemeting ``with`` contexts. When :py:class:`OverridingContext` is
closed, overriding that was created in this context is dropped also.
.. code-block:: python
with provider.override(another_provider):
assert provider.is_overridden
assert not provider.is_overridden
"""
def __init__(self, overridden, overriding):
"""Initializer.
:param overridden: Overridden provider.
:type overridden: :py:class:`Provider`
:param overriding: Overriding provider.
:type overriding: :py:class:`Provider`
"""
self.overridden = overridden
self.overriding = overriding
def __enter__(self):
"""Do nothing."""
return self.overriding
def __exit__(self, *_):
"""Exit overriding context."""
self.overridden.reset_last_overriding()
def override(overridden):
"""Decorator for overriding providers.
This decorator overrides ``overridden`` provider by decorated one.
.. code-block:: python
@Factory
class SomeClass(object):
pass
@override(SomeClass)
@Factory
class ExtendedSomeClass(SomeClass.cls):
pass
:param overridden: Provider that should be overridden.
:type overridden: :py:class:`Provider`
:return: Overriding provider.
:rtype: :py:class:`Provider`
"""
def decorator(overriding):
overridden.override(overriding)
return overriding
return decorator

View File

@ -1,13 +1,13 @@
"""Utils module."""
"""Dependency injector utils module."""
import sys
import copy
import copy as _copy
import types
import threading
import six
from dependency_injector.errors import Error
from dependency_injector import errors
GLOBAL_LOCK = threading.RLock()
@ -23,9 +23,9 @@ else: # pragma: no cover
_OBJECT_INIT = None
if six.PY2: # pragma: no cover
copy._deepcopy_dispatch[types.MethodType] = \
_copy._deepcopy_dispatch[types.MethodType] = \
lambda obj, memo: type(obj)(obj.im_func,
copy.deepcopy(obj.im_self, memo),
_copy.deepcopy(obj.im_self, memo),
obj.im_class)
@ -54,168 +54,21 @@ def ensure_is_provider(instance):
:rtype: :py:class:`dependency_injector.providers.Provider`
"""
if not is_provider(instance):
raise Error('Expected provider instance, '
raise errors.Error('Expected provider instance, '
'got {0}'.format(str(instance)))
return instance
def is_delegated_provider(instance):
"""Check if instance is delegated provider instance.
def is_container(instance):
"""Check if instance is container instance.
:param instance: Instance to be checked.
:type instance: object
:rtype: bool
"""
return (is_provider(instance) and
hasattr(instance, '__IS_DELEGATED__') and
getattr(instance, '__IS_DELEGATED__') is True)
def 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 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)))
return instance
def is_arg_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.
: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.
: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.
: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.
: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.
: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.
: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.
: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)
def ensure_is_catalog_bundle(instance):
"""Check if instance is catalog bundle 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 catalog bundle.
:rtype: :py:class:`dependency_injector.catalogs.CatalogBundle`
"""
if not is_catalog_bundle(instance):
raise Error('Expected catalog bundle instance, '
'got {0}'.format(str(instance)))
return instance
return (hasattr(instance, '__IS_CONTAINER__') and
getattr(instance, '__IS_CONTAINER__', False) is True)
def represent_provider(provider, provides):
@ -255,6 +108,6 @@ def fetch_cls_init(cls):
return cls_init
def _copy_providers(providers, memo=None):
"""Make full copy of providers dictionary."""
return copy.deepcopy(providers, memo)
def deepcopy(instance, memo=None):
"""Make full copy of instance."""
return _copy.deepcopy(instance, memo)

View File

@ -1,6 +0,0 @@
``dependency_injector.catalogs``
--------------------------------
.. automodule:: dependency_injector.catalogs
:members:
:special-members:

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

@ -0,0 +1,6 @@
``dependency_injector.containers``
--------------------------------
.. automodule:: dependency_injector.containers
:members:
:special-members:

View File

@ -6,7 +6,7 @@ API Documentation
top_level
providers
containers
injections
catalogs
utils
errors

View File

@ -4,3 +4,4 @@
.. automodule:: dependency_injector.injections
:members:
:inherited-members:
:show-inheritance:

View File

@ -1,6 +1,10 @@
``dependency_injector.providers``
---------------------------------
.. image:: /images/providers/providers_class_diagram.png
:width: 100%
:align: center
.. automodule:: dependency_injector.providers
:members:
:inherited-members:

View File

@ -1,63 +0,0 @@
Catalog provider bundles
------------------------
.. currentmodule:: dependency_injector.catalogs
: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.
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
:width: 100%
:align: center
Listing of `services.py`:
.. literalinclude:: ../../examples/catalogs/bundles/services.py
:language: python
:linenos:
Listing of `views.py`:
.. literalinclude:: ../../examples/catalogs/bundles/views.py
:language: python
:linenos:
Listing of `catalogs.py`:
.. literalinclude:: ../../examples/catalogs/bundles/catalogs.py
:language: python
:linenos:

View File

@ -1,64 +0,0 @@
Declarative catalogs
--------------------
.. currentmodule:: 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
:linenos:
Example of declarative catalogs inheritance:
.. image:: /images/catalogs/declarative_inheritance.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/catalogs/declarative_inheritance.py
:language: python
:linenos:
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
:linenos:

View File

@ -1,31 +0,0 @@
Dynamic catalogs
----------------
.. currentmodule:: 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
:linenos:
Next one example demonstrates creation and runtime filling of dynamic catalog:
.. literalinclude:: ../../examples/catalogs/dynamic_runtime_creation.py
:language: python
:linenos:

View File

@ -1,28 +0,0 @@
Catalogs
========
Catalogs are collections of providers. Main purpose of catalogs is to group
providers.
There are, actually, several popular cases of catalogs usage:
- Grouping of providers from the same architectural layer (for example,
``Services``, ``Models`` and ``Forms`` catalogs).
- Grouping of providers from the same functional groups (for example,
catalog ``Users``, that contains all functional parts of ``Users``
component).
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
declarative
dynamic
bundles
specialization
overriding

View File

@ -1,52 +0,0 @@
Overriding of catalogs
----------------------
.. currentmodule:: 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 :py:class:`DeclarativeCatalog` with another
catalog:
- Use :py:meth:`DeclarativeCatalog.override` method.
- Use :py:func:`override` class decorator.
Example of overriding catalog using :py:meth:`DeclarativeCatalog.override`
method:
.. literalinclude:: ../../examples/catalogs/override_declarative.py
:language: python
:linenos:
Example of overriding catalog using :py:func:`override` decorator:
.. literalinclude:: ../../examples/catalogs/override_declarative_decorator.py
:language: python
:linenos:
Also there are several useful :py:class:`DeclarativeCatalog` methods and
properties that help to work with catalog overridings:
- :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
:linenos:

View File

@ -1,45 +0,0 @@
Specialization of catalogs
--------------------------
.. currentmodule:: dependency_injector.catalogs
:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` could be
specialized for any kind of needs via declaring its subclasses.
One of such `builtin` features is a limitation to
:py:class:`DeclarativeCatalog` (and :py:class:`DynamicCatalog`) provider type.
Next example shows usage of this feature with :py:class:`DeclarativeCatalog`
in couple with feature of :py:class:`dependency_injector.providers.Factory`
for limitation of its provided type:
Listing of `services.py`:
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/services.py
:language: python
:linenos:
Listing of `catalog.py`:
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/catalog.py
:language: python
:linenos:
Limitation to provider type could be used with :py:class:`DynamicCatalog`
as well.
Next example does the same that previous one, but use
:py:class:`DynamicCatalog` instead of :py:class:`DeclarativeCatalog`:
Listing of `services.py`:
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/services.py
:language: python
:linenos:
Listing of `catalog.py`:
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/catalog.py
:language: python
:linenos:

View File

@ -0,0 +1,55 @@
Declarative containers
----------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` is inversion of control container that
could be defined in declarative manner. It should cover most of the cases
when list of providers that would be included in container is deterministic
(container will not change its structure in runtime).
Declarative containers have to extend base declarative container class -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Declarative container's providers have to be defined like container's class
attributes. Every provider in container has name. This name should follow
``some_provider`` convention, that is standard naming convention for
attribute names in Python.
.. note::
Declarative containers have several features that could be useful
for some kind of operations on container's providers, please visit API
documentation for getting full list of features -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Here is an simple example of defining declarative container with several
factories:
.. image:: /images/containers/declarative.png
:width: 85%
:align: center
.. literalinclude:: ../../examples/containers/declarative.py
:language: python
:linenos:
Example of declarative containers inheritance:
.. image:: /images/containers/declarative_inheritance.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/containers/declarative_inheritance.py
:language: python
:linenos:
Example of declarative containers's provider injections:
.. image:: /images/containers/declarative_injections.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/containers/declarative_injections.py
:language: python
:linenos:

View File

@ -0,0 +1,27 @@
Dynamic containers
------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DynamicContainer` is an inversion of control container with dynamic
structure. It should cover most of the cases when list of providers that
would be included in container is non-deterministic and depends on
application's flow or its configuration (container's structure could be
determined just after application will be started and will do some initial
work, like parsing list of container's providers from the configuration).
While :py:class:`DeclarativeContainer` acts on class-level,
:py:class:`DynamicContainer` does the same on instance-level.
Here is an simple example of defining dynamic container with several factories:
.. literalinclude:: ../../examples/containers/dynamic.py
:language: python
:linenos:
Next example demonstrates creation of dynamic container based on some
configuration:
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
:language: python
:linenos:

28
docs/containers/index.rst Normal file
View File

@ -0,0 +1,28 @@
IoC Containers
==============
Containers are collections of providers. Main purpose of containers is to group
providers.
There are, actually, several popular cases of containers usage:
+ Keeping all providers in a single container.
+ Grouping of providers from the same architectural layer (for example,
``Services``, ``Models`` and ``Forms`` containers).
+ Grouping of providers from the same functional groups (for example,
container ``Users``, that contains all functional parts of ``Users``
component).
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.
Containers module API docs - :py:mod:`dependency_injector.containers`.
.. toctree::
:maxdepth: 2
declarative
dynamic
specialization
overriding

View File

@ -0,0 +1,40 @@
Overriding of containers
------------------------
.. currentmodule:: dependency_injector.containers
Containers can be overridden by other containers. This, actually, means that
all of the providers from overriding container will override providers with
the same names in overridden container.
There are two ways to override :py:class:`DeclarativeContainer` with another
container:
- Use :py:meth:`DeclarativeContainer.override` method.
- Use :py:func:`override` class decorator.
Example of overriding container using :py:meth:`DeclarativeContainer.override`
method:
.. literalinclude:: ../../examples/containers/override_declarative.py
:language: python
:linenos:
Example of overriding container using :py:func:`override` decorator:
.. literalinclude:: ../../examples/containers/override_declarative_decorator.py
:language: python
:linenos:
Also there are several useful :py:class:`DeclarativeContainer` methods and
properties that help to work with container overridings:
- :py:attr:`DeclarativeContainer.overridden` - tuple of all overriding
containers.
- :py:meth:`DeclarativeContainer.reset_last_overriding()` - reset last
overriding provider for each container providers.
- :py:meth:`DeclarativeContainer.reset_override()` - reset all overridings
for each container providers.
:py:class:`DynamicContainer` has exactly the same functionality, except of
:py:func:`override` decorator.

View File

@ -0,0 +1,24 @@
Specialization of containers
----------------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` could be specialized for any kind of needs
via declaring its subclasses.
One of such `builtin` features is a limitation for providers type.
Next example shows usage of this feature with :py:class:`DeclarativeContainer`
in couple with feature of :py:class:`dependency_injector.providers.Factory`
for limitation of its provided type:
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
:language: python
:linenos:
Limitation for providers type could be used with :py:class:`DynamicContainer`
as well:
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py
:language: python
:linenos:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

View File

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -30,9 +30,6 @@ Status
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Latest Version |
| | .. image:: https://img.shields.io/pypi/dm/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Downloads |
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: License |
@ -61,7 +58,7 @@ Contents
introduction/index
main/installation
providers/index
catalogs/index
containers/index
advanced_usage/index
examples/index
api/index

View File

@ -119,9 +119,9 @@ Let's automate ``Engine`` into ``Car`` injections using *Dependency Injector*:
.. note::
``Components`` from previous example is an inversion of control container.
``Container`` from previous example is an inversion of control container.
It contains a collection of component providers that could be injected
into each other.
Assuming this, ``Components`` could be one and the only place, where
Assuming this, ``Container`` could be one and the only place, where
application's structure is being managed on the high level.

View File

@ -24,7 +24,7 @@ of Python objects and their dependencies in formal, pretty way.
*Dependency Injector* framework could be used in different application types:
+ Web applications based on Flask, Django or any other web framework.
+ Asyncronous applications based on asyncio, Tornado and Twisted.
+ Asynchronous applications based on asyncio, Tornado and Twisted.
+ Standalone frameworks and libraries.
+ GUI applications.

View File

@ -5,8 +5,8 @@ Structure of Dependency Injector
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes "Dependency Injector" framework
components and their interaction between each other.
Catalogs, providers and injections are the former
components of the framework.
Providers and containers are the former components of
the framework.
Current section describes *Dependency Injector* main entities and their
interaction between each other.
@ -15,37 +15,33 @@ interaction between each other.
:width: 100%
:align: center
There are 3 main entities: providers, injections and catalogs.
+ All of the entities could be used in composition with each other or
separatelly.
+ Each of the entities could be extended via specialization.
There are 2 main entities: providers & containers.
Providers
~~~~~~~~~
Providers are strategies of accesing objects. For example,
Providers are strategies of accessing objects. For example,
: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
injected into each other. Providers could be overridden by another
providers. Base class is -
instance once and returns it on every next call. Base class is -
:py:class:`dependency_injector.providers.Provider`.
Injections
Providers could be:
+ Injected into each other.
+ Overridden by each other.
+ Extended.
Containers
~~~~~~~~~~
Injections are instructions for making dependency injections
(there are several ways how they could be done). Injections are used
mostly 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
Containers are collections of providers. They are used for grouping
of providers by some principles. Base class is -
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Containers could be:
+ Overridden by each other.
+ Copied from each other.
+ Extended.

View File

@ -6,9 +6,7 @@ What is dependency injection and inversion of control?
:description: This article provides definition of dependency injection,
inversion of control and dependency inversion. It contains
example code in Python that is refactored to be following
inversion of control principle and then enhanced by
inversion of control container based on "Dependency Injector"
declarative catalog.
inversion of control principle.
Definition
~~~~~~~~~~

View File

@ -11,6 +11,19 @@ Development version
-------------------
- No features.
2.0.0
------
- Introduce new injections style for ``Callable``, ``Factory`` &
``Singleton`` providers.
- Drop providers: ``Static``, ``Value``, ``Function``, ``Class``, ``Config``.
- Increase performance of making injections in 2 times (+100%).
- Drop method injections.
- Simplify providers overriding system.
- Replace ``catalogs`` package with ``containers`` module.
- Drop all backward compatibilities for 1.x.
- Refactor most of the components.
- Update documentation.
1.17.0
------
- Add ``add_injections()`` method to ``Callable``, ``DelegatedCallable``,

View File

@ -12,13 +12,13 @@ framework can be installed from PyPi_:
pip install dependency_injector
# Installing particular version:
pip install dependency_injector==0.11.0
pip install dependency_injector==2.0.0
Sources can be cloned from GitHub_:
.. code-block:: bash
git clone https://github.com/ets-labs/dependency_injector.git
git clone https://github.com/ets-labs/python-dependency-injector.git
Also all *Dependency Injector* releases can be downloaded from
`GitHub releases page`_.
@ -30,7 +30,7 @@ Verification of currently installed version could be done using
>>> from dependency_injector import VERSION
>>> VERSION
'0.11.0'
'2.0.0'
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
.. _GitHub: https://github.com/ets-labs/python-dependency-injector

View File

@ -3,37 +3,26 @@ Callable providers
.. currentmodule:: 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.
:py:class:`Callable` provider calls wrapped callable on every call.
Callable providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: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.
:py:class:`Callable` provider takes a various number of positional and keyword
arguments that are used as wrapped callable injections. Every time, when
:py:class:`Callable` provider 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 with several more things:
Injections are done according to the next rules:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined obviously.
Check out `Callable providers delegation`_.
+ All other injectable values are provided *"as is"*
For example, 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.
: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 :py:class:`Callable`
Check out :ref:`callable_providers_delegation`.
+ All other injectable values are provided *"as is"*.
+ Positional context arguments will be appended after :py:class:`Callable`
positional injections.
- Keyword context arguments have priority on :py:class:`Callable` keyword
+ Keyword context arguments have priority on :py:class:`Callable` keyword
injections and will be merged over them.
Example that shows usage of :py:class:`Callable` with positional argument
@ -54,31 +43,16 @@ injections:
:language: python
:linenos:
.. _callable_delegation:
.. _callable_providers_delegation:
Callable providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: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.
:py:class:`Callable` provider could be delegated to any other provider via
any kind of injection.
:py:class:`Callable` delegate could be created obviously using
``Delegate(Callable(...))`` or by calling ``Callable(...).delegate()`` method.
Example:
.. literalinclude:: ../../examples/providers/callable_delegation.py
:language: python
:linenos:
Alternative way of doing :py:class:`Callable` delegation is an usage of
:py:class:`DelegatedCallable`. :py:class:`DelegatedCallable` is a
:py:class:`Callable` that is always injected "as is".
Example:
.. literalinclude:: ../../examples/providers/delegated_callable.py
:language: python
:linenos:
Delegation of :py:class:`Callable` providers is the same as
:py:class:`Factory` providers delegation, please follow
:ref:`factory_providers_delegation` section for examples (with exception
about using :py:class:`DelegatedCallable` instead of
:py:class:`DelegatedFactory`).

View File

@ -28,11 +28,11 @@ Example:
.. note::
Class ``UserService`` is a part of some library. ``UserService`` has
Class ``UsersService`` is a part of some library. ``UsersService`` has
dependency on database connection, which can be satisfied with any
DBAPI 2.0 database connection. Being a self-sufficient library,
``UserService`` doesn't hardcode any kind of database management logic.
Instead of this, ``UserService`` has external dependency, that has to
``UsersService`` doesn't hardcode any kind of database management logic.
Instead of this, ``UsersService`` has external dependency, that has to
be satisfied by cleint's code, out of library's scope.
.. image:: /images/providers/external_dependency.png

View File

@ -24,154 +24,58 @@ 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 with several more things:
Injections are done according to the next rules:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined obviously.
Check out `Factory providers delegation`_.
+ All other injectable values are provided *"as is"*
Check out :ref:`factory_providers_delegation`.
+ All other injectable values are provided *"as is"*.
+ Positional context arguments will be appended after :py:class:`Factory`
positional injections.
+ Keyword context arguments have priority on :py:class:`Factory` keyword
injections and will be merged over them.
For example, 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
:py:class:`Factory` of particular class with ``__init__()`` argument
injections which injectable values are also provided by another factories:
.. note::
Current positional and keyword argument injections syntax (in the examples
below) is a **simplified one** version of full syntax. Examples of full
syntax and other types of injections could be found in sections below.
While positional / keyword argument injections may be the best way of
passing injections, current simplified syntax might be the preferable one
and could be widely used.
:py:class:`Factory` of particular class with ``__init__()`` injections which
injectable values are also provided by another factories:
.. image:: /images/providers/factory_init_injections.png
:width: 90%
:align: center
Example of usage positional argument injections:
.. literalinclude:: ../../examples/providers/factory_init_args.py
.. literalinclude:: ../../examples/providers/factory_init_injections.py
:language: python
:linenos:
Example of usage keyword argument injections:
.. literalinclude:: ../../examples/providers/factory_init_kwargs.py
:language: python
:linenos:
Factory providers and __init__ injections priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 :py:class:`Factory`
positional injections.
- Keyword context arguments have priority on :py:class:`Factory` keyword
injections and will be merged over them.
So, please, follow the example below:
.. image:: /images/providers/factory_init_injections_and_contexts.png
.. literalinclude:: ../../examples/providers/factory_init_injections_and_contexts.py
:language: python
:linenos:
Factory providers and other types of injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
: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
: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:
+ :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 :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 :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.
Example:
.. image:: /images/providers/factory_attribute_injections.png
.. literalinclude:: ../../examples/providers/factory_attribute_injections.py
:language: python
:linenos:
Factory providers and method injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
some cases.
Example:
.. image:: /images/providers/factory_method_injections.png
.. literalinclude:: ../../examples/providers/factory_method_injections.py
:language: python
:linenos:
.. _factory_providers_delegation:
Factory providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: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.
kind of injection.
As it was mentioned earlier, if :py:class:`Factory` is
injectable value, it will be called every time when injection needs to be
done. But sometimes there is a need to inject :py:class:`Factory` provider
itself (not a result of its call) as a dependency. Such injections are called
- *delegated provider injections*.
Saying in other words, delegation of factories - is a way to inject factories
themselves, instead of results of their calls.
: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.
just returns wrapped :py:class:`Factory`.
Actually, there are two ways of creating factory delegates:
Actually, there are three ways for creating factory delegates:
+ ``DelegatedFactory(...)`` - use special type of factory -
:py:class:`DelegatedFactory`. Such factories are always injected as
delegates ("as is").
+ ``Delegate(Factory(...))`` - obviously wrapping factory into
:py:class:`Delegate` provider.
+ ``Factory(...).delegate()`` - calling factory :py:meth:`Factory.delegate`
@ -187,24 +91,16 @@ Example:
:language: python
:linenos:
Alternative way of doing :py:class:`Factory` delegation is an usage of
:py:class:`DelegatedFactory`. :py:class:`DelegatedFactory` is a
:py:class:`Factory` that is always injected "as is".
Example:
.. literalinclude:: ../../examples/providers/delegated_factory.py
:language: python
:linenos:
.. _factory_providers_specialization:
Factory providers specialization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Factory` provider could be specialized for any kind of needs via
declaring its subclasses.
creating its subclasses.
One of such `builtin` features is a limitation to :py:class:`Factory` provided
type:
One of such specialization features is a limitation to :py:class:`Factory`
provided type:
.. literalinclude:: ../../examples/providers/factory_provided_type.py
:language: python

View File

@ -1,11 +1,9 @@
Providers
=========
Providers are strategies of accessing objects. They describe how particular
Providers are strategies of accessing objects. They define 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.
@ -17,7 +15,13 @@ 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`
+ Base providers class is: :py:class:`dependency_injector.providers.Provider`
+ Providers module API docs: :py:mod:`dependency_injector.providers`
+ Providers class diagram:
.. image:: /images/providers/providers_class_diagram.png
:width: 100%
:align: center
.. toctree::
:maxdepth: 2
@ -25,7 +29,7 @@ Providers module API docs - :py:mod:`dependency_injector.providers`
factory
singleton
callable
object
external_dependency
static
overriding
custom

12
docs/providers/object.rst Normal file
View File

@ -0,0 +1,12 @@
Object providers
----------------
.. currentmodule:: dependency_injector.providers
:py:class:`Object` provider returns provided instance "as is"
Example:
.. literalinclude:: ../../examples/providers/object.py
:language: python
:linenos:

View File

@ -5,7 +5,7 @@ Overriding of providers
Every provider could be overridden by another provider.
This gives opportunity to make system behaviour more flexible in some points.
This gives opportunity to make system behaviour more flexible at some point.
The main feature is that while your code is using providers, it depends on
providers, but not on the objects that providers provide. As a result of this,
you can change providing by provider object to a different one, but still
@ -14,7 +14,7 @@ compatible one, without chaning your previously written code.
Provider overriding functionality has such interface:
.. image:: /images/providers/provider_override.png
:width: 45%
:width: 55%
:align: center
+ :py:meth:`Provider.override()` - takes another provider that will be used
@ -22,15 +22,6 @@ Provider overriding functionality has such interface:
In such case, last passed provider would be used as overriding one.
+ :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:
+ :py:attr:`Provider.last_overriding` - always keeps reference to last
overriding provider.
+ :py:meth:`Provider.reset_last_overriding()` - remove last overriding
provider from stack of overriding providers.

View File

@ -16,6 +16,20 @@ Example:
:language: python
:linenos:
Singleton providers resetting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 and dependes on garbage collection strategy.
Example:
.. literalinclude:: ../../examples/providers/singleton_reseting.py
:language: python
:linenos:
Singleton providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -42,47 +56,17 @@ provider.
. It makes possible to inject providers *as is*. Please check out
`Singleton providers delegation`_ section.
Singleton providers resetting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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:
.. literalinclude:: ../../examples/providers/singleton_reseting.py
:language: python
:linenos:
Singleton providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: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.
any kind of injection.
:py:class:`Singleton` delegate could be created obviously using
``Delegate(Singleton(...))`` or by calling ``Singleton(...).delegate()``
method.
Example:
.. literalinclude:: ../../examples/providers/singleton_delegation.py
:language: python
:linenos:
Alternative way of doing :py:class:`Singleton` delegation is an usage of
:py:class:`DelegatedSingleton`. :py:class:`DelegatedSingleton` is a
:py:class:`Singleton` that is always injected "as is".
Example:
.. literalinclude:: ../../examples/providers/delegated_singleton.py
:language: python
:linenos:
Delegation of :py:class:`Singleton` providers is the same as
:py:class:`Factory` providers delegation, please follow
:ref:`factory_providers_delegation` section for examples (with exception
about using :py:class:`DelegatedSingleton` instead of
:py:class:`DelegatedFactory`).
Singleton providers specialization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -90,9 +74,6 @@ Singleton providers specialization
:py:class:`Singleton` provider could be specialized for any kind of needs via
declaring its subclasses.
One of such `builtin` features is a limitation to :py:class:`Singleton`
provided type:
.. literalinclude:: ../../examples/providers/singleton_provided_type.py
:language: python
:linenos:
Specialization of :py:class:`Singleton` providers is the same as
:py:class:`Factory` providers specialization, please follow
:ref:`factory_providers_specialization` section for examples.

View File

@ -1,22 +0,0 @@
Static providers
----------------
.. currentmodule:: dependency_injector.providers
Static providers are family of providers that return their values "as is".
There are four types of static providers:
- :py:class:`Class`
- :py:class:`Object`
- :py:class:`Function`
- :py:class:`Value`
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:
.. literalinclude:: ../../examples/providers/static.py
:language: python
:linenos:

View File

@ -1,63 +0,0 @@
"""Config provider examples."""
from dependency_injector import catalogs
from dependency_injector import providers
class ObjectA(object):
"""Example class ObjectA, that has dependencies on some setting values."""
def __init__(self, fee, price, timezone):
"""Initializer."""
self.fee = fee
self.price = price
self.timezone = timezone
class Catalog(catalogs.DeclarativeCatalog):
"""Catalog of providers."""
config = providers.Config()
""":type: providers.Config"""
object_a = providers.Factory(ObjectA,
fee=config.FEE,
price=config.PRICE,
timezone=config.GLOBAL.TIMEZONE)
# Setting config value and making some tests.
Catalog.config.update_from({
'FEE': 1.25,
'PRICE': 2.99,
'GLOBAL': {
'TIMEZONE': 'US/Eastern'
}
})
object_a1 = Catalog.object_a()
assert object_a1.fee == 1.25
assert object_a1.price == 2.99
assert object_a1.timezone == 'US/Eastern'
# Changing config value one more time and making some tests.
Catalog.config.update_from({
'FEE': 5.25,
'PRICE': 19.99,
'GLOBAL': {
'TIMEZONE': 'US/Western'
}
})
object_a2 = Catalog.object_a()
# New one ObjectA has new config values.
assert object_a2.fee == 5.25
assert object_a2.price == 19.99
assert object_a2.timezone == 'US/Western'
# And old one has old ones.
assert object_a1.fee == 1.25
assert object_a1.price == 2.99
assert object_a1.timezone == 'US/Eastern'

View File

@ -3,8 +3,8 @@
import sqlite3
import flask
from dependency_injector import providers
from dependency_injector import injections
import dependency_injector.providers as providers
import dependency_injector.injections as injections
database = providers.Singleton(sqlite3.connect,

View File

@ -4,8 +4,8 @@ import sqlite3
import flask
import flask.views
from dependency_injector import providers
from dependency_injector import injections
import dependency_injector.providers as providers
import dependency_injector.injections as injections
database = providers.Singleton(sqlite3.Connection,

View File

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

View File

@ -1,59 +0,0 @@
"""Catalog bundles example."""
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import errors
import services
import views
# Declaring services catalog:
class Services(catalogs.DeclarativeCatalog):
"""Example catalog of service providers."""
users = providers.Factory(services.Users)
auth = providers.Factory(services.Auth)
photos = providers.Factory(services.Photos)
# Declaring views catalog:
class Views(catalogs.DeclarativeCatalog):
"""Example catalog of web views."""
auth = providers.Factory(views.Auth,
services=Services.Bundle(Services.users,
Services.auth))
photos = providers.Factory(views.Photos,
services=Services.Bundle(Services.users,
Services.photos))
# Creating example views:
auth_view = Views.auth()
photos_view = Views.photos()
print auth_view.services # prints: <__main__.Services.Bundle(users, auth)>
print photos_view.services # prints <__main__.Services.Bundle(photos, users)>
# Making some asserts:
assert auth_view.services.users is Services.users
assert auth_view.services.auth is Services.auth
try:
auth_view.services.photos
except errors.Error:
# `photos` service provider is not in scope of `auth_view` services bundle,
# so `di.Error` will be raised.
pass
assert photos_view.services.users is Services.users
assert photos_view.services.photos is Services.photos
try:
photos_view.services.auth
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

@ -1,17 +0,0 @@
"""Example services."""
class BaseService(object):
"""Example base class of service."""
class Users(BaseService):
"""Example users service."""
class Auth(BaseService):
"""Example auth service."""
class Photos(BaseService):
"""Example photo service."""

View File

@ -1,21 +0,0 @@
"""Example web views."""
class BaseWebView(object):
"""Example base class of web view."""
def __init__(self, services):
"""Initializer.
:param services: Bundle of service providers
:type services: catalogs.Services
"""
self.services = services
class Auth(BaseWebView):
"""Example auth web view."""
class Photos(BaseWebView):
"""Example photo processing web view."""

View File

@ -1,22 +0,0 @@
"""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)
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

@ -1,30 +0,0 @@
"""Declarative catalogs inheritance example."""
from dependency_injector import catalogs
from dependency_injector import providers
class CatalogA(catalogs.DeclarativeCatalog):
"""Example catalog A."""
provider1 = providers.Factory(object)
class CatalogB(CatalogA):
"""Example catalog B."""
provider2 = providers.Singleton(object)
# Making some asserts for `providers` attribute:
assert CatalogA.providers == dict(provider1=CatalogA.provider1)
assert CatalogB.providers == dict(provider1=CatalogA.provider1,
provider2=CatalogB.provider2)
# Making some asserts for `cls_providers` attribute:
assert CatalogA.cls_providers == dict(provider1=CatalogA.provider1)
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)

View File

@ -1,47 +0,0 @@
"""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:')
users = providers.Factory(UsersService,
db=database)
auth = providers.Factory(AuthService,
db=database,
users_service=users)
# 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

@ -1,73 +0,0 @@
"""Specialized declarative catalog example."""
import services
from dependency_injector import providers
from dependency_injector import errors
class UsersService(services.Base):
"""Users service."""
def __init__(self, config):
"""Initializer."""
self.config = config
super(UsersService, self).__init__()
class AuthService(services.Base):
"""Auth service."""
def __init__(self, config, users_service):
"""Initializer."""
self.config = config
self.users_service = users_service
super(AuthService, self).__init__()
class Services(services.Catalog):
"""Services catalog."""
users = services.Provider(UsersService,
config={'option1': '111',
'option2': '222'})
auth = services.Provider(AuthService,
config={'option3': '333',
'option4': '444'},
users_service=users)
# Creating users & auth services:
users_service = Services.users()
auth_service = Services.auth()
# Making some asserts:
assert users_service.config == {'option1': '111',
'option2': '222'}
assert auth_service.config == {'option3': '333',
'option4': '444'}
assert isinstance(auth_service.users_service, UsersService)
# Trying to declare services catalog with other provider type:
try:
class Services1(services.Catalog):
"""Services catalog."""
users = providers.Factory(UsersService)
except errors.Error as exception:
print exception
# <__main__.Services1()> can contain only <class 'services.Provider'>
# instances
# Trying to declare services catalog with correct provider by invalid provided
# type:
try:
class Services2(services.Catalog):
"""Services catalog."""
users = services.Provider(object)
except errors.Error as exception:
print exception
# <class 'services.Provider'> can provide only <class 'services.Base'>
# instances

View File

@ -1,26 +0,0 @@
"""Base classes for services."""
from dependency_injector import catalogs
from dependency_injector import providers
class Base(object):
"""Base service class."""
class Provider(providers.Factory):
"""Service provider.
Can provide :py:class:`Base` only.
"""
provided_type = Base
class Catalog(catalogs.DeclarativeCatalog):
"""Base catalog of services.
Can include :py:class:`Provider`'s only.
"""
provider_type = Provider

View File

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

@ -1,63 +0,0 @@
"""Specialized dynamic catalog example."""
import services
from dependency_injector import providers
from dependency_injector import errors
class UsersService(services.Base):
"""Users service."""
def __init__(self, config):
"""Initializer."""
self.config = config
super(UsersService, self).__init__()
class AuthService(services.Base):
"""Auth service."""
def __init__(self, config, users_service):
"""Initializer."""
self.config = config
self.users_service = users_service
super(AuthService, self).__init__()
services_catalog = services.Catalog()
services_catalog.users = services.Provider(UsersService,
config={'option1': '111',
'option2': '222'})
services_catalog.auth = services.Provider(AuthService,
config={'option3': '333',
'option4': '444'},
users_service=services_catalog.users)
# Creating users & auth services:
users_service = services_catalog.users()
auth_service = services_catalog.auth()
# Making some asserts:
assert users_service.config == {'option1': '111',
'option2': '222'}
assert auth_service.config == {'option3': '333',
'option4': '444'}
assert isinstance(auth_service.users_service, UsersService)
# Trying to declare services catalog with other provider type:
try:
services_catalog.users = providers.Factory(UsersService)
except errors.Error as exception:
print exception
# <services.Catalog(users, auth)> can contain only
# <class 'services.Provider'> instances
# Trying to declare services catalog with correct provider by invalid provided
# type:
try:
services_catalog.users = services.Provider(object)
except errors.Error as exception:
print exception
# <class 'services.Provider'> can provide only <class 'services.Base'>
# instances

View File

@ -1,26 +0,0 @@
"""Base classes for services."""
from dependency_injector import catalogs
from dependency_injector import providers
class Base(object):
"""Base service class."""
class Provider(providers.Factory):
"""Service provider.
Can provide :py:class:`Base` only.
"""
provided_type = Base
class Catalog(catalogs.DynamicCatalog):
"""Base catalog of services.
Can include :py:class:`Provider`'s only.
"""
provider_type = Provider

View File

@ -1,45 +0,0 @@
"""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)
object2_factory = providers.Factory(Object2,
object1=object1_factory)
class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(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

@ -1,41 +0,0 @@
"""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)
object2_factory = providers.Factory(Object2,
object1=object1_factory)
# 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

@ -1,43 +0,0 @@
"""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)
object2_factory = providers.Factory(Object2,
object1=object1_factory)
# Overriding `Catalog` with `AnotherCatalog`:
@catalogs.override(Catalog)
class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(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

@ -0,0 +1,22 @@
"""Declarative IoC container simple example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
# Defining declarative IoC container:
class Container(containers.DeclarativeContainer):
"""Example IoC container."""
factory1 = providers.Factory(object)
factory2 = providers.Factory(object)
# Creating some objects:
object1 = Container.factory1()
object2 = Container.factory2()
# Making some asserts:
assert object1 is not object2
assert isinstance(object1, object)
assert isinstance(object2, object)

View File

@ -0,0 +1,30 @@
"""Declarative IoC containers inheritance example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class ContainerA(containers.DeclarativeContainer):
"""Example IoC container A."""
provider1 = providers.Factory(object)
class ContainerB(ContainerA):
"""Example IoC container B."""
provider2 = providers.Singleton(object)
# Making some asserts for `providers` attribute:
assert ContainerA.providers == dict(provider1=ContainerA.provider1)
assert ContainerB.providers == dict(provider1=ContainerA.provider1,
provider2=ContainerB.provider2)
# Making some asserts for `cls_providers` attribute:
assert ContainerA.cls_providers == dict(provider1=ContainerA.provider1)
assert ContainerB.cls_providers == dict(provider2=ContainerB.provider2)
# Making some asserts for `inherited_providers` attribute:
assert ContainerA.inherited_providers == dict()
assert ContainerB.inherited_providers == dict(provider1=ContainerB.provider1)

View File

@ -0,0 +1,35 @@
"""Declarative IoC container's provider injections example."""
import sqlite3
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
UsersService = collections.namedtuple('UsersService', ['db'])
AuthService = collections.namedtuple('AuthService', ['db', 'users_service'])
class Services(containers.DeclarativeContainer):
"""IoC container of service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
users = providers.Factory(UsersService,
db=database)
auth = providers.Factory(AuthService,
db=database,
users_service=users)
# Retrieving service providers from container:
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,48 @@
"""Specializing declarative container and factory provider example."""
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
class SequencesContainer(containers.DeclarativeContainer):
"""IoC container.
Can contain only sequence providers.
"""
provider_type = SequenceProvider
if __name__ == '__main__':
try:
class _SequenceContainer1(SequencesContainer):
object_provider = providers.Factory(object)
except errors.Error as exception:
print exception
# <class '__main__._SequenceContainer1'> can contain only
# <class '__main__.SequenceProvider'> instances
try:
class _SequenceContainer2(SequencesContainer):
object_provider = SequenceProvider(object)
except errors.Error as exception:
print exception
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
class _SequenceContaier3(SequencesContainer):
list_provider = SequenceProvider(list)
assert _SequenceContaier3.list_provider() == list()

View File

@ -0,0 +1,18 @@
"""Dynamic container simple example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
# Defining dynamic container:
container = containers.DynamicContainer()
container.factory1 = providers.Factory(object)
container.factory2 = providers.Factory(object)
# Creating some objects:
object1 = container.factory1()
object2 = container.factory2()
# Making some asserts:
assert object1 is not object2
assert isinstance(object1, object) and isinstance(object2, object)

View File

@ -0,0 +1,41 @@
"""Specializing dynamic container and factory provider example."""
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
sequences_container = containers.DynamicContainer()
sequences_container.provider_type = SequenceProvider
if __name__ == '__main__':
try:
sequences_container.object_provider = providers.Factory(object)
except errors.Error as exception:
print exception
# <dependency_injector.containers.DynamicContainer object at
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
# instances
try:
sequences_container.object_provider = SequenceProvider(object)
except errors.Error as exception:
print exception
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
sequences_container.list_provider = SequenceProvider(list)
assert sequences_container.list_provider() == list()

View File

@ -1,15 +1,13 @@
"""Dynamic catalog creation and runtime filling of it example."""
"""Creation of dynamic container based on some configuration example."""
from dependency_injector import catalogs
import collections
import dependency_injector.containers as containers
# Defining several example services:
class UsersService(object):
"""Example users service."""
class AuthService(object):
"""Example auth service."""
UsersService = collections.namedtuple('UsersService', [])
AuthService = collections.namedtuple('AuthService', [])
def import_cls(cls_name):
@ -19,9 +17,7 @@ def import_cls(cls_name):
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]),
module = __import__('.'.join(path_components[:-1]),
locals(),
globals(),
fromlist=path_components[-1:])
@ -32,30 +28,27 @@ def import_cls(cls_name):
config = {
'services': {
'users': {
'class': 'UsersService',
'class': '__main__.UsersService',
'provider_class': 'dependency_injector.providers.Factory',
},
'auth': {
'class': 'AuthService',
'class': '__main__.AuthService',
'provider_class': 'dependency_injector.providers.Factory',
}
}
}
# Defining dynamic service providers catalog:
services = catalogs.DynamicCatalog()
# Creating empty container of service providers:
services = containers.DynamicContainer()
# Filling dynamic service providers catalog according to the configuration:
# Filling dynamic container with service providers using 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)
setattr(services, service_name, service_provider_cls(service_cls))
# Creating some objects:
users_service = services.users()

View File

@ -0,0 +1,28 @@
"""Declarative IoC container overriding example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Container(containers.DeclarativeContainer):
"""IoC container."""
sequence_factory = providers.Factory(list)
class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container."""
sequence_factory = providers.Factory(tuple)
# Overriding `Container` with `OverridingContainer`:
Container.override(OverridingContainer)
# Creating some objects using overridden container:
sequence_1 = Container.sequence_factory([1, 2, 3])
sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts:
assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)

View File

@ -0,0 +1,27 @@
"""Declarative IoC container overriding using `@override()` decorator."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Container(containers.DeclarativeContainer):
"""IoC container."""
sequence_factory = providers.Factory(list)
# Overriding `Container` with `OverridingContainer`:
@containers.override(Container)
class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container."""
sequence_factory = providers.Factory(tuple)
# Creating some objects using overridden container:
sequence_1 = Container.sequence_factory([1, 2, 3])
sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts:
assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)

View File

@ -10,7 +10,7 @@ class Car(object):
def __init__(self):
"""Initializer."""
self.engine = Engine()
self.engine = Engine() # Engine is a "hardcoded" dependency
if __name__ == '__main__':

View File

@ -1,4 +1,4 @@
"""Refactored Car & Engine example that demostrates inversion of control."""
"""Refactored Car & Engine example that demonstrates inversion of control."""
class Engine(object):
@ -10,7 +10,7 @@ class Car(object):
def __init__(self, engine):
"""Initializer."""
self.engine = engine
self.engine = engine # Engine is an "injected" dependency
if __name__ == '__main__':

Some files were not shown because too many files have changed in this diff Show More