mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 17:47:02 +03:00
336 Dependency provider default (#382)
* Add implementation and tests * Refactor dependency provider docs * Update docs * Update changelog
This commit is contained in:
parent
1f17bc6e08
commit
478ca18ae3
|
@ -7,6 +7,12 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
|
Development version
|
||||||
|
-------------------
|
||||||
|
- Add ``default`` argument to the dependency provider: ``Dependency(..., default=...)``.
|
||||||
|
See issue: `#336 <https://github.com/ets-labs/python-dependency-injector/issues/336>`_.
|
||||||
|
Many thanks to `Shaun Cutts <https://github.com/shaunc>`_ for providing the use case.
|
||||||
|
|
||||||
4.12.0
|
4.12.0
|
||||||
------
|
------
|
||||||
- Add wiring import hook that auto-wires dynamically imported modules.
|
- Add wiring import hook that auto-wires dynamically imported modules.
|
||||||
|
|
|
@ -3,19 +3,27 @@ Dependency provider
|
||||||
|
|
||||||
.. currentmodule:: dependency_injector.providers
|
.. currentmodule:: dependency_injector.providers
|
||||||
|
|
||||||
:py:class:`Dependency` provider is a placeholder for the dependency of the specified type.
|
:py:class:`Dependency` provider is a placeholder for a dependency of a certain type.
|
||||||
|
|
||||||
The first argument of the ``Dependency`` provider specifies a type of the dependency. It is
|
To specify a type of the dependency use ``instance_of`` argument: ``Dependency(instance_of=SomeClass)``.
|
||||||
called ``instance_of``. ``Dependency`` provider controls the type of the returned object to be an
|
Dependency provider will control that returned object is an instance of ``instance_of`` type.
|
||||||
instance of the ``instance_of`` type.
|
|
||||||
|
|
||||||
The ``Dependency`` provider must be overridden before usage. It can be overridden by any type of
|
|
||||||
the provider. The only rule is that overriding provider must return an instance of ``instance_of``
|
|
||||||
dependency type.
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/dependency.py
|
.. literalinclude:: ../../examples/providers/dependency.py
|
||||||
:language: python
|
:language: python
|
||||||
:lines: 3-
|
:lines: 3-
|
||||||
:emphasize-lines: 26
|
:emphasize-lines: 26,35-36
|
||||||
|
|
||||||
|
To provide a dependency you need to override the ``Dependency`` provider. You can call
|
||||||
|
provider ``.override()`` method or provide an overriding provider when creating a container.
|
||||||
|
See :ref:`provider-overriding`.
|
||||||
|
|
||||||
|
You also can provide a default for the dependency. To provide a default use ``default`` argument:
|
||||||
|
``Dependency(..., default=...)``. Default can be a value or a provider. If default is not a provider,
|
||||||
|
dependency provider will wrap it into the ``Object`` provider.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/providers/dependency_default.py
|
||||||
|
:language: python
|
||||||
|
:lines: 16-23
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
25
examples/providers/dependency_default.py
Normal file
25
examples/providers/dependency_default.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
"""`Dependency` provider default example."""
|
||||||
|
|
||||||
|
import abc
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
class Cache(metaclass=abc.ABCMeta):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class InMemoryCache(Cache):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
cache = providers.Dependency(instance_of=Cache, default=InMemoryCache())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
cache = container.cache() # provides InMemoryCache()
|
||||||
|
|
||||||
|
assert isinstance(cache, InMemoryCache)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -35,6 +35,7 @@ cdef class Delegate(Provider):
|
||||||
|
|
||||||
cdef class Dependency(Provider):
|
cdef class Dependency(Provider):
|
||||||
cdef object __instance_of
|
cdef object __instance_of
|
||||||
|
cdef object __default
|
||||||
|
|
||||||
|
|
||||||
cdef class ExternalDependency(Dependency):
|
cdef class ExternalDependency(Dependency):
|
||||||
|
|
|
@ -82,9 +82,11 @@ class Delegate(Provider[Provider]):
|
||||||
|
|
||||||
|
|
||||||
class Dependency(Provider[T]):
|
class Dependency(Provider[T]):
|
||||||
def __init__(self, instance_of: Type[T] = object) -> None: ...
|
def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ...
|
||||||
@property
|
@property
|
||||||
def instance_of(self) -> Type[T]: ...
|
def instance_of(self) -> Type[T]: ...
|
||||||
|
@property
|
||||||
|
def default(self) -> Provider[T]: ...
|
||||||
def provided_by(self, provider: Provider) -> OverridingContext: ...
|
def provided_by(self, provider: Provider) -> OverridingContext: ...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import sys
|
||||||
import types
|
import types
|
||||||
import threading
|
import threading
|
||||||
import warnings
|
import warnings
|
||||||
import weakref
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -525,7 +524,7 @@ cdef class Dependency(Provider):
|
||||||
:type: type
|
:type: type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, object instance_of=object):
|
def __init__(self, object instance_of=object, default=UNDEFINED):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not isinstance(instance_of, CLASS_TYPES):
|
if not isinstance(instance_of, CLASS_TYPES):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
|
@ -534,8 +533,12 @@ cdef class Dependency(Provider):
|
||||||
instance_of,
|
instance_of,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.__instance_of = instance_of
|
self.__instance_of = instance_of
|
||||||
|
|
||||||
|
if default is not UNDEFINED and not isinstance(default, Provider):
|
||||||
|
default = Object(default)
|
||||||
|
self.__default = default
|
||||||
|
|
||||||
super(Dependency, self).__init__()
|
super(Dependency, self).__init__()
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
|
@ -544,7 +547,8 @@ cdef class Dependency(Provider):
|
||||||
if copied is not None:
|
if copied is not None:
|
||||||
return copied
|
return copied
|
||||||
|
|
||||||
copied = self.__class__(self.__instance_of)
|
copied_default = deepcopy(self.__default, memo) if self.__default is not UNDEFINED else UNDEFINED
|
||||||
|
copied = self.__class__(self.__instance_of, copied_default)
|
||||||
|
|
||||||
self._copy_overridings(copied, memo)
|
self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
@ -557,11 +561,12 @@ cdef class Dependency(Provider):
|
||||||
|
|
||||||
:rtype: object
|
:rtype: object
|
||||||
"""
|
"""
|
||||||
if self.__last_overriding is None:
|
if self.__last_overriding:
|
||||||
raise Error('Dependency is not defined')
|
|
||||||
|
|
||||||
result = self.__last_overriding(*args, **kwargs)
|
result = self.__last_overriding(*args, **kwargs)
|
||||||
|
elif not self.__last_overriding and self.__default is not UNDEFINED:
|
||||||
|
result = self.__default(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
raise Error('Dependency is not defined')
|
||||||
|
|
||||||
if self.is_async_mode_disabled():
|
if self.is_async_mode_disabled():
|
||||||
self._check_instance_type(result)
|
self._check_instance_type(result)
|
||||||
|
@ -609,6 +614,11 @@ cdef class Dependency(Provider):
|
||||||
"""Return class of required dependency."""
|
"""Return class of required dependency."""
|
||||||
return self.__instance_of
|
return self.__instance_of
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default(self):
|
||||||
|
"""Return default provider."""
|
||||||
|
return self.__default
|
||||||
|
|
||||||
def provided_by(self, provider):
|
def provided_by(self, provider):
|
||||||
"""Set external dependency provider.
|
"""Set external dependency provider.
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,25 @@ class DependencyTests(unittest.TestCase):
|
||||||
def test_provided_instance_provider(self):
|
def test_provided_instance_provider(self):
|
||||||
self.assertIsInstance(self.provider.provided, providers.ProvidedInstance)
|
self.assertIsInstance(self.provider.provided, providers.ProvidedInstance)
|
||||||
|
|
||||||
|
def test_default(self):
|
||||||
|
provider = providers.Dependency(instance_of=dict, default={'foo': 'bar'})
|
||||||
|
self.assertEqual(provider(), {'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_default_attribute(self):
|
||||||
|
provider = providers.Dependency(instance_of=dict, default={'foo': 'bar'})
|
||||||
|
self.assertEqual(provider.default(), {'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_default_provider(self):
|
||||||
|
provider = providers.Dependency(instance_of=dict, default=providers.Factory(dict, foo='bar'))
|
||||||
|
self.assertEqual(provider.default(), {'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_default_attribute_provider(self):
|
||||||
|
default = providers.Factory(dict, foo='bar')
|
||||||
|
provider = providers.Dependency(instance_of=dict, default=default)
|
||||||
|
|
||||||
|
self.assertEqual(provider.default(), {'foo': 'bar'})
|
||||||
|
self.assertIs(provider.default, default)
|
||||||
|
|
||||||
def test_call_overridden(self):
|
def test_call_overridden(self):
|
||||||
self.provider.provided_by(providers.Factory(list))
|
self.provider.provided_by(providers.Factory(list))
|
||||||
self.assertIsInstance(self.provider(), list)
|
self.assertIsInstance(self.provider(), list)
|
||||||
|
@ -314,6 +333,62 @@ class DependencyTests(unittest.TestCase):
|
||||||
self.assertIsNot(overriding_provider, overriding_provider_copy)
|
self.assertIsNot(overriding_provider, overriding_provider_copy)
|
||||||
self.assertIsInstance(overriding_provider_copy, providers.Provider)
|
self.assertIsInstance(overriding_provider_copy, providers.Provider)
|
||||||
|
|
||||||
|
def test_deep_copy_default_object(self):
|
||||||
|
default = {'foo': 'bar'}
|
||||||
|
provider = providers.Dependency(dict, default=default)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertIs(provider_copy(), default)
|
||||||
|
self.assertIs(provider_copy.default(), default)
|
||||||
|
|
||||||
|
def test_deep_copy_default_provider(self):
|
||||||
|
bar = object()
|
||||||
|
default = providers.Factory(dict, foo=providers.Object(bar))
|
||||||
|
provider = providers.Dependency(dict, default=default)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertEqual(provider_copy(), {'foo': bar})
|
||||||
|
self.assertEqual(provider_copy.default(), {'foo': bar})
|
||||||
|
self.assertIs(provider_copy()['foo'], bar)
|
||||||
|
|
||||||
|
def test_with_container_default_object(self):
|
||||||
|
default = {'foo': 'bar'}
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
provider = providers.Dependency(dict, default=default)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
self.assertIs(container.provider(), default)
|
||||||
|
self.assertIs(container.provider.default(), default)
|
||||||
|
|
||||||
|
def test_with_container_default_provider(self):
|
||||||
|
bar = object()
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
provider = providers.Dependency(dict, default=providers.Factory(dict, foo=providers.Object(bar)))
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
self.assertEqual(container.provider(), {'foo': bar})
|
||||||
|
self.assertEqual(container.provider.default(), {'foo': bar})
|
||||||
|
self.assertIs(container.provider()['foo'], bar)
|
||||||
|
|
||||||
|
def test_with_container_default_provider_with_overriding(self):
|
||||||
|
bar = object()
|
||||||
|
baz = object()
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
provider = providers.Dependency(dict, default=providers.Factory(dict, foo=providers.Object(bar)))
|
||||||
|
|
||||||
|
container = Container(provider=providers.Factory(dict, foo=providers.Object(baz)))
|
||||||
|
|
||||||
|
self.assertEqual(container.provider(), {'foo': baz})
|
||||||
|
self.assertEqual(container.provider.default(), {'foo': bar})
|
||||||
|
self.assertIs(container.provider()['foo'], baz)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
self.assertEqual(repr(self.provider),
|
self.assertEqual(repr(self.provider),
|
||||||
'<dependency_injector.providers.'
|
'<dependency_injector.providers.'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user