mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-30 23:47:40 +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  | ||||
| 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 | ||||
| ------ | ||||
| - Add wiring import hook that auto-wires dynamically imported modules. | ||||
|  |  | |||
|  | @ -3,19 +3,27 @@ Dependency provider | |||
| 
 | ||||
| .. 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 | ||||
| called ``instance_of``. ``Dependency`` provider controls the type of the returned object to be an | ||||
| 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. | ||||
| To specify a type of the dependency use ``instance_of`` argument: ``Dependency(instance_of=SomeClass)``. | ||||
| Dependency provider will control that returned object is an instance of ``instance_of`` type. | ||||
| 
 | ||||
| .. literalinclude:: ../../examples/providers/dependency.py | ||||
|    :language: python | ||||
|    :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:: | ||||
|  |  | |||
							
								
								
									
										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 object __instance_of | ||||
|     cdef object __default | ||||
| 
 | ||||
| 
 | ||||
| cdef class ExternalDependency(Dependency): | ||||
|  |  | |||
|  | @ -82,9 +82,11 @@ class Delegate(Provider[Provider]): | |||
| 
 | ||||
| 
 | ||||
| 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 | ||||
|     def instance_of(self) -> Type[T]: ... | ||||
|     @property | ||||
|     def default(self) -> Provider[T]: ... | ||||
|     def provided_by(self, provider: Provider) -> OverridingContext: ... | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ import sys | |||
| import types | ||||
| import threading | ||||
| import warnings | ||||
| import weakref | ||||
| 
 | ||||
| try: | ||||
|     import asyncio | ||||
|  | @ -525,7 +524,7 @@ cdef class Dependency(Provider): | |||
|         :type: type | ||||
|    """ | ||||
| 
 | ||||
|     def __init__(self, object instance_of=object): | ||||
|     def __init__(self, object instance_of=object, default=UNDEFINED): | ||||
|         """Initializer.""" | ||||
|         if not isinstance(instance_of, CLASS_TYPES): | ||||
|             raise TypeError( | ||||
|  | @ -534,8 +533,12 @@ cdef class Dependency(Provider): | |||
|                     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__() | ||||
| 
 | ||||
|     def __deepcopy__(self, memo): | ||||
|  | @ -544,7 +547,8 @@ cdef class Dependency(Provider): | |||
|         if copied is not None: | ||||
|             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) | ||||
| 
 | ||||
|  | @ -557,11 +561,12 @@ cdef class Dependency(Provider): | |||
| 
 | ||||
|         :rtype: object | ||||
|         """ | ||||
|         if self.__last_overriding is None: | ||||
|             raise Error('Dependency is not defined') | ||||
| 
 | ||||
|         if self.__last_overriding: | ||||
|             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(): | ||||
|             self._check_instance_type(result) | ||||
|  | @ -609,6 +614,11 @@ cdef class Dependency(Provider): | |||
|         """Return class of required dependency.""" | ||||
|         return self.__instance_of | ||||
| 
 | ||||
|     @property | ||||
|     def default(self): | ||||
|         """Return default provider.""" | ||||
|         return self.__default | ||||
| 
 | ||||
|     def provided_by(self, provider): | ||||
|         """Set external dependency provider. | ||||
| 
 | ||||
|  |  | |||
|  | @ -271,6 +271,25 @@ class DependencyTests(unittest.TestCase): | |||
|     def test_provided_instance_provider(self): | ||||
|         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): | ||||
|         self.provider.provided_by(providers.Factory(list)) | ||||
|         self.assertIsInstance(self.provider(), list) | ||||
|  | @ -314,6 +333,62 @@ class DependencyTests(unittest.TestCase): | |||
|         self.assertIsNot(overriding_provider, overriding_provider_copy) | ||||
|         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): | ||||
|         self.assertEqual(repr(self.provider), | ||||
|                          '<dependency_injector.providers.' | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user