mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-31 16:07:51 +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,12 +561,13 @@ cdef class Dependency(Provider): | ||||||
| 
 | 
 | ||||||
|         :rtype: object |         :rtype: object | ||||||
|         """ |         """ | ||||||
|         if self.__last_overriding is None: |         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') |             raise Error('Dependency is not defined') | ||||||
| 
 | 
 | ||||||
|         result = self.__last_overriding(*args, **kwargs) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         if self.is_async_mode_disabled(): |         if self.is_async_mode_disabled(): | ||||||
|             self._check_instance_type(result) |             self._check_instance_type(result) | ||||||
|             return result |             return 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