Merge remote-tracking branch 'origin/2.0'
135
README.rst
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
|
|
|
@ -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',
|
||||
)
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
363
dependency_injector/containers.py
Normal 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))
|
|
@ -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__()``.
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
"""
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
``dependency_injector.catalogs``
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: dependency_injector.catalogs
|
||||
:members:
|
||||
:special-members:
|
6
docs/api/containers.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``dependency_injector.containers``
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: dependency_injector.containers
|
||||
:members:
|
||||
:special-members:
|
|
@ -6,7 +6,7 @@ API Documentation
|
|||
|
||||
top_level
|
||||
providers
|
||||
containers
|
||||
injections
|
||||
catalogs
|
||||
utils
|
||||
errors
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
.. automodule:: dependency_injector.injections
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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:
|
|
@ -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
|
|
@ -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:
|
|
@ -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:
|
55
docs/containers/declarative.rst
Normal 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:
|
27
docs/containers/dynamic.rst
Normal 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
|
@ -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
|
40
docs/containers/overriding.rst
Normal 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.
|
24
docs/containers/specialization.rst
Normal 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:
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 231 KiB |
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 295 KiB |
BIN
docs/images/containers/declarative.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
docs/images/containers/declarative_inheritance.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
docs/images/containers/declarative_injections.png
Normal file
After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 10 KiB |
BIN
docs/images/providers/providers_class_diagram.png
Normal file
After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 11 KiB |
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~
|
||||
|
|
|
@ -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``,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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:
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
|
@ -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'
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
|
@ -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."""
|
|
@ -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."""
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
22
examples/containers/declarative.py
Normal 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)
|
30
examples/containers/declarative_inheritance.py
Normal 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)
|
35
examples/containers/declarative_injections.py
Normal 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()
|
48
examples/containers/declarative_provider_type.py
Normal 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()
|
18
examples/containers/dynamic.py
Normal 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)
|
41
examples/containers/dynamic_provider_type.py
Normal 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()
|
|
@ -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()
|
28
examples/containers/override_declarative.py
Normal 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)
|
27
examples/containers/override_declarative_decorator.py
Normal 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)
|
|
@ -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__':
|
||||
|
|
|
@ -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__':
|
||||
|
|