Make providers stable

This commit is contained in:
Roman Mogilatov 2016-11-08 12:46:11 +02:00
parent 322ba98f18
commit 7b61464c93
16 changed files with 531 additions and 368 deletions

View File

@ -63,10 +63,6 @@ setup(name='dependency-injector',
['src/dependency_injector/providers/singletons.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.static',
['src/dependency_injector/providers/static.c'],
define_macros=defined_macros,
extra_compile_args=['-O2']),
Extension('dependency_injector.providers.injections',
['src/dependency_injector/providers/injections.c'],
define_macros=defined_macros,

View File

@ -1,20 +1,12 @@
"""Dependency injector container utils."""
import copy as _copy
import types
import six
from dependency_injector.providers import deepcopy
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):
"""Check if instance is container instance.
@ -74,11 +66,6 @@ def copy(container):
return _decorator
def deepcopy(instance, memo=None):
"""Make full copy of instance."""
return _copy.deepcopy(instance, memo)
def _check_provider_type(cls, provider):
if not isinstance(provider, cls.provider_type):
raise Error('{0} can contain only {1} '

View File

@ -2,6 +2,10 @@
from .base import (
Provider,
Object,
Delegate,
ExternalDependency,
OverridingContext,
)
from .callables import (
Callable,
@ -23,27 +27,26 @@ from .singletons import (
ThreadLocalSingleton,
DelegatedThreadLocalSingleton,
)
from .static import (
Object,
Delegate,
ExternalDependency,
)
from .injections import (
Injection,
PositionalInjection,
NamedInjection,
)
from .utils import (
OverridingContext,
is_provider,
ensure_is_provider,
is_delegated,
represent_provider,
deepcopy,
)
__all__ = (
'Provider',
'Object',
'Delegate',
'ExternalDependency',
'OverridingContext',
'Callable',
'DelegatedCallable',
@ -62,17 +65,13 @@ __all__ = (
'ThreadLocalSingleton',
'DelegatedThreadLocalSingleton',
'Object',
'Delegate',
'ExternalDependency',
'Injection',
'PositionalInjection',
'NamedInjection',
'OverridingContext',
'is_provider',
'ensure_is_provider',
'is_delegated',
'represent_provider',
'deepcopy',
)

View File

@ -10,3 +10,22 @@ cdef class Provider(object):
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

@ -7,15 +7,12 @@ cimport cython
from dependency_injector.errors import Error
from .static cimport (
Object,
Delegate,
)
from .utils cimport (
CLASS_TYPES,
is_provider,
ensure_is_provider,
represent_provider,
OverridingContext,
deepcopy,
)
@ -83,6 +80,19 @@ cdef class Provider(object):
return self._call_last_overriding(args, kwargs)
return self._provide(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__()
for overriding_provider in self.overridden:
copied.override(deepcopy(overriding_provider, memo))
return copied
def __str__(self):
"""Return string representation of provider.
@ -173,3 +183,214 @@ cdef class Provider(object):
return None
return <object>self.__overridden[self.__overridden_len - 1](*args,
**kwargs)
cdef class Object(Provider):
"""Object provider returns provided instance "as is".
.. py:attribute:: provides
Value that have to be provided.
:type: object
"""
def __init__(self, provides):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
self.__provides = provides
super(Object, 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__(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.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.__provides
cdef class Delegate(Object):
"""Delegate provider returns provider "as is".
.. py:attribute:: provides
Value that have to be provided.
:type: object
"""
def __init__(self, provides):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
super(Delegate, self).__init__(ensure_is_provider(provides))
cdef class ExternalDependency(Provider):
""":py:class:`ExternalDependency` provider describes dependency interface.
This provider is used for description of dependency interface. That might
be useful when dependency could be provided in the client's code only,
but it's interface is known. Such situations could happen when required
dependency has non-determenistic list of dependencies itself.
.. code-block:: python
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
database_provider.override(Factory(sqlite3.connect, ':memory:'))
database = database_provider()
.. py:attribute:: instance_of
Class of required dependency.
:type: type
"""
def __init__(self, type instance_of):
"""Initializer."""
self.__instance_of = instance_of
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):
"""Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: object
"""
cdef object instance
if self.__overridden_len == 0:
raise Error('Dependency is not defined')
instance = self._call_last_overriding(args, kwargs)
if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) +
'instance of {0}'.format(self.instance_of))
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):
"""Set external dependency provider.
:param provider: Provider that provides required dependency.
:type provider: :py:class:`Provider`
:rtype: None
"""
return self.override(provider)
cdef class OverridingContext(object):
"""Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
implemeting ``with`` contexts. When :py:class:`OverridingContext` is
closed, overriding that was created in this context is dropped also.
.. code-block:: python
with provider.override(another_provider):
assert provider.overridden
assert not provider.overridden
"""
def __init__(self, Provider overridden, Provider overriding):
"""Initializer.
:param overridden: Overridden provider.
:type overridden: :py:class:`Provider`
:param overriding: Overriding provider.
:type overriding: :py:class:`Provider`
"""
self.__overridden = overridden
self.__overriding = overriding
super(OverridingContext, self).__init__()
def __enter__(self):
"""Do nothing."""
return self.__overriding
def __exit__(self, *_):
"""Exit overriding context."""
self.__overridden.reset_last_overriding()

View File

@ -12,7 +12,10 @@ from .injections cimport (
parse_positional_injections,
parse_named_injections,
)
from .utils cimport represent_provider
from .utils cimport (
represent_provider,
deepcopy,
)
cdef class Callable(Provider):
@ -68,6 +71,21 @@ cdef class Callable(Provider):
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.__provies,
*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.
@ -132,13 +150,13 @@ cdef class Callable(Provider):
def kwargs(self):
"""Return keyword argument injections."""
cdef int index
cdef NamedInjection arg
cdef NamedInjection kwarg
cdef dict kwargs
kwargs = dict()
for index in range(self.__args_len):
arg = self.__args[index]
kwargs[arg.__name] = arg.__value
for index in range(self.__kwargs_len):
kwarg = self.__kwargs[index]
kwargs[kwarg.__name] = kwarg.__value
return kwargs
def add_kwargs(self, **kwargs):

View File

@ -11,7 +11,10 @@ from .injections cimport (
NamedInjection,
parse_named_injections,
)
from .utils cimport represent_provider
from .utils cimport (
represent_provider,
deepcopy,
)
cdef class Factory(Provider):
@ -90,6 +93,22 @@ cdef class Factory(Provider):
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.
@ -185,8 +204,8 @@ cdef class Factory(Provider):
cdef dict attributes
attributes = dict()
for index in range(self.__args_len):
attribute = self.__args[index]
for index in range(self.__attributes_len):
attribute = self.__attributes[index]
attributes[attribute.__name] = attribute.__value
return attributes

View File

@ -6,7 +6,7 @@ Powered by Cython.
cimport cython
cdef class Injection:
cdef class Injection(object):
pass

View File

@ -8,10 +8,11 @@ cimport cython
from .utils cimport (
is_provider,
is_delegated,
deepcopy,
)
cdef class Injection:
cdef class Injection(object):
"""Abstract injection class."""
@ -25,6 +26,14 @@ cdef class PositionalInjection(Injection):
self.__is_delegated = <int>is_delegated(value)
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):
"""Return injection value."""
@ -46,6 +55,15 @@ cdef class NamedInjection(Injection):
self.__is_delegated = <int>is_delegated(value)
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):
"""Return injection value."""

View File

@ -9,7 +9,10 @@ from dependency_injector.errors import Error
from .base cimport Provider
from .factories cimport Factory
from .utils cimport represent_provider
from .utils cimport (
represent_provider,
deepcopy,
)
GLOBAL_LOCK = threading.RLock()
@ -43,7 +46,7 @@ cdef class BaseSingleton(Provider):
self.__instantiator = Factory(provides, *args, **kwargs)
super(Provider, self).__init__()
super(BaseSingleton, self).__init__()
def __str__(self):
"""Return string representation of provider.
@ -53,6 +56,22 @@ cdef class BaseSingleton(Provider):
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."""
@ -178,7 +197,35 @@ cdef class BaseSingleton(Provider):
cdef class Singleton(BaseSingleton):
"""Singleton provider."""
"""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.
@ -208,6 +255,23 @@ cdef class Singleton(BaseSingleton):
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
@ -243,11 +307,43 @@ cdef class ThreadSafeSingleton(BaseSingleton):
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 provider."""
"""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.
@ -277,4 +373,21 @@ cdef class ThreadLocalSingleton(BaseSingleton):
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

@ -1,19 +0,0 @@
"""Dependency injector static providers.
Powered by Cython.
"""
from .base cimport Provider
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

View File

@ -1,165 +0,0 @@
"""Dependency injector static providers.
Powered by Cython.
"""
from dependency_injector.errors import Error
from .base cimport Provider
from .utils cimport (
ensure_is_provider,
represent_provider,
CLASS_TYPES,
)
cdef class Object(Provider):
"""Object provider returns provided instance "as is".
.. py:attribute:: provides
Value that have to be provided.
:type: object
"""
def __init__(self, provides):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
self.__provides = provides
super(Object, self).__init__()
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.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.__provides
cdef class Delegate(Object):
"""Delegate provider returns provider "as is".
.. py:attribute:: provides
Value that have to be provided.
:type: object
"""
def __init__(self, provides):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
"""
super(Delegate, self).__init__(ensure_is_provider(provides))
cdef class ExternalDependency(Provider):
""":py:class:`ExternalDependency` provider describes dependency interface.
This provider is used for description of dependency interface. That might
be useful when dependency could be provided in the client's code only,
but it's interface is known. Such situations could happen when required
dependency has non-determenistic list of dependencies itself.
.. code-block:: python
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
database_provider.override(Factory(sqlite3.connect, ':memory:'))
database = database_provider()
.. py:attribute:: instance_of
Class of required dependency.
:type: type
"""
def __init__(self, type instance_of):
"""Initializer."""
if not isinstance(instance_of, CLASS_TYPES):
raise Error('ExternalDependency provider expects to get class, ' +
'got {0} instead'.format(str(instance_of)))
self.__instance_of = instance_of
super(ExternalDependency, self).__init__()
def __call__(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]
:raise: :py:exc:`dependency_injector.errors.Error`
:rtype: object
"""
cdef object instance
if self.__overridden_len == 0:
raise Error('Dependency is not defined')
instance = self._call_last_overriding(args, kwargs)
if not isinstance(instance, self.instance_of):
raise Error('{0} is not an '.format(instance) +
'instance of {0}'.format(self.instance_of))
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):
"""Set external dependency provider.
:param provider: Provider that provides required dependency.
:type provider: :py:class:`Provider`
:rtype: None
"""
return self.override(provider)

View File

@ -3,18 +3,11 @@
Powered by Cython.
"""
from .base cimport Provider
cdef public object CLASS_TYPES
cdef class OverridingContext(object):
cdef Provider __overridden
cdef Provider __overriding
cpdef bint is_provider(object instance)
cpdef object ensure_is_provider(object instance)
cpdef bint is_delegated(object instance)
cpdef str represent_provider(object provider, object provides)
cpdef object deepcopy(object instance, dict memo=*)

View File

@ -3,55 +3,23 @@
Powered by Cython.
"""
import copy
import sys
import types
import threading
from dependency_injector.errors import Error
from .base cimport Provider
if sys.version_info[0] == 3: # pragma: no cover
CLASS_TYPES = (type,)
else: # pragma: no cover
CLASS_TYPES = (type, types.ClassType)
cdef class OverridingContext(object):
"""Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
implemeting ``with`` contexts. When :py:class:`OverridingContext` is
closed, overriding that was created in this context is dropped also.
.. code-block:: python
with provider.override(another_provider):
assert provider.overridden
assert not provider.overridden
"""
def __init__(self, Provider overridden, Provider overriding):
"""Initializer.
:param overridden: Overridden provider.
:type overridden: :py:class:`Provider`
:param overriding: Overriding provider.
:type overriding: :py:class:`Provider`
"""
self.__overridden = overridden
self.__overriding = overriding
def __enter__(self):
"""Do nothing."""
return self.__overriding
def __exit__(self, *_):
"""Exit overriding context."""
self.__overridden.reset_last_overriding()
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):
@ -112,3 +80,7 @@ cpdef str represent_provider(object provider, object provides):
provider.__class__.__name__)),
provides=repr(provides) if provides is not None else '',
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)

View File

@ -84,3 +84,86 @@ class ProviderTests(unittest.TestCase):
self.assertEqual(repr(self.provider),
'<dependency_injector.providers.base.'
'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_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):
def setUp(self):
self.delegated = providers.Provider()
self.delegate = providers.Delegate(self.delegated)
def test_is_provider(self):
self.assertTrue(providers.is_provider(self.delegate))
def test_init_with_not_provider(self):
self.assertRaises(errors.Error, providers.Delegate, object())
def test_call(self):
delegated1 = self.delegate()
delegated2 = self.delegate()
self.assertIs(delegated1, self.delegated)
self.assertIs(delegated2, self.delegated)
def test_repr(self):
self.assertEqual(repr(self.delegate),
'<dependency_injector.providers.base.'
'Delegate({0}) at {1}>'.format(
repr(self.delegated),
hex(id(self.delegate))))
class ExternalDependencyTests(unittest.TestCase):
def setUp(self):
self.provider = providers.ExternalDependency(instance_of=list)
def test_init_with_not_class(self):
self.assertRaises(TypeError, providers.ExternalDependency, object())
def test_is_provider(self):
self.assertTrue(providers.is_provider(self.provider))
def test_call_overridden(self):
self.provider.provided_by(providers.Factory(list))
self.assertIsInstance(self.provider(), list)
def test_call_overridden_but_not_instance_of(self):
self.provider.provided_by(providers.Factory(dict))
self.assertRaises(errors.Error, self.provider)
def test_call_not_overridden(self):
self.assertRaises(errors.Error, self.provider)
def test_repr(self):
self.assertEqual(repr(self.provider),
'<dependency_injector.providers.base.'
'ExternalDependency({0}) at {1}>'.format(
repr(list),
hex(id(self.provider))))

View File

@ -1,91 +0,0 @@
"""Dependency injector static providers unit tests."""
import unittest2 as unittest
from dependency_injector import (
providers,
errors,
)
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.static.'
'Object({0}) at {1}>'.format(
repr(some_object),
hex(id(provider))))
class DelegateTests(unittest.TestCase):
def setUp(self):
self.delegated = providers.Provider()
self.delegate = providers.Delegate(self.delegated)
def test_is_provider(self):
self.assertTrue(providers.is_provider(self.delegate))
def test_init_with_not_provider(self):
self.assertRaises(errors.Error, providers.Delegate, object())
def test_call(self):
delegated1 = self.delegate()
delegated2 = self.delegate()
self.assertIs(delegated1, self.delegated)
self.assertIs(delegated2, self.delegated)
def test_repr(self):
self.assertEqual(repr(self.delegate),
'<dependency_injector.providers.static.'
'Delegate({0}) at {1}>'.format(
repr(self.delegated),
hex(id(self.delegate))))
class ExternalDependencyTests(unittest.TestCase):
def setUp(self):
self.provider = providers.ExternalDependency(instance_of=list)
def test_init_with_not_class(self):
self.assertRaises(TypeError, providers.ExternalDependency, object())
def test_is_provider(self):
self.assertTrue(providers.is_provider(self.provider))
def test_call_overridden(self):
self.provider.provided_by(providers.Factory(list))
self.assertIsInstance(self.provider(), list)
def test_call_overridden_but_not_instance_of(self):
self.provider.provided_by(providers.Factory(dict))
self.assertRaises(errors.Error, self.provider)
def test_call_not_overridden(self):
self.assertRaises(errors.Error, self.provider)
def test_repr(self):
self.assertEqual(repr(self.provider),
'<dependency_injector.providers.static.'
'ExternalDependency({0}) at {1}>'.format(
repr(list),
hex(id(self.provider))))