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 |     specialization | ||||||
|     overriding |     overriding | ||||||
|     reset_singletons |     reset_singletons | ||||||
|  |     check_dependencies | ||||||
|     traversal |     traversal | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | .. _reset-container-singletons: | ||||||
|  | 
 | ||||||
| Reset container singletons | Reset container singletons | ||||||
| -------------------------- | -------------------------- | ||||||
| 
 | 
 | ||||||
|  | @ -16,4 +18,6 @@ Method ``.reset_singletons()`` also resets singletons in sub-containers: ``provi | ||||||
|    :lines: 3- |    :lines: 3- | ||||||
|    :emphasize-lines: 21 |    :emphasize-lines: 21 | ||||||
| 
 | 
 | ||||||
|  | See also: :ref:`singleton-provider`. | ||||||
|  | 
 | ||||||
| .. disqus:: | .. disqus:: | ||||||
|  |  | ||||||
|  | @ -7,6 +7,16 @@ 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`_ | ||||||
| 
 | 
 | ||||||
|  | 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 | 4.21.0 | ||||||
| ------ | ------ | ||||||
| - Improve ``Dependency`` provider error message: when dependency is undefined, | - Improve ``Dependency`` provider error message: when dependency is undefined, | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | .. _dependency-provider: | ||||||
|  | 
 | ||||||
| Dependency provider | Dependency provider | ||||||
| =================== | =================== | ||||||
| 
 | 
 | ||||||
|  | @ -31,4 +33,6 @@ dependency provider will wrap it into the ``Object`` provider. | ||||||
|    :lines: 16-23 |    :lines: 16-23 | ||||||
|    :emphasize-lines: 3 |    :emphasize-lines: 3 | ||||||
| 
 | 
 | ||||||
|  | See also: :ref:`check-container-dependencies`. | ||||||
|  | 
 | ||||||
| .. disqus:: | .. disqus:: | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | .. _singleton-provider: | ||||||
|  | 
 | ||||||
| Singleton provider | Singleton provider | ||||||
| ================== | ================== | ||||||
| 
 | 
 | ||||||
|  | @ -62,6 +64,8 @@ call ``.full_reset()`` method. | ||||||
|    :lines: 3- |    :lines: 3- | ||||||
|    :emphasize-lines: 25 |    :emphasize-lines: 25 | ||||||
| 
 | 
 | ||||||
|  | See also: :ref:`reset-container-singletons`. | ||||||
|  | 
 | ||||||
| Using singleton with multiple threads | 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.""" | """Top-level package.""" | ||||||
| 
 | 
 | ||||||
| __version__ = '4.21.0' | __version__ = '4.22.0' | ||||||
| """Version number. | """Version number. | ||||||
| 
 | 
 | ||||||
| :type: str | :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 shutdown_resources(self) -> Optional[Awaitable]: ... | ||||||
|     def apply_container_providers_overridings(self) -> None: ... |     def apply_container_providers_overridings(self) -> None: ... | ||||||
|     def reset_singletons(self) -> None: ... |     def reset_singletons(self) -> None: ... | ||||||
|  |     def check_dependencies(self) -> None: ... | ||||||
|     @overload |     @overload | ||||||
|     def resolve_provider_name(self, provider: Provider) -> str: ... |     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||||
|     @classmethod |     @classmethod | ||||||
|  |  | ||||||
|  | @ -305,6 +305,30 @@ class DynamicContainer(Container): | ||||||
|         for provider in self.traverse(types=[providers.BaseSingleton]): |         for provider in self.traverse(types=[providers.BaseSingleton]): | ||||||
|             provider.reset() |             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): |     def resolve_provider_name(self, provider): | ||||||
|         """Try to resolve provider name.""" |         """Try to resolve provider name.""" | ||||||
|         for provider_name, container_provider in self.providers.items(): |         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]: ... |     def instance_of(self) -> Type[T]: ... | ||||||
|     @property |     @property | ||||||
|     def default(self) -> Provider[T]: ... |     def default(self) -> Provider[T]: ... | ||||||
|  |     @property | ||||||
|  |     def is_defined(self) -> bool: ... | ||||||
|     def provided_by(self, provider: Provider) -> OverridingContext: ... |     def provided_by(self, provider: Provider) -> OverridingContext: ... | ||||||
|     @property |     @property | ||||||
|     def parent(self) -> Optional[ProviderParent]: ... |     def parent(self) -> Optional[ProviderParent]: ... | ||||||
|  |  | ||||||
|  | @ -689,7 +689,12 @@ cdef class Dependency(Provider): | ||||||
| 
 | 
 | ||||||
|         :rtype: str |         :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): |     def __repr__(self): | ||||||
|         """Return string representation of provider. |         """Return string representation of provider. | ||||||
|  | @ -708,6 +713,11 @@ cdef class Dependency(Provider): | ||||||
|         """Return default provider.""" |         """Return default provider.""" | ||||||
|         return self.__default |         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): |     def provided_by(self, provider): | ||||||
|         """Set external dependency provider. |         """Set external dependency provider. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -336,6 +336,35 @@ class DeclarativeContainerInstanceTests(unittest.TestCase): | ||||||
|         self.assertIs(obj32, obj42) |         self.assertIs(obj32, obj42) | ||||||
|         self.assertIs(obj33, obj43) |         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): |     def test_assign_parent(self): | ||||||
|         parent = providers.DependenciesContainer() |         parent = providers.DependenciesContainer() | ||||||
|         container = ContainerA() |         container = ContainerA() | ||||||
|  |  | ||||||
|  | @ -352,6 +352,19 @@ class DependencyTests(unittest.TestCase): | ||||||
|         self.assertEqual(provider.default(), {'foo': 'bar'}) |         self.assertEqual(provider.default(), {'foo': 'bar'}) | ||||||
|         self.assertIs(provider.default, default) |         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): |     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) | ||||||
|  | @ -578,6 +591,18 @@ class DependencyTests(unittest.TestCase): | ||||||
|                              repr(list), |                              repr(list), | ||||||
|                              hex(id(self.provider)))) |                              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): | class ExternalDependencyTests(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user