mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-31 07:57:43 +03:00 
			
		
		
		
	Merge branch 'release/4.22.0' into master
This commit is contained in:
		
						commit
						02dea7bce5
					
				
							
								
								
									
										18
									
								
								docs/containers/check_dependencies.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								docs/containers/check_dependencies.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| .. _check-container-dependencies: | ||||
| 
 | ||||
| Check container dependencies | ||||
| ---------------------------- | ||||
| 
 | ||||
| To check container dependencies use method ``.check_dependencies()``. | ||||
| 
 | ||||
| .. literalinclude:: ../../examples/containers/check_dependencies.py | ||||
|    :language: python | ||||
|    :lines: 3- | ||||
|    :emphasize-lines: 12 | ||||
| 
 | ||||
| Method ``.check_dependencies()`` raises an error if container has any undefined dependencies. | ||||
| If all dependencies are provided or have defaults, no error is raised. | ||||
| 
 | ||||
| See also: :ref:`dependency-provider`. | ||||
| 
 | ||||
| .. disqus:: | ||||
|  | @ -24,4 +24,5 @@ Containers module API docs - :py:mod:`dependency_injector.containers`. | |||
|     specialization | ||||
|     overriding | ||||
|     reset_singletons | ||||
|     check_dependencies | ||||
|     traversal | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| .. _reset-container-singletons: | ||||
| 
 | ||||
| Reset container singletons | ||||
| -------------------------- | ||||
| 
 | ||||
|  | @ -16,4 +18,6 @@ Method ``.reset_singletons()`` also resets singletons in sub-containers: ``provi | |||
|    :lines: 3- | ||||
|    :emphasize-lines: 21 | ||||
| 
 | ||||
| See also: :ref:`singleton-provider`. | ||||
| 
 | ||||
| .. disqus:: | ||||
|  |  | |||
|  | @ -7,6 +7,16 @@ that were made in every particular version. | |||
| From version 0.7.6 *Dependency Injector* framework strictly  | ||||
| follows `Semantic versioning`_ | ||||
| 
 | ||||
| 4.22.0 | ||||
| ------ | ||||
| - Add method ``container.check_dependencies()`` to check if all container dependencies | ||||
|   are defined. | ||||
|   See issue: `#383 <https://github.com/ets-labs/python-dependency-injector/issues/383>`_. | ||||
|   Thanks to `@shaunc <https://github.com/shaunc>`_ for suggesting the feature. | ||||
| - Add container name to the representation of the ``Dependency`` provider. | ||||
| - Add docs cross-links between ``Singleton`` provider and "Reset container singletons" | ||||
|   pages. | ||||
| 
 | ||||
| 4.21.0 | ||||
| ------ | ||||
| - Improve ``Dependency`` provider error message: when dependency is undefined, | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| .. _dependency-provider: | ||||
| 
 | ||||
| Dependency provider | ||||
| =================== | ||||
| 
 | ||||
|  | @ -31,4 +33,6 @@ dependency provider will wrap it into the ``Object`` provider. | |||
|    :lines: 16-23 | ||||
|    :emphasize-lines: 3 | ||||
| 
 | ||||
| See also: :ref:`check-container-dependencies`. | ||||
| 
 | ||||
| .. disqus:: | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| .. _singleton-provider: | ||||
| 
 | ||||
| Singleton provider | ||||
| ================== | ||||
| 
 | ||||
|  | @ -62,6 +64,8 @@ call ``.full_reset()`` method. | |||
|    :lines: 3- | ||||
|    :emphasize-lines: 25 | ||||
| 
 | ||||
| See also: :ref:`reset-container-singletons`. | ||||
| 
 | ||||
| Using singleton with multiple threads | ||||
| ------------------------------------- | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										15
									
								
								examples/containers/check_dependencies.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/containers/check_dependencies.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| """Container dependencies check example.""" | ||||
| 
 | ||||
| from dependency_injector import containers, providers | ||||
| 
 | ||||
| 
 | ||||
| class Container(containers.DeclarativeContainer): | ||||
| 
 | ||||
|     service1 = providers.Dependency() | ||||
|     service2 = providers.Dependency() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     container = Container() | ||||
|     container.check_dependencies()  # <-- raises error: | ||||
|     # Container has undefined dependencies: "Container.service1", "Container.service2" | ||||
|  | @ -1,6 +1,6 @@ | |||
| """Top-level package.""" | ||||
| 
 | ||||
| __version__ = '4.21.0' | ||||
| __version__ = '4.22.0' | ||||
| """Version number. | ||||
| 
 | ||||
| :type: str | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -45,6 +45,7 @@ class Container: | |||
|     def shutdown_resources(self) -> Optional[Awaitable]: ... | ||||
|     def apply_container_providers_overridings(self) -> None: ... | ||||
|     def reset_singletons(self) -> None: ... | ||||
|     def check_dependencies(self) -> None: ... | ||||
|     @overload | ||||
|     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||
|     @classmethod | ||||
|  |  | |||
|  | @ -305,6 +305,30 @@ class DynamicContainer(Container): | |||
|         for provider in self.traverse(types=[providers.BaseSingleton]): | ||||
|             provider.reset() | ||||
| 
 | ||||
|     def check_dependencies(self): | ||||
|         """Check if container dependencies are defined. | ||||
| 
 | ||||
|         If any dependency is undefined, raises an error. | ||||
|         """ | ||||
|         undefined = [ | ||||
|             dependency | ||||
|             for dependency in self.traverse(types=[providers.Dependency]) | ||||
|             if not dependency.is_defined | ||||
|         ] | ||||
| 
 | ||||
|         if not undefined: | ||||
|             return | ||||
| 
 | ||||
|         container_name = self.parent_name if self.parent_name else self.__class__.__name__ | ||||
|         undefined_names = [ | ||||
|             f'"{dependency.parent_name if dependency.parent_name else dependency}"' | ||||
|             for dependency in undefined | ||||
|         ] | ||||
|         raise errors.Error( | ||||
|             f'Container "{container_name}" has undefined dependencies: ' | ||||
|             f'{", ".join(undefined_names)}', | ||||
|         ) | ||||
| 
 | ||||
|     def resolve_provider_name(self, provider): | ||||
|         """Try to resolve provider name.""" | ||||
|         for provider_name, container_provider in self.providers.items(): | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -106,6 +106,8 @@ class Dependency(Provider[T]): | |||
|     def instance_of(self) -> Type[T]: ... | ||||
|     @property | ||||
|     def default(self) -> Provider[T]: ... | ||||
|     @property | ||||
|     def is_defined(self) -> bool: ... | ||||
|     def provided_by(self, provider: Provider) -> OverridingContext: ... | ||||
|     @property | ||||
|     def parent(self) -> Optional[ProviderParent]: ... | ||||
|  |  | |||
|  | @ -689,7 +689,12 @@ cdef class Dependency(Provider): | |||
| 
 | ||||
|         :rtype: str | ||||
|         """ | ||||
|         return represent_provider(provider=self, provides=self.__instance_of) | ||||
|         name = f'<{self.__class__.__module__}.{self.__class__.__name__}' | ||||
|         name += f'({repr(self.__instance_of)}) at {hex(id(self))}' | ||||
|         if self.parent_name: | ||||
|             name += f', container name: "{self.parent_name}"' | ||||
|         name += f'>' | ||||
|         return name | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         """Return string representation of provider. | ||||
|  | @ -708,6 +713,11 @@ cdef class Dependency(Provider): | |||
|         """Return default provider.""" | ||||
|         return self.__default | ||||
| 
 | ||||
|     @property | ||||
|     def is_defined(self): | ||||
|         """Return True if dependency is defined.""" | ||||
|         return self.__last_overriding or self.__default is not UNDEFINED | ||||
| 
 | ||||
|     def provided_by(self, provider): | ||||
|         """Set external dependency provider. | ||||
| 
 | ||||
|  |  | |||
|  | @ -336,6 +336,35 @@ class DeclarativeContainerInstanceTests(unittest.TestCase): | |||
|         self.assertIs(obj32, obj42) | ||||
|         self.assertIs(obj33, obj43) | ||||
| 
 | ||||
|     def test_check_dependencies(self): | ||||
|         class SubContainer(containers.DeclarativeContainer): | ||||
|             dependency = providers.Dependency() | ||||
| 
 | ||||
|         class Container(containers.DeclarativeContainer): | ||||
|             dependency = providers.Dependency() | ||||
|             dependencies_container = providers.DependenciesContainer() | ||||
|             provider = providers.List(dependencies_container.dependency) | ||||
|             sub_container = providers.Container(SubContainer) | ||||
| 
 | ||||
|         container = Container() | ||||
| 
 | ||||
|         with self.assertRaises(errors.Error) as context: | ||||
|             container.check_dependencies() | ||||
| 
 | ||||
|         self.assertIn('Container "Container" has undefined dependencies:', str(context.exception)) | ||||
|         self.assertIn('"Container.dependency"', str(context.exception)) | ||||
|         self.assertIn('"Container.dependencies_container.dependency"', str(context.exception)) | ||||
|         self.assertIn('"Container.sub_container.dependency"', str(context.exception)) | ||||
| 
 | ||||
|     def test_check_dependencies_all_defined(self): | ||||
|         class Container(containers.DeclarativeContainer): | ||||
|             dependency = providers.Dependency() | ||||
| 
 | ||||
|         container = Container(dependency='provided') | ||||
|         result = container.check_dependencies() | ||||
| 
 | ||||
|         self.assertIsNone(result) | ||||
| 
 | ||||
|     def test_assign_parent(self): | ||||
|         parent = providers.DependenciesContainer() | ||||
|         container = ContainerA() | ||||
|  |  | |||
|  | @ -352,6 +352,19 @@ class DependencyTests(unittest.TestCase): | |||
|         self.assertEqual(provider.default(), {'foo': 'bar'}) | ||||
|         self.assertIs(provider.default, default) | ||||
| 
 | ||||
|     def test_is_defined(self): | ||||
|         provider = providers.Dependency() | ||||
|         self.assertFalse(provider.is_defined) | ||||
| 
 | ||||
|     def test_is_defined_when_overridden(self): | ||||
|         provider = providers.Dependency() | ||||
|         provider.override('value') | ||||
|         self.assertTrue(provider.is_defined) | ||||
| 
 | ||||
|     def test_is_defined_with_default(self): | ||||
|         provider = providers.Dependency(default='value') | ||||
|         self.assertTrue(provider.is_defined) | ||||
| 
 | ||||
|     def test_call_overridden(self): | ||||
|         self.provider.provided_by(providers.Factory(list)) | ||||
|         self.assertIsInstance(self.provider(), list) | ||||
|  | @ -578,6 +591,18 @@ class DependencyTests(unittest.TestCase): | |||
|                              repr(list), | ||||
|                              hex(id(self.provider)))) | ||||
| 
 | ||||
|     def test_repr_in_container(self): | ||||
|         class Container(containers.DeclarativeContainer): | ||||
|             dependency = providers.Dependency(instance_of=int) | ||||
| 
 | ||||
|         container = Container() | ||||
| 
 | ||||
|         self.assertEqual(repr(container.dependency), | ||||
|                          '<dependency_injector.providers.' | ||||
|                          'Dependency({0}) at {1}, container name: "Container.dependency">'.format( | ||||
|                              repr(int), | ||||
|                              hex(id(container.dependency)))) | ||||
| 
 | ||||
| 
 | ||||
| class ExternalDependencyTests(unittest.TestCase): | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user