Merge pull request #143 from ets-labs/138_cython_providers_v3

Cython providers
This commit is contained in:
Roman 2016-11-11 17:27:54 +02:00 committed by GitHub
commit 8259de6dd7
38 changed files with 2753 additions and 1541 deletions

View File

@ -16,6 +16,9 @@ dependency injection pattern in formal, pretty, Pythonic way.
+ Documentation. + Documentation.
+ Semantic versioning. + Semantic versioning.
*Dependency Injector* providers are implemented as C extension types using
Cython.
Status Status
------ ------

View File

@ -7,5 +7,4 @@ API Documentation
top_level top_level
providers providers
containers containers
utils
errors errors

View File

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

View File

@ -17,7 +17,7 @@ Also, for both of these and some other cases, it might be useful to attach
some init / shutdown functionality or something else, that deals with group some init / shutdown functionality or something else, that deals with group
of providers. of providers.
Containers module API docs - :py:mod:`dependency_injector.containers`. Containers package API docs - :py:mod:`dependency_injector.containers`.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

View File

@ -8,7 +8,14 @@ Dependency Injector --- Dependency injection microframework for Python
:description: Dependency Injector is a dependency injection microframework :description: Dependency Injector is a dependency injection microframework
for Python. It was designed to be unified, developer-friendly for Python. It was designed to be unified, developer-friendly
tool that helps to implement dependency injection pattern in tool that helps to implement dependency injection pattern in
formal, pretty, Pythonic way. formal, pretty, Pythonic way. Dependency Injector framework
key features are: Easy, smart, pythonic style; Obvious, clear
structure; Extensibility and flexibility; High performance;
Memory efficiency; Thread safety; Documentation; Semantic
versioning; Dependency Injector providers are implemented as
C extension types using Cython.
.. _index:
*Dependency Injector* is a dependency injection microframework for Python. *Dependency Injector* is a dependency injection microframework for Python.
It was designed to be unified, developer-friendly tool that helps to implement It was designed to be unified, developer-friendly tool that helps to implement
@ -19,11 +26,15 @@ dependency injection pattern in formal, pretty, Pythonic way.
+ Easy, smart, pythonic style. + Easy, smart, pythonic style.
+ Obvious, clear structure. + Obvious, clear structure.
+ Extensibility and flexibility. + Extensibility and flexibility.
+ High performance.
+ Memory efficiency. + Memory efficiency.
+ Thread safety. + Thread safety.
+ Documentation. + Documentation.
+ Semantic versioning. + Semantic versioning.
*Dependency Injector* providers are implemented as C extension types using
Cython.
Status Status
------ ------

View File

@ -121,7 +121,7 @@ code for specification of dependencies. Nevertheless, this disadvantage could
be easily avoided by creating inversion of control container (IoC container). be easily avoided by creating inversion of control container (IoC container).
Example of creation of several inversion of control containers (IoC containers) Example of creation of several inversion of control containers (IoC containers)
using *Dependency Injector*: using :doc:`Dependency Injector <../index>`:
.. literalinclude:: ../../examples/miniapps/engines_cars/example_ioc_containers.py .. literalinclude:: ../../examples/miniapps/engines_cars/example_ioc_containers.py
:language: python :language: python

View File

@ -9,16 +9,40 @@ follows `Semantic versioning`_
Development version Development version
------------------- -------------------
- Add ``dependency_injector.injections`` module (C extension). - **Providers**
- Transfer ``dependency_injector.utils`` module to Cython (C extension).
- Transfer ``dependency_injector.errors`` module to Cython (C extension).
- Remove ``@inject`` decorator.
- Add makefile (``clean``, ``test``, ``build``, ``install``, ``uninstall``
& ``publish`` commands).
- Update repository structure:
- Sources are moved under ``src``. 1. All providers from ``dependency_injector.providers`` package are
- Tests are moved under ``tests/unit``. implemented as C extension types using Cython.
2. Add ``BaseSingleton`` super class for all singleton providers.
3. Make ``Singleton`` provider not thread-safe. It makes performance of
``Singleton`` provider 10x times faster.
4. Add ``ThreadSafeSingleton`` provider - thread-safe version of
``Singleton`` provider.
5. Add ``ThreadLocalSingleton`` provider - ``Singleton`` provider that uses
thread-local storage.
6. Remove ``provides`` attribute from ``Factory`` and ``Singleton``
providers.
7. Add ``set_args()`` and ``clear_args()`` methods for ``Callable``,
``Factory`` and ``Singleton`` providers.
- **Containers**
1. Module ``dependency_injector.containers`` was splitted into submodules
without any functional changes.
- **Utils**
1. Module ``dependency_injector.utils`` is splitted into
``dependency_injector.containers`` and ``dependency_injector.providers``.
- **Miscellaneous**
1. Remove ``@inject`` decorator.
2. Add makefile (``clean``, ``test``, ``build``, ``install``, ``uninstall``
& ``publish`` commands).
3. Update repository structure:
1. Sources are moved under ``src/`` folder.
2. Tests are moved under ``tests/unit/`` folder.
.. - No features. .. - No features.

View File

@ -12,7 +12,12 @@ framework can be installed from PyPi_:
pip install dependency_injector pip install dependency_injector
# Installing particular version: # Installing particular version:
pip install dependency_injector==2.0.0 pip install dependency-injector==3.0.0
.. note::
Some components of *Dependency Injector* are implemented as C extension types.
*Dependency Injector* is distributed as an archive with a source code, so
C compiler and Python header files are required for the installation.
Sources can be cloned from GitHub_: Sources can be cloned from GitHub_:
@ -30,7 +35,7 @@ Verification of currently installed version could be done using
>>> from dependency_injector import VERSION >>> from dependency_injector import VERSION
>>> VERSION >>> VERSION
'2.0.0' '3.0.0'
.. _PyPi: https://pypi.python.org/pypi/dependency_injector .. _PyPi: https://pypi.python.org/pypi/dependency_injector
.. _GitHub: https://github.com/ets-labs/python-dependency-injector .. _GitHub: https://github.com/ets-labs/python-dependency-injector

View File

@ -12,16 +12,7 @@ Current documentation section consists from description of standard providers
library and some useful information like overriding of providers and writing library and some useful information like overriding of providers and writing
of custom providers. of custom providers.
All providers are validated in multithreading environment and considered to Providers package API docs - :py:mod:`dependency_injector.providers`
be thread safe.
+ 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:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -33,15 +33,15 @@ Example:
Singleton providers and injections Singleton providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider extends :py:class:`Factory` provider, so, all :py:class:`Singleton` provider has same interface as :py:class:`Factory`
of the rules about injections are the same, as for :py:class:`Factory` provider, so, all of the rules about injections are the same, as for
provider. :py:class:`Factory` provider.
.. note:: .. note::
Due that :py:class:`Singleton` provider creates specified class instance Due that :py:class:`Singleton` provider creates specified class instance
only on the first call, all injections are done once, during the first only on the first call, all injections are done once, during the first
call, also. Every next call, while instance has been already created call. Every next call, while instance has been already created
and memorized, no injections are done, :py:class:`Singleton` provider just and memorized, no injections are done, :py:class:`Singleton` provider just
returns memorized earlier instance. returns memorized earlier instance.
@ -81,10 +81,12 @@ Specialization of :py:class:`Singleton` providers is the same as
Singleton providers and multi-threading Singleton providers and multi-threading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider is thread-safe and could be used in :py:class:`Singleton` provider is NOT thread-safe and should be used in
multi-threading applications without any negative impact. Race condition on multi-threading applications with manually controlled locking.
singleton's initialization is escaped by using a global reentrant mutex -
:py:obj:`dependency_injector.utils.GLOBAL_LOCK`. :py:class:`ThreadSafeSingleton` is a thread-safe version of
:py:class:`Singleton` and could be used in multi-threading applications
without any additional locking.
Also there could be a need to use thread-scoped singletons and there is a Also there could be a need to use thread-scoped singletons and there is a
special provider for such case - :py:class:`ThreadLocalSingleton`. special provider for such case - :py:class:`ThreadLocalSingleton`.

View File

@ -47,6 +47,22 @@ setup(name='dependency-injector',
'': 'src', '': 'src',
}, },
ext_modules=[ ext_modules=[
Extension('dependency_injector.providers.base',
['src/dependency_injector/providers/base.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.callables',
['src/dependency_injector/providers/callables.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.factories',
['src/dependency_injector/providers/factories.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.singletons',
['src/dependency_injector/providers/singletons.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.injections', Extension('dependency_injector.providers.injections',
['src/dependency_injector/providers/injections.c'], ['src/dependency_injector/providers/injections.c'],
define_macros=defined_macros, define_macros=defined_macros,

View File

@ -1,20 +1,12 @@
"""Dependency injector container utils.""" """Dependency injector container utils."""
import copy as _copy
import types
import six import six
from dependency_injector.providers import deepcopy
from dependency_injector.errors import Error from dependency_injector.errors import Error
if six.PY2: # pragma: no cover
_copy._deepcopy_dispatch[types.MethodType] = \
lambda obj, memo: type(obj)(obj.im_func,
_copy.deepcopy(obj.im_self, memo),
obj.im_class)
def is_container(instance): def is_container(instance):
"""Check if instance is container instance. """Check if instance is container instance.
@ -74,11 +66,6 @@ def copy(container):
return _decorator return _decorator
def deepcopy(instance, memo=None):
"""Make full copy of instance."""
return _copy.deepcopy(instance, memo)
def _check_provider_type(cls, provider): def _check_provider_type(cls, provider):
if not isinstance(provider, cls.provider_type): if not isinstance(provider, cls.provider_type):
raise Error('{0} can contain only {1} ' raise Error('{0} can contain only {1} '

View File

@ -2,46 +2,51 @@
from .base import ( from .base import (
Provider, Provider,
Delegate,
Object, Object,
Delegate,
ExternalDependency, ExternalDependency,
OverridingContext, OverridingContext,
override,
) )
from .callable import ( from .callables import (
Callable, Callable,
DelegatedCallable, DelegatedCallable,
) )
from .creational import ( from .factories import (
Factory, Factory,
DelegatedFactory, DelegatedFactory,
)
from .singletons import (
BaseSingleton,
Singleton, Singleton,
DelegatedSingleton, DelegatedSingleton,
ThreadSafeSingleton,
DelegatedThreadSafeSingleton,
ThreadLocalSingleton, ThreadLocalSingleton,
DelegatedThreadLocalSingleton, DelegatedThreadLocalSingleton,
) )
from .utils import (
GLOBAL_LOCK,
is_provider,
ensure_is_provider,
is_delegated,
represent_provider,
)
from .injections import ( from .injections import (
Injection, Injection,
PositionalInjection, PositionalInjection,
NamedInjection, NamedInjection,
) )
from .utils import (
is_provider,
ensure_is_provider,
is_delegated,
represent_provider,
deepcopy,
)
__all__ = ( __all__ = (
'Provider', 'Provider',
'Delegate',
'Object', 'Object',
'Delegate',
'ExternalDependency', 'ExternalDependency',
'OverridingContext', 'OverridingContext',
'override',
'Callable', 'Callable',
'DelegatedCallable', 'DelegatedCallable',
@ -49,19 +54,24 @@ __all__ = (
'Factory', 'Factory',
'DelegatedFactory', 'DelegatedFactory',
'BaseSingleton',
'Singleton', 'Singleton',
'DelegatedSingleton', 'DelegatedSingleton',
'ThreadSafeSingleton',
'DelegatedThreadSafeSingleton',
'ThreadLocalSingleton', 'ThreadLocalSingleton',
'DelegatedThreadLocalSingleton', 'DelegatedThreadLocalSingleton',
'GLOBAL_LOCK', 'Injection',
'PositionalInjection',
'NamedInjection',
'is_provider', 'is_provider',
'ensure_is_provider', 'ensure_is_provider',
'is_delegated', 'is_delegated',
'represent_provider', 'represent_provider',
'deepcopy',
'Injection',
'PositionalInjection',
'NamedInjection',
) )

View File

@ -0,0 +1,31 @@
"""Dependency injector base providers.
Powered by Cython.
"""
cdef class Provider(object):
cdef tuple __overridden
cdef int __overridden_len
cpdef object _provide(self, tuple args, dict kwargs)
cpdef object _call_last_overriding(self, tuple args, dict kwargs)
cdef class Object(Provider):
cdef object __provides
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Delegate(Object):
pass
cdef class ExternalDependency(Provider):
cdef type __instance_of
cdef class OverridingContext(object):
cdef Provider __overridden
cdef Provider __overriding

View File

@ -1,17 +1,22 @@
"""Dependency injector base providers.""" """Dependency injector base providers.
import six Powered by Cython.
"""
cimport cython
from dependency_injector.errors import Error from dependency_injector.errors import Error
from .utils import (
from .utils cimport (
CLASS_TYPES,
is_provider, is_provider,
ensure_is_provider, ensure_is_provider,
represent_provider, represent_provider,
deepcopy,
) )
@six.python_2_unicode_compatible cdef class Provider(object):
class Provider(object):
"""Base provider class. """Base provider class.
:py:class:`Provider` is callable (implements ``__call__`` method). Every :py:class:`Provider` is callable (implements ``__call__`` method). Every
@ -59,38 +64,53 @@ class Provider(object):
""" """
__IS_PROVIDER__ = True __IS_PROVIDER__ = True
__OPTIMIZED_CALLS__ = True
__slots__ = ('overridden', 'provide', '__call__')
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer."""
self.overridden = tuple() self.__overridden = tuple()
self.__overridden_len = 0
super(Provider, self).__init__() super(Provider, self).__init__()
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._provide
def _provide(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Providing strategy implementation. """Return provided object.
Abstract protected method that implements providing strategy of Callable interface implementation.
particular provider. Current method is called every time when not
overridden provider is called. Need to be overridden in subclasses.
""" """
raise NotImplementedError() if self.__overridden_len != 0:
return self._call_last_overriding(args, kwargs)
return self._provide(args, kwargs)
def _call_last_overriding(self, *args, **kwargs): def __deepcopy__(self, memo):
"""Call last overriding provider and return result.""" """Create and return full copy of provider."""
return (self.overridden[-1](*args, **kwargs) copied = memo.get(id(self))
if self.overridden if copied is not None:
else None) return copied
def provide_injection(self): copied = self.__class__()
"""Injection strategy implementation.
:rtype: object for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
""" """
return self.provide() return represent_provider(provider=self, provides=None)
def __repr__(self):
"""Return string representation of provider.
:rtype: str
"""
return self.__str__()
@property
def overridden(self):
"""Return tuple of overriding providers."""
return self.__overridden
def override(self, provider): def override(self, provider):
"""Override provider with another provider. """Override provider with another provider.
@ -100,8 +120,8 @@ class Provider(object):
:raise: :py:exc:`dependency_injector.errors.Error` :raise: :py:exc:`dependency_injector.errors.Error`
:return: Overriding provider. :return: Overriding context.
:rtype: :py:class:`Provider` :rtype: :py:class:`OverridingContext`
""" """
if provider is self: if provider is self:
raise Error('Provider {0} could not be overridden ' raise Error('Provider {0} could not be overridden '
@ -110,14 +130,13 @@ class Provider(object):
if not is_provider(provider): if not is_provider(provider):
provider = Object(provider) provider = Object(provider)
self.overridden += (ensure_is_provider(provider),) self.__overridden += (provider,)
self.__overridden_len += 1
# Disable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._call_last_overriding
return OverridingContext(self, provider) return OverridingContext(self, provider)
@cython.boundscheck(False)
@cython.wraparound(False)
def reset_last_overriding(self): def reset_last_overriding(self):
"""Reset last overriding provider. """Reset last overriding provider.
@ -126,26 +145,19 @@ class Provider(object):
:rtype: None :rtype: None
""" """
if not self.overridden: if self.__overridden_len == 0:
raise Error('Provider {0} is not overridden'.format(str(self))) raise Error('Provider {0} is not overridden'.format(str(self)))
self.overridden = self.overridden[:-1] self.__overridden = self.__overridden[:self.__overridden_len - 1]
self.__overridden_len -= 1
if not self.overridden:
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._provide
def reset_override(self): def reset_override(self):
"""Reset all overriding providers. """Reset all overriding providers.
:rtype: None :rtype: None
""" """
self.overridden = tuple() self.__overridden = tuple()
self.__overridden_len = 0
# Enable __call__() / _provide() optimization
if self.__class__.__OPTIMIZED_CALLS__:
self.__call__ = self.provide = self._provide
def delegate(self): def delegate(self):
"""Return provider's delegate. """Return provider's delegate.
@ -154,73 +166,25 @@ class Provider(object):
""" """
return Delegate(self) return Delegate(self)
def __str__(self): cpdef object _provide(self, tuple args, dict kwargs):
"""Return string representation of provider. """Providing strategy implementation.
:rtype: str Abstract protected method that implements providing strategy of
particular provider. Current method is called every time when not
overridden provider is called. Need to be overridden in subclasses.
""" """
return represent_provider(provider=self, provides=None) raise NotImplementedError()
__repr__ = __str__ @cython.boundscheck(False)
@cython.wraparound(False)
cpdef object _call_last_overriding(self, tuple args, dict kwargs):
"""Call last overriding provider and return result."""
return <object>self.__overridden[self.__overridden_len - 1](*args,
**kwargs)
@six.python_2_unicode_compatible cdef class Object(Provider):
class Delegate(Provider): """Object provider returns provided instance "as is".
""":py:class:`Delegate` provider delegates another provider.
.. code-block:: python
provider = Factory(object)
delegate = Delegate(provider)
delegated = delegate()
assert provider is delegated
.. py:attribute:: delegated
Delegated provider.
:type: :py:class:`Provider`
"""
__slots__ = ('delegated',)
def __init__(self, delegated):
"""Initializer.
:provider delegated: Delegated provider.
:type delegated: :py:class:`Provider`
"""
self.delegated = ensure_is_provider(delegated)
super(Delegate, self).__init__()
def _provide(self, *args, **kwargs):
"""Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.delegated
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.delegated)
__repr__ = __str__
@six.python_2_unicode_compatible
class Object(Provider):
""":py:class:`Object` provider returns provided instance "as is".
.. py:attribute:: provides .. py:attribute:: provides
@ -229,18 +193,43 @@ class Object(Provider):
:type: object :type: object
""" """
__slots__ = ('provides',)
def __init__(self, provides): def __init__(self, provides):
"""Initializer. """Initializer.
:param provides: Value that have to be provided. :param provides: Value that have to be provided.
:type provides: object :type provides: object
""" """
self.provides = provides self.__provides = provides
super(Object, self).__init__() super(Object, self).__init__()
def _provide(self, *args, **kwargs): def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(deepcopy(self.__provides, memo))
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.__provides)
def __repr__(self):
"""Return string representation of provider.
:rtype: str
"""
return self.__str__()
cpdef object _provide(self, tuple args, dict kwargs):
"""Return provided instance. """Return provided instance.
:param args: Tuple of context positional arguments. :param args: Tuple of context positional arguments.
@ -251,20 +240,29 @@ class Object(Provider):
:rtype: object :rtype: object
""" """
return self.provides return self.__provides
def __str__(self):
"""Return string representation of provider.
:rtype: str cdef class Delegate(Object):
"""Delegate provider returns provider "as is".
.. py:attribute:: provides
Value that have to be provided.
:type: object
""" """
return represent_provider(provider=self, provides=self.provides)
__repr__ = __str__ def __init__(self, provides):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
super(Delegate, self).__init__(ensure_is_provider(provides))
@six.python_2_unicode_compatible cdef class ExternalDependency(Provider):
class ExternalDependency(Provider):
""":py:class:`ExternalDependency` provider describes dependency interface. """:py:class:`ExternalDependency` provider describes dependency interface.
This provider is used for description of dependency interface. That might This provider is used for description of dependency interface. That might
@ -286,18 +284,24 @@ class ExternalDependency(Provider):
:type: type :type: type
""" """
__OPTIMIZED_CALLS__ = False def __init__(self, type instance_of):
__slots__ = ('instance_of',)
def __init__(self, instance_of):
"""Initializer.""" """Initializer."""
if not isinstance(instance_of, six.class_types): self.__instance_of = instance_of
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__() super(ExternalDependency, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(self.__instance_of)
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Return provided instance. """Return provided instance.
@ -311,10 +315,12 @@ class ExternalDependency(Provider):
:rtype: object :rtype: object
""" """
if not self.overridden: cdef object instance
if self.__overridden_len == 0:
raise Error('Dependency is not defined') raise Error('Dependency is not defined')
instance = self._call_last_overriding(*args, **kwargs) instance = self._call_last_overriding(args, kwargs)
if not isinstance(instance, self.instance_of): if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) + raise Error('{0} is not an '.format(instance) +
@ -322,6 +328,25 @@ class ExternalDependency(Provider):
return instance return instance
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.__instance_of)
def __repr__(self):
"""Return string representation of provider.
:rtype: str
"""
return self.__str__()
@property
def instance_of(self):
"""Return class of required dependency."""
return self.__instance_of
def provided_by(self, provider): def provided_by(self, provider):
"""Set external dependency provider. """Set external dependency provider.
@ -332,17 +357,8 @@ class ExternalDependency(Provider):
""" """
return self.override(provider) return self.override(provider)
def __str__(self):
"""Return string representation of provider.
:rtype: str cdef class OverridingContext(object):
"""
return represent_provider(provider=self, provides=self.instance_of)
__repr__ = __str__
class OverridingContext(object):
"""Provider overriding context. """Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
@ -356,7 +372,7 @@ class OverridingContext(object):
assert not provider.overridden assert not provider.overridden
""" """
def __init__(self, overridden, overriding): def __init__(self, Provider overridden, Provider overriding):
"""Initializer. """Initializer.
:param overridden: Overridden provider. :param overridden: Overridden provider.
@ -365,52 +381,14 @@ class OverridingContext(object):
:param overriding: Overriding provider. :param overriding: Overriding provider.
:type overriding: :py:class:`Provider` :type overriding: :py:class:`Provider`
""" """
self.overridden = overridden self.__overridden = overridden
self.overriding = overriding self.__overriding = overriding
super(OverridingContext, self).__init__()
def __enter__(self): def __enter__(self):
"""Do nothing.""" """Do nothing."""
return self.overriding return self.__overriding
def __exit__(self, *_): def __exit__(self, *_):
"""Exit overriding context.""" """Exit overriding context."""
self.overridden.reset_last_overriding() self.__overridden.reset_last_overriding()
def override(overridden):
"""Decorator for overriding providers.
This decorator overrides ``overridden`` provider by decorated one.
.. code-block:: python
@Factory
class SomeClass(object):
pass
@override(SomeClass)
@Factory
class ExtendedSomeClass(SomeClass.cls):
pass
:param overridden: Provider that should be overridden.
:type overridden: :py:class:`Provider`
:return: Overriding provider.
:rtype: :py:class:`Provider`
"""
def decorator(overriding):
overridden.override(overriding)
return overriding
return decorator
def _parse_positional_injections(args):
return tuple(arg if is_provider(arg) else Object(arg)
for arg in args)
def _parse_keyword_injections(kwargs):
return dict((name, arg if is_provider(arg) else Object(arg))
for name, arg in six.iteritems(kwargs))

View File

@ -1,127 +0,0 @@
"""Dependency injector callable providers."""
import six
from dependency_injector.providers.base import (
Provider,
_parse_positional_injections,
_parse_keyword_injections,
)
from .utils import represent_provider
from dependency_injector.errors import Error
@six.python_2_unicode_compatible
class Callable(Provider):
r""":py:class:`Callable` provider calls wrapped callable on every call.
:py:class:`Callable` supports positional and keyword argument injections:
.. code-block:: python
some_function = Callable(some_function,
'positional_arg1', 'positional_arg2',
keyword_argument1=3, keyword_argument=4)
# or
some_function = Callable(some_function) \
.add_args('positional_arg1', 'positional_arg2') \
.add_kwargs(keyword_argument1=3, keyword_argument=4)
# or
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')
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Wrapped callable.
:type provides: callable
"""
if not callable(provides):
raise Error('Provider {0} expected to get callable, '
'got {0}'.format('.'.join((self.__class__.__module__,
self.__class__.__name__)),
provides))
self.provides = provides
self.args = tuple()
self.kwargs = dict()
self.add_args(*args)
self.add_kwargs(**kwargs)
super(Callable, self).__init__()
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.kwargs.update(_parse_keyword_injections(kwargs))
return self
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
"""
if self.args:
args = tuple(arg.provide_injection() for arg in self.args) + args
for name, arg in six.iteritems(self.kwargs):
if name not in kwargs:
kwargs[name] = arg.provide_injection()
return self.provides(*args, **kwargs)
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.provides)
__repr__ = __str__
class DelegatedCallable(Callable):
""":py:class:`DelegatedCallable` is a delegated :py:class:`Callable`.
:py:class:`DelegatedCallable` is a :py:class:`Callable`, that is injected
"as is".
"""
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self

View File

@ -0,0 +1,42 @@
"""Dependency injector callable providers.
Powered by Cython.
"""
from .base cimport Provider
from .injections cimport (
PositionalInjection,
NamedInjection,
__provide_positional_args,
__provide_keyword_args,
)
cdef class Callable(Provider):
cdef object __provides
cdef tuple __args
cdef int __args_len
cdef tuple __kwargs
cdef int __kwargs_len
cpdef object _provide(self, tuple args, dict kwargs)
cdef inline object __provide(self, tuple args, dict kwargs):
cdef tuple positional_args
cdef dict keyword_args
positional_args = __provide_positional_args(args,
self.__args,
self.__args_len)
keyword_args = __provide_keyword_args(kwargs,
self.__kwargs,
self.__kwargs_len)
return self.__provides(*positional_args, **keyword_args)
cdef class DelegatedCallable(Callable):
pass

View File

@ -0,0 +1,208 @@
"""Dependency injector callable providers.
Powered by Cython.
"""
from dependency_injector.errors import Error
from .base cimport Provider
from .injections cimport (
PositionalInjection,
NamedInjection,
parse_positional_injections,
parse_named_injections,
)
from .utils cimport (
represent_provider,
deepcopy,
)
cdef class Callable(Provider):
r"""Callable provider calls wrapped callable on every call.
Callable supports positional and keyword argument injections:
.. code-block:: python
some_function = Callable(some_function,
'positional_arg1', 'positional_arg2',
keyword_argument1=3, keyword_argument=4)
# or
some_function = Callable(some_function) \
.add_args('positional_arg1', 'positional_arg2') \
.add_kwargs(keyword_argument1=3, keyword_argument=4)
# or
some_function = Callable(some_function)
some_function.add_args('positional_arg1', 'positional_arg2')
some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
"""
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Wrapped callable.
:type provides: callable
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
if not callable(provides):
raise Error('Provider {0} expected to get callable, '
'got {0}'.format('.'.join((self.__class__.__module__,
self.__class__.__name__)),
provides))
self.__provides = provides
self.__args = tuple()
self.__args_len = 0
self.set_args(*args)
self.__kwargs = tuple()
self.__kwargs_len = 0
self.set_kwargs(**kwargs)
super(Callable, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(self.provides,
*deepcopy(self.args, memo),
**deepcopy(self.kwargs, memo))
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.__provides)
@property
def provides(self):
"""Return wrapped callable."""
return self.__provides
@property
def args(self):
"""Return positional argument injections."""
cdef int index
cdef PositionalInjection arg
cdef list args
args = list()
for index in range(self.__args_len):
arg = self.__args[index]
args.append(arg.__value)
return tuple(args)
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)
self.__args_len = len(self.__args)
return self
def set_args(self, *args):
"""Set postional argument injections.
Existing positional argument injections are dropped.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__args = parse_positional_injections(args)
self.__args_len = len(self.__args)
return self
def clear_args(self):
"""Drop postional argument injections.
:return: Reference ``self``
"""
self.__args = tuple()
self.__args_len = len(self.__args)
return self
@property
def kwargs(self):
"""Return keyword argument injections."""
cdef int index
cdef NamedInjection kwarg
cdef dict kwargs
kwargs = dict()
for index in range(self.__kwargs_len):
kwarg = self.__kwargs[index]
kwargs[kwarg.__name] = kwarg.__value
return kwargs
def add_kwargs(self, **kwargs):
"""Add keyword argument injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__kwargs += parse_named_injections(kwargs)
self.__kwargs_len = len(self.__kwargs)
return self
def set_kwargs(self, **kwargs):
"""Set keyword argument injections.
Existing keyword argument injections are dropped.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__kwargs = parse_named_injections(kwargs)
self.__kwargs_len = len(self.__kwargs)
return self
def clear_kwargs(self):
"""Drop keyword argument injections.
:return: Reference ``self``
"""
self.__kwargs = tuple()
self.__kwargs_len = len(self.__kwargs)
return self
cpdef object _provide(self, tuple args, dict kwargs):
"""Return result of provided callable's call."""
return self.__provide(args, kwargs)
cdef class DelegatedCallable(Callable):
"""Callable that is injected "as is".
DelegatedCallable is a :py:class:`Callable`, that is injected "as is".
"""
__IS_DELEGATED__ = True

View File

@ -1,342 +0,0 @@
"""Dependency injector creational providers."""
import threading
import six
from dependency_injector.providers.callable import Callable
from dependency_injector.providers.base import _parse_keyword_injections
from .utils import GLOBAL_LOCK
from dependency_injector.errors import Error
class Factory(Callable):
r""":py:class:`Factory` provider creates new instance on every call.
: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
factory = Factory(SomeClass,
'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_object = factory()
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
provided_type = None
__slots__ = ('cls', 'attributes')
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
"""
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 = dict()
super(Factory, self).__init__(provides, *args, **kwargs)
self.cls = self.provides
def add_attributes(self, **kwargs):
"""Add attribute injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.attributes.update(_parse_keyword_injections(kwargs))
return self
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
"""
instance = super(Factory, self)._provide(*args, **kwargs)
for name, arg in six.iteritems(self.attributes):
setattr(instance, name, arg.provide_injection())
return instance
class DelegatedFactory(Factory):
""":py:class:`Factory` that is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self
class Singleton(Factory):
""":py:class:`Singleton` provider returns same instance on every call.
:py:class:`Singleton` provider creates instance once and returns it on
every call. :py:class:`Singleton` extends :py:class:`Factory`, so, please
follow :py:class:`Factory` documentation for getting familiar with
injections syntax.
:py:class:`Singleton` is thread-safe and could be used in multithreading
environment without any negative impact.
Retrieving of provided instance can be performed via calling
:py:class:`Singleton` object:
.. code-block:: python
singleton = Singleton(SomeClass)
some_object = singleton()
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__slots__ = ('instance',)
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
"""
self.instance = None
super(Singleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.instance = None
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
"""
with GLOBAL_LOCK:
if self.instance is None:
self.instance = super(Singleton, self)._provide(*args,
**kwargs)
return self.instance
class DelegatedSingleton(Singleton):
""":py:class:`Singleton` that is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self
class ThreadLocalSingleton(Factory):
""":py:class:`ThreadLocalSingleton` is singleton based on thread locals.
:py:class:`ThreadLocalSingleton` provider creates instance once for each
thread and returns it on every call. :py:class:`ThreadLocalSingleton`
extends :py:class:`Factory`, so, please follow :py:class:`Factory`
documentation for getting familiar with injections syntax.
:py:class:`ThreadLocalSingleton` is thread-safe and could be used in
multithreading environment without any negative impact.
Retrieving of provided instance can be performed via calling
:py:class:`ThreadLocalSingleton` object:
.. code-block:: python
singleton = ThreadLocalSingleton(SomeClass)
some_object = singleton()
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__slots__ = ('local_storage',)
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Class or other callable that provides object
for creation.
:type provides: type | callable
"""
self.local_storage = threading.local()
super(ThreadLocalSingleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.local_storage.instance = None
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
"""
try:
instance = self.local_storage.instance
except AttributeError:
instance = super(ThreadLocalSingleton, self)._provide(*args,
**kwargs)
self.local_storage.instance = instance
finally:
return instance
class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
""":py:class:`ThreadLocalSingleton` that is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
def provide_injection(self):
"""Injection strategy implementation.
:rtype: object
"""
return self

View File

@ -0,0 +1,33 @@
"""Dependency injector factory providers.
Powered by Cython.
"""
from .base cimport Provider
from .callables cimport Callable
from .injections cimport __inject_attributes
cdef class Factory(Provider):
cdef Callable __instantiator
cdef tuple __attributes
cdef int __attributes_len
cpdef object _provide(self, tuple args, dict kwargs)
cdef inline object __provide(self, tuple args, dict kwargs):
cdef object instance
instance = self.__instantiator.__provide(args, kwargs)
if self.__attributes_len > 0:
__inject_attributes(instance,
self.__attributes,
self.__attributes_len)
return instance
cdef class DelegatedFactory(Factory):
pass

View File

@ -0,0 +1,270 @@
"""Dependency injector factory providers.
Powered by Cython.
"""
from dependency_injector.errors import Error
from .base cimport Provider
from .callables cimport Callable
from .injections cimport (
NamedInjection,
parse_named_injections,
)
from .utils cimport (
represent_provider,
deepcopy,
)
cdef class Factory(Provider):
r"""Factory provider creates new instance on every call.
: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
factory = Factory(SomeClass,
'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_object = factory()
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
"""
provided_type = None
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Provided type.
:type provides: type
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
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.__instantiator = Callable(provides, *args, **kwargs)
self.__attributes = tuple()
self.__attributes_len = 0
super(Factory, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(self.cls,
*deepcopy(self.args, memo),
**deepcopy(self.kwargs, memo))
copied.set_attributes(**deepcopy(self.attributes, memo))
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self,
provides=self.__instantiator.provides)
@property
def cls(self):
"""Return provided type."""
return self.__instantiator.provides
@property
def args(self):
"""Return positional argument injections."""
return self.__instantiator.args
def add_args(self, *args):
"""Add __init__ postional argument injections.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.add_args(*args)
return self
def set_args(self, *args):
"""Set __init__ postional argument injections.
Existing __init__ positional argument injections are dropped.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.set_args(*args)
return self
def clear_args(self):
"""Drop __init__ postional argument injections.
:return: Reference ``self``
"""
self.__instantiator.clear_args()
return self
@property
def kwargs(self):
"""Return keyword argument injections."""
return self.__instantiator.kwargs
def add_kwargs(self, **kwargs):
"""Add __init__ keyword argument injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__instantiator.add_kwargs(**kwargs)
return self
def set_kwargs(self, **kwargs):
"""Set __init__ keyword argument injections.
Existing __init__ keyword argument injections are dropped.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__instantiator.set_kwargs(**kwargs)
return self
def clear_kwargs(self):
"""Drop __init__ keyword argument injections.
:return: Reference ``self``
"""
self.__instantiator.clear_kwargs()
return self
@property
def attributes(self):
"""Return attribute injections."""
cdef int index
cdef NamedInjection attribute
cdef dict attributes
attributes = dict()
for index in range(self.__attributes_len):
attribute = self.__attributes[index]
attributes[attribute.__name] = attribute.__value
return attributes
def add_attributes(self, **kwargs):
"""Add attribute injections.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__attributes += parse_named_injections(kwargs)
self.__attributes_len = len(self.__attributes)
return self
def set_attributes(self, **kwargs):
"""Set attribute injections.
Existing attribute injections are dropped.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__attributes = parse_named_injections(kwargs)
self.__attributes_len = len(self.__attributes)
return self
def clear_attributes(self):
"""Drop attribute injections.
:return: Reference ``self``
"""
self.__attributes = tuple()
self.__attributes_len = len(self.__attributes)
return self
cpdef object _provide(self, tuple args, dict kwargs):
"""Return new instance."""
return self.__provide(args, kwargs)
cdef class DelegatedFactory(Factory):
"""Factory that is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__IS_DELEGATED__ = True

View File

@ -6,7 +6,7 @@ Powered by Cython.
cimport cython cimport cython
cdef class Injection: cdef class Injection(object):
pass pass
@ -40,9 +40,9 @@ cdef class NamedInjection(Injection):
@cython.boundscheck(False) @cython.boundscheck(False)
@cython.wraparound(False) @cython.wraparound(False)
cdef inline tuple __provide_positional_args(tuple inj_args, cdef inline tuple __provide_positional_args(tuple args,
int inj_args_len, tuple inj_args,
tuple args): int inj_args_len):
cdef int index cdef int index
cdef list positional_args cdef list positional_args
cdef PositionalInjection injection cdef PositionalInjection injection
@ -53,29 +53,48 @@ cdef inline tuple __provide_positional_args(tuple inj_args,
positional_args = list() positional_args = list()
for index in range(inj_args_len): for index in range(inj_args_len):
injection = <PositionalInjection>inj_args[index] injection = <PositionalInjection>inj_args[index]
positional_args.append(injection.get_value()) positional_args.append(injection.__get_value())
positional_args.extend(args) positional_args.extend(args)
return positional_args return tuple(positional_args)
@cython.boundscheck(False) @cython.boundscheck(False)
@cython.wraparound(False) @cython.wraparound(False)
cdef inline dict __provide_keyword_args(tuple inj_kwargs, cdef inline dict __provide_keyword_args(dict kwargs,
int inj_kwargs_len, tuple inj_kwargs,
dict kwargs): int inj_kwargs_len):
cdef int index cdef int index
cdef object name
cdef NamedInjection kw_injection cdef NamedInjection kw_injection
if inj_kwargs_len == 0: if len(kwargs) == 0:
return kwargs
for index in range(inj_kwargs_len): for index in range(inj_kwargs_len):
kw_injection = <NamedInjection>inj_kwargs[index] kw_injection = <NamedInjection>inj_kwargs[index]
kwargs[kw_injection.get_name()] = kw_injection.get_value() name = kw_injection.__get_name()
kwargs[name] = kw_injection.__get_value()
else:
for index in range(inj_kwargs_len):
kw_injection = <NamedInjection>inj_kwargs[index]
name = kw_injection.__get_name()
if name not in kwargs:
kwargs[name] = kw_injection.__get_value()
return kwargs return kwargs
@cython.boundscheck(False)
@cython.wraparound(False)
cdef inline object __inject_attributes(object instance,
tuple attributes,
int attributes_len):
cdef NamedInjection attr_injection
for index in range(attributes_len):
attr_injection = <NamedInjection>attributes[index]
setattr(instance,
attr_injection.__get_name(),
attr_injection.__get_value())
cpdef tuple parse_positional_injections(tuple args) cpdef tuple parse_positional_injections(tuple args)
cpdef tuple parse_named_injections(dict kwargs) cpdef tuple parse_named_injections(dict kwargs)

View File

@ -8,10 +8,11 @@ cimport cython
from .utils cimport ( from .utils cimport (
is_provider, is_provider,
is_delegated, is_delegated,
deepcopy,
) )
cdef class Injection: cdef class Injection(object):
"""Abstract injection class.""" """Abstract injection class."""
@ -23,7 +24,16 @@ cdef class PositionalInjection(Injection):
self.__value = value self.__value = value
self.__is_provider = <int>is_provider(value) self.__is_provider = <int>is_provider(value)
self.__is_delegated = <int>is_delegated(value) self.__is_delegated = <int>is_delegated(value)
self.__call = <int>self.__is_provider == 1 and self.__is_delegated == 0 self.__call = <int>(self.__is_provider == 1 and
self.__is_delegated == 0)
super(PositionalInjection, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
return self.__class__(deepcopy(self.__value, memo))
def get_value(self): def get_value(self):
"""Return injection value.""" """Return injection value."""
@ -43,7 +53,17 @@ cdef class NamedInjection(Injection):
self.__value = value self.__value = value
self.__is_provider = <int>is_provider(value) self.__is_provider = <int>is_provider(value)
self.__is_delegated = <int>is_delegated(value) self.__is_delegated = <int>is_delegated(value)
self.__call = <int>self.__is_provider == 1 and self.__is_delegated == 0 self.__call = <int>(self.__is_provider == 1 and
self.__is_delegated == 0)
super(NamedInjection, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
return self.__class__(deepcopy(self.__name, memo),
deepcopy(self.__value, memo))
def get_name(self): def get_name(self):
"""Return injection value.""" """Return injection value."""

View File

@ -0,0 +1,64 @@
"""Dependency injector singleton providers.
Powered by Cython.
"""
from .base cimport Provider
from .factories cimport Factory
cdef class BaseSingleton(Provider):
cdef Factory __instantiator
cdef class Singleton(BaseSingleton):
cdef object __storage
cpdef object _provide(self, tuple args, dict kwargs)
cdef inline object __provide(self, tuple args, dict kwargs):
if self.__storage is None:
self.__storage = self.__instantiator.__provide(args, kwargs)
return self.__storage
cdef class DelegatedSingleton(Singleton):
pass
cdef class ThreadSafeSingleton(BaseSingleton):
cdef object __storage
cdef object __lock
cpdef object _provide(self, tuple args, dict kwargs)
cdef inline object __provide(self, tuple args, dict kwargs):
with self.__lock:
if self.__storage is None:
self.__storage = self.__instantiator.__provide(args, kwargs)
return self.__storage
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
pass
cdef class ThreadLocalSingleton(BaseSingleton):
cdef object __storage
cpdef object _provide(self, tuple args, dict kwargs)
cdef inline object __provide(self, tuple args, dict kwargs):
cdef object instance
try:
instance = self.__storage.instance
except AttributeError:
instance = self.__instantiator.__provide(args, kwargs)
self.__storage.instance = instance
finally:
return instance
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
pass

View File

@ -0,0 +1,393 @@
"""Dependency injector singleton providers.
Powered by Cython.
"""
import threading
from dependency_injector.errors import Error
from .base cimport Provider
from .factories cimport Factory
from .utils cimport (
represent_provider,
deepcopy,
)
GLOBAL_LOCK = threading.RLock()
"""Global reentrant lock.
:type: :py:class:`threading.RLock`
"""
cdef class BaseSingleton(Provider):
"""Base class of singleton providers."""
provided_type = None
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Provided type.
:type provides: type
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
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.__instantiator = Factory(provides, *args, **kwargs)
super(BaseSingleton, self).__init__()
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self,
provides=self.__instantiator.cls)
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(self.cls,
*deepcopy(self.args, memo),
**deepcopy(self.kwargs, memo))
copied.set_attributes(**deepcopy(self.attributes, memo))
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
@property
def cls(self):
"""Return provided type."""
return self.__instantiator.cls
@property
def args(self):
"""Return positional argument injections."""
return self.__instantiator.args
def add_args(self, *args):
"""Add __init__ postional argument injections.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.add_args(*args)
return self
def set_args(self, *args):
"""Set __init__ postional argument injections.
Existing __init__ positional argument injections are dropped.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.set_args(*args)
return self
def clear_args(self):
"""Drop __init__ postional argument injections.
:return: Reference ``self``
"""
self.__instantiator.clear_args()
return self
@property
def kwargs(self):
"""Return keyword argument injections."""
return self.__instantiator.kwargs
def add_kwargs(self, **kwargs):
"""Add __init__ keyword argument injections.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__instantiator.add_kwargs(**kwargs)
return self
def set_kwargs(self, **kwargs):
"""Set __init__ keyword argument injections.
Existing __init__ keyword argument injections are dropped.
:param kwargs: Dictionary of injections.
:type kwargs: dict
:return: Reference ``self``
"""
self.__instantiator.set_kwargs(**kwargs)
return self
def clear_kwargs(self):
"""Drop __init__ keyword argument injections.
:return: Reference ``self``
"""
self.__instantiator.clear_kwargs()
return self
@property
def attributes(self):
"""Return attribute injections."""
return self.__instantiator.attributes
def add_attributes(self, **kwargs):
"""Add attribute injections.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.add_attributes(**kwargs)
return self
def set_attributes(self, **kwargs):
"""Set attribute injections.
Existing attribute injections are dropped.
:param args: Tuple of injections.
:type args: tuple
:return: Reference ``self``
"""
self.__instantiator.set_attributes(**kwargs)
return self
def clear_attributes(self):
"""Drop attribute injections.
:return: Reference ``self``
"""
self.__instantiator.clear_attributes()
return self
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
raise NotImplementedError()
cdef class Singleton(BaseSingleton):
"""Singleton provider returns same instance on every call.
:py:class:`Singleton` provider creates instance once and returns it on
every call. :py:class:`Singleton` extends :py:class:`Factory`, so, please
follow :py:class:`Factory` documentation for getting familiar with
injections syntax.
Retrieving of provided instance can be performed via calling
:py:class:`Singleton` object:
.. code-block:: python
singleton = Singleton(SomeClass)
some_object = singleton()
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Provided type.
:type provides: type
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
self.__storage = None
super(Singleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.__storage = None
cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance."""
return self.__provide(args, kwargs)
cdef class DelegatedSingleton(Singleton):
"""Delegated singleton is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__IS_DELEGATED__ = True
cdef class ThreadSafeSingleton(BaseSingleton):
"""Thread-safe singleton provider."""
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Provided type.
:type provides: type
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
self.__storage = None
self.__lock = GLOBAL_LOCK
super(ThreadSafeSingleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.__storage = None
cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance."""
return self.__provide(args, kwargs)
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
"""Delegated thread-safe singleton is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__IS_DELEGATED__ = True
cdef class ThreadLocalSingleton(BaseSingleton):
"""Thread-local singleton provides single objects in scope of thread.
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
def __init__(self, provides, *args, **kwargs):
"""Initializer.
:param provides: Provided type.
:type provides: type
:param args: Tuple of positional argument injections.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword argument injections.
:type kwargs: dict[str, object]
"""
self.__storage = threading.local()
super(ThreadLocalSingleton, self).__init__(provides, *args, **kwargs)
def reset(self):
"""Reset cached instance, if any.
:rtype: None
"""
self.__storage.instance = None
cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance."""
return self.__provide(args, kwargs)
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
"""Delegated thread-local singleton is injected "as is".
.. py:attribute:: provided_type
If provided type is defined, provider checks that providing class is
its subclass.
:type: type | None
.. py:attribute:: cls
Class that provides object.
Alias for :py:attr:`provides`.
:type: type
"""
__IS_DELEGATED__ = True

View File

@ -0,0 +1,31 @@
/* Generated by Cython 0.25.1 */
#ifndef __PYX_HAVE__dependency_injector__providers__utils
#define __PYX_HAVE__dependency_injector__providers__utils
#ifndef __PYX_HAVE_API__dependency_injector__providers__utils
#ifndef __PYX_EXTERN_C
#ifdef __cplusplus
#define __PYX_EXTERN_C extern "C"
#else
#define __PYX_EXTERN_C extern
#endif
#endif
#ifndef DL_IMPORT
#define DL_IMPORT(_T) _T
#endif
__PYX_EXTERN_C DL_IMPORT(PyObject) *__pyx_v_19dependency_injector_9providers_5utils_CLASS_TYPES;
#endif /* !__PYX_HAVE_API__dependency_injector__providers__utils */
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC initutils(void);
#else
PyMODINIT_FUNC PyInit_utils(void);
#endif
#endif /* !__PYX_HAVE__dependency_injector__providers__utils */

View File

@ -3,7 +3,11 @@
Powered by Cython. Powered by Cython.
""" """
cdef public object CLASS_TYPES
cpdef bint is_provider(object instance) cpdef bint is_provider(object instance)
cpdef object ensure_is_provider(object instance) cpdef object ensure_is_provider(object instance)
cpdef bint is_delegated(object instance) cpdef bint is_delegated(object instance)
cpdef str represent_provider(object provider, object provides) cpdef str represent_provider(object provider, object provides)
cpdef object deepcopy(object instance, dict memo=*)

View File

@ -3,23 +3,23 @@
Powered by Cython. Powered by Cython.
""" """
import copy
import sys import sys
import types import types
import threading import threading
from dependency_injector.errors import Error from dependency_injector.errors import Error
GLOBAL_LOCK = threading.RLock()
"""Global reentrant lock.
:type: :py:class:`threading.RLock`
"""
if sys.version_info[0] == 3: # pragma: no cover if sys.version_info[0] == 3: # pragma: no cover
_CLASS_TYPES = (type,) CLASS_TYPES = (type,)
else: # pragma: no cover else: # pragma: no cover
_CLASS_TYPES = (type, types.ClassType) CLASS_TYPES = (type, types.ClassType)
copy._deepcopy_dispatch[types.MethodType] = \
lambda obj, memo: type(obj)(obj.im_func,
copy.deepcopy(obj.im_self, memo),
obj.im_class)
cpdef bint is_provider(object instance): cpdef bint is_provider(object instance):
@ -30,7 +30,7 @@ cpdef bint is_provider(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, _CLASS_TYPES) and return (not isinstance(instance, CLASS_TYPES) and
getattr(instance, '__IS_PROVIDER__', False) is True) getattr(instance, '__IS_PROVIDER__', False) is True)
@ -59,7 +59,7 @@ cpdef bint is_delegated(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, _CLASS_TYPES) and return (not isinstance(instance, CLASS_TYPES) and
getattr(instance, '__IS_DELEGATED__', False) is True) getattr(instance, '__IS_DELEGATED__', False) is True)
@ -80,3 +80,7 @@ cpdef str represent_provider(object provider, object provides):
provider.__class__.__name__)), provider.__class__.__name__)),
provides=repr(provides) if provides is not None else '', provides=repr(provides) if provides is not None else '',
address=hex(id(provider))) address=hex(id(provider)))
cpdef object deepcopy(object instance, dict memo=None):
"""Return full copy of provider or container with providers."""
return copy.deepcopy(instance, memo)

177
tests/performance/test.py Normal file
View File

@ -0,0 +1,177 @@
"""Test providers performance."""
import time
import gc
import dependency_injector.providers
class Tester(object):
"""Performance tester for provider module implementations."""
def __init__(self, provider_modules, duration_factor):
"""Initializer."""
self.provider_modules = provider_modules
self.tests = [getattr(self, name)
for name in dir(self)
if name.startswith('test')]
self.total_time = 0
self.duration_factor = duration_factor
def run(self):
"""Run all tests for all provider modules."""
for module in self.provider_modules:
print('\n')
print('Running tests for module - "{module}":'
.format(module=module.__name__))
gc.disable()
for test in self.tests:
start_time = time.time()
test(module)
self.total_time = time.time() - start_time
print('Test "{test}" took - {time}'
.format(test=test.__name__,
time=self.total_time))
gc.collect()
gc.enable()
print('\n')
# def test_simple_object(self, providers):
# """Test simple object's creation."""
# class Test(object):
# pass
#
# for x in xrange(int(5000000 * self.duration_factor)):
# Test()
#
# def test_simple_object_factory(self, providers):
# """Test simple object's factory."""
# class Test(object):
# pass
#
# test_factory = providers.Factory(Test)
# for x in xrange(int(5000000 * self.duration_factor)):
# test_factory()
#
# def test_3_ctx_positional_injections(self, providers):
# """Test factory with 3 context positional injections."""
# class Test(object):
# def __init__(self, a, b, c):
# pass
#
# for x in xrange(int(5000000 * self.duration_factor)):
# Test(1, 2, 3)
#
# def test_factory_3_ctx_positional_injections(self, providers):
# """Test factory with 3 context positional injections."""
# class Test(object):
# def __init__(self, a, b, c):
# pass
#
# test_factory = providers.Factory(Test)
# for x in xrange(int(5000000 * self.duration_factor)):
# test_factory(1, 2, 3)
def test_3_kw_injections(self, providers):
"""Test 3 keyword argument injections."""
class A(object):
pass
class B(object):
pass
class C(object):
pass
class Test(object):
def __init__(self, a, b, c):
pass
for x in xrange(int(5000000 * self.duration_factor)):
Test(a=A(), b=B(), c=C())
def test_factory_3_factory_kw_injections(self, providers):
"""Test factory with 3 keyword argument injections via factories."""
class A(object):
pass
class B(object):
pass
class C(object):
pass
class Test(object):
def __init__(self, a, b, c):
pass
a_factory = providers.Factory(A)
b_factory = providers.Factory(B)
c_factory = providers.Factory(C)
test_factory = providers.Factory(Test,
a=a_factory,
b=b_factory,
c=c_factory)
for x in xrange(int(5000000 * self.duration_factor)):
test_factory()
def test_factory_subcls_3_factory_subcls_kw_injections(self, providers):
"""Test factory with 3 keyword argument injections via factories."""
class MyFactory(providers.Factory):
pass
class A(object):
pass
class B(object):
pass
class C(object):
pass
class Test(object):
def __init__(self, a, b, c):
pass
a_factory = MyFactory(A)
b_factory = MyFactory(B)
c_factory = MyFactory(C)
test_factory = MyFactory(Test,
a=a_factory,
b=b_factory,
c=c_factory)
for x in xrange(int(5000000 * self.duration_factor)):
test_factory()
# def test_singleton(self, providers):
# """Test factory with 3 keyword argument injections via factories."""
# class Test(object):
# def __init__(self):
# pass
#
# test_factory = providers.Singleton(Test)
# for x in xrange(int(5000000 * self.duration_factor)):
# test_factory()
#
# def test_singleton_subcls(self, providers):
# """Test factory with 3 keyword argument injections via factories."""
# class MySingleton(providers.Singleton):
# pass
#
# class Test(object):
# pass
#
# test_factory = MySingleton(Test)
# for x in xrange(int(5000000 * self.duration_factor)):
# test_factory()
if __name__ == '__main__':
tester = Tester(
provider_modules=[
dependency_injector.providers,
],
duration_factor=1)
tester.run()

View File

@ -80,23 +80,113 @@ class ProviderTests(unittest.TestCase):
self.assertEqual(self.provider.overridden, tuple()) self.assertEqual(self.provider.overridden, tuple())
def test_deepcopy(self):
provider = providers.Provider()
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Provider)
def test_deepcopy_from_memo(self):
provider = providers.Provider()
provider_copy_memo = providers.Provider()
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_overridden(self):
provider = providers.Provider()
overriding_provider = providers.Provider()
provider.override(overriding_provider)
provider_copy = providers.deepcopy(provider)
overriding_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Provider)
self.assertIsNot(overriding_provider, overriding_provider_copy)
self.assertIsInstance(overriding_provider_copy, providers.Provider)
def test_repr(self): def test_repr(self):
self.assertEqual(repr(self.provider), self.assertEqual(repr(self.provider),
'<dependency_injector.providers.base.' '<dependency_injector.providers.base.'
'Provider() at {0}>'.format(hex(id(self.provider)))) 'Provider() at {0}>'.format(hex(id(self.provider))))
class ObjectProviderTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Object(object())))
def test_call_object_provider(self):
obj = object()
self.assertIs(providers.Object(obj)(), obj)
def test_call_overridden_object_provider(self):
obj1 = object()
obj2 = object()
provider = providers.Object(obj1)
provider.override(providers.Object(obj2))
self.assertIs(provider(), obj2)
def test_deepcopy(self):
provider = providers.Object(1)
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Object)
def test_deepcopy_from_memo(self):
provider = providers.Object(1)
provider_copy_memo = providers.Provider()
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_overridden(self):
provider = providers.Object(1)
overriding_provider = providers.Provider()
provider.override(overriding_provider)
provider_copy = providers.deepcopy(provider)
overriding_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.Object)
self.assertIsNot(overriding_provider, overriding_provider_copy)
self.assertIsInstance(overriding_provider_copy, providers.Provider)
def test_repr(self):
some_object = object()
provider = providers.Object(some_object)
self.assertEqual(repr(provider),
'<dependency_injector.providers.base.'
'Object({0}) at {1}>'.format(
repr(some_object),
hex(id(provider))))
class DelegateTests(unittest.TestCase): class DelegateTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.delegated = providers.Provider() self.delegated = providers.Provider()
self.delegate = providers.Delegate(delegated=self.delegated) self.delegate = providers.Delegate(self.delegated)
def test_is_provider(self): def test_is_provider(self):
self.assertTrue(providers.is_provider(self.delegate)) self.assertTrue(providers.is_provider(self.delegate))
def test_init_with_not_provider(self): def test_init_with_not_provider(self):
self.assertRaises(errors.Error, providers.Delegate, delegated=object()) self.assertRaises(errors.Error, providers.Delegate, object())
def test_call(self): def test_call(self):
delegated1 = self.delegate() delegated1 = self.delegate()
@ -119,7 +209,7 @@ class ExternalDependencyTests(unittest.TestCase):
self.provider = providers.ExternalDependency(instance_of=list) self.provider = providers.ExternalDependency(instance_of=list)
def test_init_with_not_class(self): def test_init_with_not_class(self):
self.assertRaises(errors.Error, providers.ExternalDependency, object()) self.assertRaises(TypeError, providers.ExternalDependency, object())
def test_is_provider(self): def test_is_provider(self):
self.assertTrue(providers.is_provider(self.provider)) self.assertTrue(providers.is_provider(self.provider))
@ -135,6 +225,38 @@ class ExternalDependencyTests(unittest.TestCase):
def test_call_not_overridden(self): def test_call_not_overridden(self):
self.assertRaises(errors.Error, self.provider) self.assertRaises(errors.Error, self.provider)
def test_deepcopy(self):
provider = providers.ExternalDependency(int)
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.ExternalDependency)
def test_deepcopy_from_memo(self):
provider = providers.ExternalDependency(int)
provider_copy_memo = providers.Provider()
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_overridden(self):
provider = providers.ExternalDependency(int)
overriding_provider = providers.Provider()
provider.override(overriding_provider)
provider_copy = providers.deepcopy(provider)
overriding_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIsInstance(provider, providers.ExternalDependency)
self.assertIsNot(overriding_provider, overriding_provider_copy)
self.assertIsInstance(overriding_provider_copy, providers.Provider)
def test_repr(self): def test_repr(self):
self.assertEqual(repr(self.provider), self.assertEqual(repr(self.provider),
'<dependency_injector.providers.base.' '<dependency_injector.providers.base.'

View File

@ -1,91 +0,0 @@
"""Dependency injector callable providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
class CallableTests(unittest.TestCase):
def example(self, arg1, arg2, arg3, arg4):
return arg1, arg2, arg3, arg4
def test_init_with_callable(self):
self.assertTrue(providers.Callable(self.example))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.Callable, 123)
def test_call(self):
provider = providers.Callable(lambda: True)
self.assertTrue(provider())
def test_call_with_positional_args(self):
provider = providers.Callable(self.example,
1, 2, 3, 4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_keyword_args(self):
provider = providers.Callable(self.example,
arg1=1, arg2=2, arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_positional_and_keyword_args(self):
provider = providers.Callable(self.example,
1, 2,
arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_context_args(self):
provider = providers.Callable(self.example, 1, 2)
self.assertTupleEqual(provider(3, 4), (1, 2, 3, 4))
def test_call_with_context_kwargs(self):
provider = providers.Callable(self.example, arg1=1)
self.assertTupleEqual(provider(arg2=2, arg3=3, arg4=4), (1, 2, 3, 4))
def test_call_with_context_args_and_kwargs(self):
provider = providers.Callable(self.example, 1)
self.assertTupleEqual(provider(2, arg3=3, arg4=4), (1, 2, 3, 4))
def test_fluent_interface(self):
provider = providers.Singleton(self.example) \
.add_args(1, 2) \
.add_kwargs(arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_overridden(self):
provider = providers.Callable(self.example)
provider.override(providers.Object((4, 3, 2, 1)))
provider.override(providers.Object((1, 2, 3, 4)))
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_repr(self):
provider = providers.Callable(self.example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.callable.'
'Callable({0}) at {1}>'.format(
repr(self.example),
hex(id(provider))))
class DelegatedCallableTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedCallable(len),
providers.Callable)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.DelegatedCallable(len)))
def test_is_delegated_provider(self):
provider = providers.DelegatedCallable(len)
self.assertIs(provider.provide_injection(), provider)

View File

@ -0,0 +1,191 @@
"""Dependency injector callable providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
class CallableTests(unittest.TestCase):
def example(self, arg1, arg2, arg3, arg4):
return arg1, arg2, arg3, arg4
def test_init_with_callable(self):
self.assertTrue(providers.Callable(self.example))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.Callable, 123)
def test_call(self):
provider = providers.Callable(lambda: True)
self.assertTrue(provider())
def test_call_with_positional_args(self):
provider = providers.Callable(self.example,
1, 2, 3, 4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_keyword_args(self):
provider = providers.Callable(self.example,
arg1=1, arg2=2, arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_positional_and_keyword_args(self):
provider = providers.Callable(self.example,
1, 2,
arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_call_with_context_args(self):
provider = providers.Callable(self.example, 1, 2)
self.assertTupleEqual(provider(3, 4), (1, 2, 3, 4))
def test_call_with_context_kwargs(self):
provider = providers.Callable(self.example, arg1=1)
self.assertTupleEqual(provider(arg2=2, arg3=3, arg4=4), (1, 2, 3, 4))
def test_call_with_context_args_and_kwargs(self):
provider = providers.Callable(self.example, 1)
self.assertTupleEqual(provider(2, arg3=3, arg4=4), (1, 2, 3, 4))
def test_fluent_interface(self):
provider = providers.Singleton(self.example) \
.add_args(1, 2) \
.add_kwargs(arg3=3, arg4=4)
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_set_args(self):
provider = providers.Callable(self.example) \
.add_args(1, 2) \
.set_args(3, 4)
self.assertEqual(provider.args, tuple([3, 4]))
def test_set_kwargs(self):
provider = providers.Callable(self.example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.set_kwargs(init_arg3=4, init_arg4=5)
self.assertEqual(provider.kwargs, dict(init_arg3=4, init_arg4=5))
def test_clear_args(self):
provider = providers.Callable(self.example) \
.add_args(1, 2) \
.clear_args()
self.assertEqual(provider.args, tuple())
def test_clear_kwargs(self):
provider = providers.Callable(self.example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.clear_kwargs()
self.assertEqual(provider.kwargs, dict())
def test_call_overridden(self):
provider = providers.Callable(self.example)
provider.override(providers.Object((4, 3, 2, 1)))
provider.override(providers.Object((1, 2, 3, 4)))
self.assertTupleEqual(provider(), (1, 2, 3, 4))
def test_deepcopy(self):
provider = providers.Callable(self.example)
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.provides, provider_copy.provides)
self.assertIsInstance(provider, providers.Callable)
def test_deepcopy_from_memo(self):
provider = providers.Callable(self.example)
provider_copy_memo = providers.Callable(self.example)
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_args(self):
provider = providers.Callable(self.example)
dependent_provider1 = providers.Callable(list)
dependent_provider2 = providers.Callable(dict)
provider.add_args(dependent_provider1, dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.args[0]
dependent_provider_copy2 = provider_copy.args[1]
self.assertNotEqual(provider.args, provider_copy.args)
self.assertIs(dependent_provider1.provides,
dependent_provider_copy1.provides)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.provides,
dependent_provider_copy2.provides)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_kwargs(self):
provider = providers.Callable(self.example)
dependent_provider1 = providers.Callable(list)
dependent_provider2 = providers.Callable(dict)
provider.add_kwargs(a1=dependent_provider1, a2=dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.kwargs['a1']
dependent_provider_copy2 = provider_copy.kwargs['a2']
self.assertNotEqual(provider.kwargs, provider_copy.kwargs)
self.assertIs(dependent_provider1.provides,
dependent_provider_copy1.provides)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.provides,
dependent_provider_copy2.provides)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_overridden(self):
provider = providers.Callable(self.example)
object_provider = providers.Object(object())
provider.override(object_provider)
provider_copy = providers.deepcopy(provider)
object_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.provides, provider_copy.provides)
self.assertIsInstance(provider, providers.Callable)
self.assertIsNot(object_provider, object_provider_copy)
self.assertIsInstance(object_provider_copy, providers.Object)
def test_repr(self):
provider = providers.Callable(self.example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.callables.'
'Callable({0}) at {1}>'.format(
repr(self.example),
hex(id(provider))))
class DelegatedCallableTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedCallable(len),
providers.Callable)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.DelegatedCallable(len)))
def test_is_delegated_provider(self):
provider = providers.DelegatedCallable(len)
self.assertTrue(providers.is_delegated(provider))

View File

@ -1,662 +0,0 @@
"""Dependency injector creational providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
class Example(object):
def __init__(self, init_arg1=None, init_arg2=None, init_arg3=None,
init_arg4=None):
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
self.init_arg3 = init_arg3
self.init_arg4 = init_arg4
self.attribute1 = None
self.attribute2 = None
class FactoryTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Factory(Example)))
def test_init_with_callable(self):
self.assertTrue(providers.Factory(credits))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.Factory, 123)
def test_init_with_valid_provided_type(self):
class ExampleProvider(providers.Factory):
provided_type = Example
example_provider = ExampleProvider(Example, 1, 2)
self.assertIsInstance(example_provider(), Example)
def test_init_with_valid_provided_subtype(self):
class ExampleProvider(providers.Factory):
provided_type = Example
class NewExampe(Example):
pass
example_provider = ExampleProvider(NewExampe, 1, 2)
self.assertIsInstance(example_provider(), NewExampe)
def test_init_with_invalid_provided_type(self):
class ExampleProvider(providers.Factory):
provided_type = Example
with self.assertRaises(errors.Error):
ExampleProvider(list)
def test_call(self):
provider = providers.Factory(Example)
instance1 = provider()
instance2 = provider()
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
provider = providers.Factory(Example, 'i1', 'i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
provider = providers.Factory(Example, init_arg1='i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
provider = providers.Factory(Example, 'i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
provider = providers.Factory(Example)
provider.add_attributes(attribute1='a1', attribute2='a2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
provider = providers.Factory(Example, 11, 22)
instance = provider(33, 44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_call_with_context_kwargs(self):
provider = providers.Factory(Example, init_arg1=1)
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
instance2 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance2.init_arg1, 11)
self.assertEqual(instance2.init_arg2, 22)
def test_call_with_context_args_and_kwargs(self):
provider = providers.Factory(Example, 11)
instance = provider(22, init_arg3=33, init_arg4=44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_fluent_interface(self):
provider = providers.Factory(Example) \
.add_args(1, 2) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.add_attributes(attribute1=5, attribute2=6)
instance = provider()
self.assertEqual(instance.init_arg1, 1)
self.assertEqual(instance.init_arg2, 2)
self.assertEqual(instance.init_arg3, 3)
self.assertEqual(instance.init_arg4, 4)
self.assertEqual(instance.attribute1, 5)
self.assertEqual(instance.attribute2, 6)
def test_call_overridden(self):
provider = providers.Factory(Example)
overriding_provider1 = providers.Factory(dict)
overriding_provider2 = providers.Factory(list)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, list)
self.assertIsInstance(instance2, list)
def test_repr(self):
provider = providers.Factory(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.creational.'
'Factory({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedFactoryTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedFactory(object),
providers.Factory)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.DelegatedFactory(object)))
def test_is_delegated_provider(self):
provider = providers.DelegatedFactory(object)
self.assertIs(provider.provide_injection(), provider)
class SingletonTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Singleton(Example)))
def test_init_with_callable(self):
self.assertTrue(providers.Singleton(credits))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.Singleton, 123)
def test_init_with_valid_provided_type(self):
class ExampleProvider(providers.Singleton):
provided_type = Example
example_provider = ExampleProvider(Example, 1, 2)
self.assertIsInstance(example_provider(), Example)
def test_init_with_valid_provided_subtype(self):
class ExampleProvider(providers.Singleton):
provided_type = Example
class NewExampe(Example):
pass
example_provider = ExampleProvider(NewExampe, 1, 2)
self.assertIsInstance(example_provider(), NewExampe)
def test_init_with_invalid_provided_type(self):
class ExampleProvider(providers.Singleton):
provided_type = Example
with self.assertRaises(errors.Error):
ExampleProvider(list)
def test_call(self):
provider = providers.Singleton(Example)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
provider = providers.Singleton(Example, 'i1', 'i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
provider = providers.Singleton(Example, init_arg1='i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
provider = providers.Singleton(Example, 'i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
provider = providers.Singleton(Example)
provider.add_attributes(attribute1='a1', attribute2='a2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
provider = providers.Singleton(Example)
instance = provider(11, 22)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
def test_call_with_context_kwargs(self):
provider = providers.Singleton(Example, init_arg1=1)
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
# Instance is created earlier
instance1 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
def test_call_with_context_args_and_kwargs(self):
provider = providers.Singleton(Example, 11)
instance = provider(22, init_arg3=33, init_arg4=44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_fluent_interface(self):
provider = providers.Singleton(Example) \
.add_args(1, 2) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.add_attributes(attribute1=5, attribute2=6)
instance = provider()
self.assertEqual(instance.init_arg1, 1)
self.assertEqual(instance.init_arg2, 2)
self.assertEqual(instance.init_arg3, 3)
self.assertEqual(instance.init_arg4, 4)
self.assertEqual(instance.attribute1, 5)
self.assertEqual(instance.attribute2, 6)
def test_call_overridden(self):
provider = providers.Singleton(Example)
overriding_provider1 = providers.Singleton(dict)
overriding_provider2 = providers.Singleton(object)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, object)
self.assertIsInstance(instance2, object)
def test_reset(self):
provider = providers.Singleton(object)
instance1 = provider()
self.assertIsInstance(instance1, object)
provider.reset()
instance2 = provider()
self.assertIsInstance(instance1, object)
self.assertIsNot(instance1, instance2)
def test_repr(self):
provider = providers.Singleton(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.creational.'
'Singleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedSingletonTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedSingleton(object),
providers.Singleton)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.DelegatedSingleton(object)))
def test_is_delegated_provider(self):
provider = providers.DelegatedSingleton(object)
self.assertIs(provider.provide_injection(), provider)
class ThreadLocalSingletonTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.ThreadLocalSingleton(Example)))
def test_init_with_callable(self):
self.assertTrue(providers.ThreadLocalSingleton(credits))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.ThreadLocalSingleton, 123)
def test_init_with_valid_provided_type(self):
class ExampleProvider(providers.ThreadLocalSingleton):
provided_type = Example
example_provider = ExampleProvider(Example, 1, 2)
self.assertIsInstance(example_provider(), Example)
def test_init_with_valid_provided_subtype(self):
class ExampleProvider(providers.ThreadLocalSingleton):
provided_type = Example
class NewExampe(Example):
pass
example_provider = ExampleProvider(NewExampe, 1, 2)
self.assertIsInstance(example_provider(), NewExampe)
def test_init_with_invalid_provided_type(self):
class ExampleProvider(providers.ThreadLocalSingleton):
provided_type = Example
with self.assertRaises(errors.Error):
ExampleProvider(list)
def test_call(self):
provider = providers.ThreadLocalSingleton(Example)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
provider = providers.ThreadLocalSingleton(Example, 'i1', 'i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
provider = providers.ThreadLocalSingleton(Example,
init_arg1='i1',
init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
provider = providers.ThreadLocalSingleton(Example,
'i1',
init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
provider = providers.ThreadLocalSingleton(Example)
provider.add_attributes(attribute1='a1', attribute2='a2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
provider = providers.ThreadLocalSingleton(Example)
instance = provider(11, 22)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
def test_call_with_context_kwargs(self):
provider = providers.ThreadLocalSingleton(Example, init_arg1=1)
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
# Instance is created earlier
instance1 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
def test_call_with_context_args_and_kwargs(self):
provider = providers.ThreadLocalSingleton(Example, 11)
instance = provider(22, init_arg3=33, init_arg4=44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_fluent_interface(self):
provider = providers.ThreadLocalSingleton(Example) \
.add_args(1, 2) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.add_attributes(attribute1=5, attribute2=6)
instance = provider()
self.assertEqual(instance.init_arg1, 1)
self.assertEqual(instance.init_arg2, 2)
self.assertEqual(instance.init_arg3, 3)
self.assertEqual(instance.init_arg4, 4)
self.assertEqual(instance.attribute1, 5)
self.assertEqual(instance.attribute2, 6)
def test_call_overridden(self):
provider = providers.ThreadLocalSingleton(Example)
overriding_provider1 = providers.ThreadLocalSingleton(dict)
overriding_provider2 = providers.ThreadLocalSingleton(object)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, object)
self.assertIsInstance(instance2, object)
def test_reset(self):
provider = providers.ThreadLocalSingleton(object)
instance1 = provider()
self.assertIsInstance(instance1, object)
provider.reset()
instance2 = provider()
self.assertIsInstance(instance1, object)
self.assertIsNot(instance1, instance2)
def test_repr(self):
provider = providers.ThreadLocalSingleton(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.creational.'
'ThreadLocalSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedThreadLocalSingletonTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedThreadLocalSingleton(object),
providers.ThreadLocalSingleton)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(
providers.DelegatedThreadLocalSingleton(object)))
def test_is_delegated_provider(self):
provider = providers.DelegatedThreadLocalSingleton(object)
self.assertIs(provider.provide_injection(), provider)
class FactoryAsDecoratorTests(unittest.TestCase):
def test_decoration_and_overriding(self):
@providers.Factory
class AuthService(object):
pass
@providers.override(AuthService)
@providers.Factory
class ExtAuthService(AuthService.cls):
pass
auth_service = AuthService()
self.assertIsInstance(auth_service, ExtAuthService.cls)

View File

@ -0,0 +1,346 @@
"""Dependency injector factory providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
class Example(object):
def __init__(self, init_arg1=None, init_arg2=None, init_arg3=None,
init_arg4=None):
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
self.init_arg3 = init_arg3
self.init_arg4 = init_arg4
self.attribute1 = None
self.attribute2 = None
class FactoryTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Factory(Example)))
def test_init_with_callable(self):
self.assertTrue(providers.Factory(credits))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, providers.Factory, 123)
def test_init_with_valid_provided_type(self):
class ExampleProvider(providers.Factory):
provided_type = Example
example_provider = ExampleProvider(Example, 1, 2)
self.assertIsInstance(example_provider(), Example)
def test_init_with_valid_provided_subtype(self):
class ExampleProvider(providers.Factory):
provided_type = Example
class NewExampe(Example):
pass
example_provider = ExampleProvider(NewExampe, 1, 2)
self.assertIsInstance(example_provider(), NewExampe)
def test_init_with_invalid_provided_type(self):
class ExampleProvider(providers.Factory):
provided_type = Example
with self.assertRaises(errors.Error):
ExampleProvider(list)
def test_call(self):
provider = providers.Factory(Example)
instance1 = provider()
instance2 = provider()
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
provider = providers.Factory(Example, 'i1', 'i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
provider = providers.Factory(Example, init_arg1='i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
provider = providers.Factory(Example, 'i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
provider = providers.Factory(Example)
provider.add_attributes(attribute1='a1', attribute2='a2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
provider = providers.Factory(Example, 11, 22)
instance = provider(33, 44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_call_with_context_kwargs(self):
provider = providers.Factory(Example, init_arg1=1)
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
instance2 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance2.init_arg1, 11)
self.assertEqual(instance2.init_arg2, 22)
def test_call_with_context_args_and_kwargs(self):
provider = providers.Factory(Example, 11)
instance = provider(22, init_arg3=33, init_arg4=44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_fluent_interface(self):
provider = providers.Factory(Example) \
.add_args(1, 2) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.add_attributes(attribute1=5, attribute2=6)
instance = provider()
self.assertEqual(instance.init_arg1, 1)
self.assertEqual(instance.init_arg2, 2)
self.assertEqual(instance.init_arg3, 3)
self.assertEqual(instance.init_arg4, 4)
self.assertEqual(instance.attribute1, 5)
self.assertEqual(instance.attribute2, 6)
def test_set_args(self):
provider = providers.Factory(Example) \
.add_args(1, 2) \
.set_args(3, 4)
self.assertEqual(provider.args, tuple([3, 4]))
def test_set_kwargs(self):
provider = providers.Factory(Example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.set_kwargs(init_arg3=4, init_arg4=5)
self.assertEqual(provider.kwargs, dict(init_arg3=4, init_arg4=5))
def test_set_attributes(self):
provider = providers.Factory(Example) \
.add_attributes(attribute1=5, attribute2=6) \
.set_attributes(attribute1=6, attribute2=7)
self.assertEqual(provider.attributes, dict(attribute1=6, attribute2=7))
def test_clear_args(self):
provider = providers.Factory(Example) \
.add_args(1, 2) \
.clear_args()
self.assertEqual(provider.args, tuple())
def test_clear_kwargs(self):
provider = providers.Factory(Example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.clear_kwargs()
self.assertEqual(provider.kwargs, dict())
def test_clear_attributes(self):
provider = providers.Factory(Example) \
.add_attributes(attribute1=5, attribute2=6) \
.clear_attributes()
self.assertEqual(provider.attributes, dict())
def test_call_overridden(self):
provider = providers.Factory(Example)
overriding_provider1 = providers.Factory(dict)
overriding_provider2 = providers.Factory(list)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, list)
self.assertIsInstance(instance2, list)
def test_deepcopy(self):
provider = providers.Factory(Example)
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.cls, provider_copy.cls)
self.assertIsInstance(provider, providers.Factory)
def test_deepcopy_from_memo(self):
provider = providers.Factory(Example)
provider_copy_memo = providers.Factory(Example)
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_args(self):
provider = providers.Factory(Example)
dependent_provider1 = providers.Factory(list)
dependent_provider2 = providers.Factory(dict)
provider.add_args(dependent_provider1, dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.args[0]
dependent_provider_copy2 = provider_copy.args[1]
self.assertNotEqual(provider.args, provider_copy.args)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_kwargs(self):
provider = providers.Factory(Example)
dependent_provider1 = providers.Factory(list)
dependent_provider2 = providers.Factory(dict)
provider.add_kwargs(a1=dependent_provider1, a2=dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.kwargs['a1']
dependent_provider_copy2 = provider_copy.kwargs['a2']
self.assertNotEqual(provider.kwargs, provider_copy.kwargs)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_attributes(self):
provider = providers.Factory(Example)
dependent_provider1 = providers.Factory(list)
dependent_provider2 = providers.Factory(dict)
provider.add_attributes(a1=dependent_provider1, a2=dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.attributes['a1']
dependent_provider_copy2 = provider_copy.attributes['a2']
self.assertNotEqual(provider.attributes, provider_copy.attributes)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_overridden(self):
provider = providers.Factory(Example)
object_provider = providers.Object(object())
provider.override(object_provider)
provider_copy = providers.deepcopy(provider)
object_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.cls, provider_copy.cls)
self.assertIsInstance(provider, providers.Factory)
self.assertIsNot(object_provider, object_provider_copy)
self.assertIsInstance(object_provider_copy, providers.Object)
def test_repr(self):
provider = providers.Factory(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.factories.'
'Factory({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedFactoryTests(unittest.TestCase):
def test_inheritance(self):
self.assertIsInstance(providers.DelegatedFactory(object),
providers.Factory)
def test_is_provider(self):
self.assertTrue(
providers.is_provider(providers.DelegatedFactory(object)))
def test_is_delegated_provider(self):
self.assertTrue(
providers.is_delegated(providers.DelegatedFactory(object)))

View File

@ -25,6 +25,33 @@ class PositionalInjectionTests(unittest.TestCase):
self.assertIs(type(obj2), object) self.assertIs(type(obj2), object)
self.assertIsNot(obj1, obj2) self.assertIsNot(obj1, obj2)
def test_get_original_value(self):
provider = providers.Factory(object)
injection = providers.PositionalInjection(provider)
self.assertIs(injection.get_original_value(), provider)
def test_deepcopy(self):
provider = providers.Factory(object)
injection = providers.PositionalInjection(provider)
injection_copy = providers.deepcopy(injection)
self.assertIsNot(injection_copy, injection)
self.assertIsNot(injection_copy.get_original_value(),
injection.get_original_value())
def test_deepcopy_memo(self):
provider = providers.Factory(object)
injection = providers.PositionalInjection(provider)
injection_copy_orig = providers.PositionalInjection(provider)
injection_copy = providers.deepcopy(
injection, {id(injection): injection_copy_orig})
self.assertIs(injection_copy, injection_copy_orig)
self.assertIs(injection_copy.get_original_value(),
injection.get_original_value())
class NamedInjectionTests(unittest.TestCase): class NamedInjectionTests(unittest.TestCase):
@ -50,3 +77,30 @@ class NamedInjectionTests(unittest.TestCase):
self.assertIs(type(obj1), object) self.assertIs(type(obj1), object)
self.assertIs(type(obj2), object) self.assertIs(type(obj2), object)
self.assertIsNot(obj1, obj2) self.assertIsNot(obj1, obj2)
def test_get_original_value(self):
provider = providers.Factory(object)
injection = providers.NamedInjection('name', provider)
self.assertIs(injection.get_original_value(), provider)
def test_deepcopy(self):
provider = providers.Factory(object)
injection = providers.NamedInjection('name', provider)
injection_copy = providers.deepcopy(injection)
self.assertIsNot(injection_copy, injection)
self.assertIsNot(injection_copy.get_original_value(),
injection.get_original_value())
def test_deepcopy_memo(self):
provider = providers.Factory(object)
injection = providers.NamedInjection('name', provider)
injection_copy_orig = providers.NamedInjection('name', provider)
injection_copy = providers.deepcopy(
injection, {id(injection): injection_copy_orig})
self.assertIs(injection_copy, injection_copy_orig)
self.assertIs(injection_copy.get_original_value(),
injection.get_original_value())

View File

@ -0,0 +1,434 @@
"""Dependency injector singleton providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
class Example(object):
def __init__(self, init_arg1=None, init_arg2=None, init_arg3=None,
init_arg4=None):
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
self.init_arg3 = init_arg3
self.init_arg4 = init_arg4
self.attribute1 = None
self.attribute2 = None
class _BaseSingletonTestCase(object):
singleton_cls = None
def test_is_provider(self):
self.assertTrue(providers.is_provider(self.singleton_cls(Example)))
def test_init_with_callable(self):
self.assertTrue(self.singleton_cls(credits))
def test_init_with_not_callable(self):
self.assertRaises(errors.Error, self.singleton_cls, 123)
def test_init_with_valid_provided_type(self):
class ExampleProvider(self.singleton_cls):
provided_type = Example
example_provider = ExampleProvider(Example, 1, 2)
self.assertIsInstance(example_provider(), Example)
def test_init_with_valid_provided_subtype(self):
class ExampleProvider(self.singleton_cls):
provided_type = Example
class NewExampe(Example):
pass
example_provider = ExampleProvider(NewExampe, 1, 2)
self.assertIsInstance(example_provider(), NewExampe)
def test_init_with_invalid_provided_type(self):
class ExampleProvider(self.singleton_cls):
provided_type = Example
with self.assertRaises(errors.Error):
ExampleProvider(list)
def test_call(self):
provider = self.singleton_cls(Example)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
provider = self.singleton_cls(Example, 'i1', 'i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
provider = self.singleton_cls(Example, init_arg1='i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
provider = self.singleton_cls(Example, 'i1', init_arg2='i2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.init_arg1, 'i1')
self.assertEqual(instance1.init_arg2, 'i2')
self.assertEqual(instance2.init_arg1, 'i1')
self.assertEqual(instance2.init_arg2, 'i2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
provider = self.singleton_cls(Example)
provider.add_attributes(attribute1='a1', attribute2='a2')
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
provider = self.singleton_cls(Example)
instance = provider(11, 22)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
def test_call_with_context_kwargs(self):
provider = self.singleton_cls(Example, init_arg1=1)
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
# Instance is created earlier
instance1 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
def test_call_with_context_args_and_kwargs(self):
provider = self.singleton_cls(Example, 11)
instance = provider(22, init_arg3=33, init_arg4=44)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
self.assertEqual(instance.init_arg3, 33)
self.assertEqual(instance.init_arg4, 44)
def test_fluent_interface(self):
provider = self.singleton_cls(Example) \
.add_args(1, 2) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.add_attributes(attribute1=5, attribute2=6)
instance = provider()
self.assertEqual(instance.init_arg1, 1)
self.assertEqual(instance.init_arg2, 2)
self.assertEqual(instance.init_arg3, 3)
self.assertEqual(instance.init_arg4, 4)
self.assertEqual(instance.attribute1, 5)
self.assertEqual(instance.attribute2, 6)
def test_set_args(self):
provider = self.singleton_cls(Example) \
.add_args(1, 2) \
.set_args(3, 4)
self.assertEqual(provider.args, tuple([3, 4]))
def test_set_kwargs(self):
provider = self.singleton_cls(Example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.set_kwargs(init_arg3=4, init_arg4=5)
self.assertEqual(provider.kwargs, dict(init_arg3=4, init_arg4=5))
def test_set_attributes(self):
provider = self.singleton_cls(Example) \
.add_attributes(attribute1=5, attribute2=6) \
.set_attributes(attribute1=6, attribute2=7)
self.assertEqual(provider.attributes, dict(attribute1=6, attribute2=7))
def test_clear_args(self):
provider = self.singleton_cls(Example) \
.add_args(1, 2) \
.clear_args()
self.assertEqual(provider.args, tuple())
def test_clear_kwargs(self):
provider = self.singleton_cls(Example) \
.add_kwargs(init_arg3=3, init_arg4=4) \
.clear_kwargs()
self.assertEqual(provider.kwargs, dict())
def test_clear_attributes(self):
provider = self.singleton_cls(Example) \
.add_attributes(attribute1=5, attribute2=6) \
.clear_attributes()
self.assertEqual(provider.attributes, dict())
def test_call_overridden(self):
provider = self.singleton_cls(Example)
overriding_provider1 = self.singleton_cls(dict)
overriding_provider2 = self.singleton_cls(list)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, list)
self.assertIsInstance(instance2, list)
def test_deepcopy(self):
provider = self.singleton_cls(Example)
provider_copy = providers.deepcopy(provider)
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.cls, provider_copy.cls)
self.assertIsInstance(provider, self.singleton_cls)
def test_deepcopy_from_memo(self):
provider = self.singleton_cls(Example)
provider_copy_memo = self.singleton_cls(Example)
provider_copy = providers.deepcopy(
provider, memo={id(provider): provider_copy_memo})
self.assertIs(provider_copy, provider_copy_memo)
def test_deepcopy_args(self):
provider = self.singleton_cls(Example)
dependent_provider1 = self.singleton_cls(list)
dependent_provider2 = self.singleton_cls(dict)
provider.add_args(dependent_provider1, dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.args[0]
dependent_provider_copy2 = provider_copy.args[1]
self.assertNotEqual(provider.args, provider_copy.args)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_kwargs(self):
provider = self.singleton_cls(Example)
dependent_provider1 = self.singleton_cls(list)
dependent_provider2 = self.singleton_cls(dict)
provider.add_kwargs(a1=dependent_provider1, a2=dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.kwargs['a1']
dependent_provider_copy2 = provider_copy.kwargs['a2']
self.assertNotEqual(provider.kwargs, provider_copy.kwargs)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_attributes(self):
provider = self.singleton_cls(Example)
dependent_provider1 = self.singleton_cls(list)
dependent_provider2 = self.singleton_cls(dict)
provider.add_attributes(a1=dependent_provider1, a2=dependent_provider2)
provider_copy = providers.deepcopy(provider)
dependent_provider_copy1 = provider_copy.attributes['a1']
dependent_provider_copy2 = provider_copy.attributes['a2']
self.assertNotEqual(provider.attributes, provider_copy.attributes)
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
def test_deepcopy_overridden(self):
provider = self.singleton_cls(Example)
object_provider = providers.Object(object())
provider.override(object_provider)
provider_copy = providers.deepcopy(provider)
object_provider_copy = provider_copy.overridden[0]
self.assertIsNot(provider, provider_copy)
self.assertIs(provider.cls, provider_copy.cls)
self.assertIsInstance(provider, self.singleton_cls)
self.assertIsNot(object_provider, object_provider_copy)
self.assertIsInstance(object_provider_copy, providers.Object)
def test_reset(self):
provider = self.singleton_cls(object)
instance1 = provider()
self.assertIsInstance(instance1, object)
provider.reset()
instance2 = provider()
self.assertIsInstance(instance1, object)
self.assertIsNot(instance1, instance2)
class SingletonTests(_BaseSingletonTestCase, unittest.TestCase):
singleton_cls = providers.Singleton
def test_repr(self):
provider = self.singleton_cls(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'Singleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedSingletonTests(_BaseSingletonTestCase, unittest.TestCase):
singleton_cls = providers.DelegatedSingleton
def test_is_delegated_provider(self):
provider = self.singleton_cls(object)
self.assertTrue(providers.is_delegated(provider))
def test_repr(self):
provider = self.singleton_cls(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'DelegatedSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class ThreadLocalSingletonTests(_BaseSingletonTestCase, unittest.TestCase):
singleton_cls = providers.ThreadLocalSingleton
def test_repr(self):
provider = providers.ThreadLocalSingleton(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'ThreadLocalSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedThreadLocalSingletonTests(_BaseSingletonTestCase,
unittest.TestCase):
singleton_cls = providers.DelegatedThreadLocalSingleton
def test_is_delegated_provider(self):
provider = self.singleton_cls(object)
self.assertTrue(providers.is_delegated(provider))
def test_repr(self):
provider = self.singleton_cls(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'DelegatedThreadLocalSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class ThreadSafeSingletonTests(_BaseSingletonTestCase, unittest.TestCase):
singleton_cls = providers.ThreadSafeSingleton
def test_repr(self):
provider = self.singleton_cls(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'ThreadSafeSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))
class DelegatedThreadSafeSingletonTests(_BaseSingletonTestCase,
unittest.TestCase):
singleton_cls = providers.DelegatedThreadSafeSingleton
def test_is_delegated_provider(self):
provider = self.singleton_cls(object)
self.assertTrue(providers.is_delegated(provider))
def test_repr(self):
provider = self.singleton_cls(Example)
self.assertEqual(repr(provider),
'<dependency_injector.providers.singletons.'
'DelegatedThreadSafeSingleton({0}) at {1}>'.format(
repr(Example),
hex(id(provider))))

View File

@ -1,31 +0,0 @@
"""Dependency injector static providers unit tests."""
import unittest2 as unittest
from dependency_injector import providers
class ObjectProviderTests(unittest.TestCase):
def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Object(object())))
def test_call_object_provider(self):
obj = object()
self.assertIs(providers.Object(obj)(), obj)
def test_call_overridden_object_provider(self):
obj1 = object()
obj2 = object()
provider = providers.Object(obj1)
provider.override(providers.Object(obj2))
self.assertIs(provider(), obj2)
def test_repr(self):
some_object = object()
provider = providers.Object(some_object)
self.assertEqual(repr(provider),
'<dependency_injector.providers.base.'
'Object({0}) at {1}>'.format(
repr(some_object),
hex(id(provider))))