mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-31 07:57:43 +03:00 
			
		
		
		
	Merge branch 'release/4.21.0' into master
This commit is contained in:
		
						commit
						2127e3cef9
					
				|  | @ -7,6 +7,11 @@ 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.21.0 | ||||||
|  | ------ | ||||||
|  | - Improve ``Dependency`` provider error message: when dependency is undefined, | ||||||
|  |   error message contains its name. | ||||||
|  | 
 | ||||||
| 4.20.2 | 4.20.2 | ||||||
| ------ | ------ | ||||||
| - Move docs on container "self" injections to "Providers" section. | - Move docs on container "self" injections to "Providers" section. | ||||||
|  |  | ||||||
|  | @ -15,7 +15,12 @@ Dependency provider will control that returned object is an instance of ``instan | ||||||
| 
 | 
 | ||||||
| To provide a dependency you need to override the ``Dependency`` provider. You can call | 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. | provider ``.override()`` method or provide an overriding provider when creating a container. | ||||||
| See :ref:`provider-overriding`. | See :ref:`provider-overriding`. If you don't provide a dependency, ``Dependency`` provider | ||||||
|  | will raise an error: | ||||||
|  | 
 | ||||||
|  | .. literalinclude:: ../../examples/providers/dependency_undefined_error.py | ||||||
|  |    :language: python | ||||||
|  |    :lines: 18- | ||||||
| 
 | 
 | ||||||
| You also can provide a default for the dependency. To provide a default use ``default`` argument: | 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(..., default=...)``. Default can be a value or a provider. If default is not a provider, | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| import abc | import abc | ||||||
| import dataclasses | import dataclasses | ||||||
| 
 | 
 | ||||||
| from dependency_injector import containers, providers, errors | from dependency_injector import containers, providers | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DbAdapter(metaclass=abc.ABCMeta): | class DbAdapter(metaclass=abc.ABCMeta): | ||||||
|  | @ -41,10 +41,5 @@ if __name__ == '__main__': | ||||||
|     assert isinstance(container2.user_service().database, PostgresDbAdapter) |     assert isinstance(container2.user_service().database, PostgresDbAdapter) | ||||||
| 
 | 
 | ||||||
|     container3 = Container(database=providers.Singleton(object)) |     container3 = Container(database=providers.Singleton(object)) | ||||||
|     try: |     container3.user_service()  # <-- raises error: | ||||||
|         container3.user_service() |     # <object ...> is not an instance of DbAdapter | ||||||
|     except errors.Error as exception: |  | ||||||
|         print(exception) |  | ||||||
|         # The output is: |  | ||||||
|         # <object object at 0x107ce5c40> is not an |  | ||||||
|         # instance of <class '__main__.DbAdapter'> |  | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								examples/providers/dependency_undefined_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/providers/dependency_undefined_error.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | """`Dependency` provider undefined error example.""" | ||||||
|  | 
 | ||||||
|  | import abc | ||||||
|  | import dataclasses | ||||||
|  | 
 | ||||||
|  | from dependency_injector import containers, providers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DbAdapter(metaclass=abc.ABCMeta): | ||||||
|  |     ... | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @dataclasses.dataclass | ||||||
|  | class UserService: | ||||||
|  |     database: DbAdapter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Container(containers.DeclarativeContainer): | ||||||
|  | 
 | ||||||
|  |     database = providers.Dependency(instance_of=DbAdapter) | ||||||
|  | 
 | ||||||
|  |     user_service = providers.Factory( | ||||||
|  |         UserService, | ||||||
|  |         database=database, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     container = Container() | ||||||
|  |     container.user_service()  # <-- raises error: | ||||||
|  |     # Dependency "Container.database" is not defined | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| """Top-level package.""" | """Top-level package.""" | ||||||
| 
 | 
 | ||||||
| __version__ = '4.20.2' | __version__ = '4.21.0' | ||||||
| """Version number. | """Version number. | ||||||
| 
 | 
 | ||||||
| :type: str | :type: str | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -8,12 +8,13 @@ from typing import ( | ||||||
|     ClassVar, |     ClassVar, | ||||||
|     Callable as _Callable, |     Callable as _Callable, | ||||||
|     Iterable, |     Iterable, | ||||||
|  |     Iterator, | ||||||
|     TypeVar, |     TypeVar, | ||||||
|     Awaitable, |     Awaitable, | ||||||
|     overload, |     overload, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| from .providers import Provider, Self | from .providers import Provider, Self, ProviderParent | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| C_Base = TypeVar('C_Base', bound='Container') | C_Base = TypeVar('C_Base', bound='Container') | ||||||
|  | @ -38,7 +39,6 @@ class Container: | ||||||
|     def override_providers(self, **overriding_providers: Provider) -> None: ... |     def override_providers(self, **overriding_providers: Provider) -> None: ... | ||||||
|     def reset_last_overriding(self) -> None: ... |     def reset_last_overriding(self) -> None: ... | ||||||
|     def reset_override(self) -> None: ... |     def reset_override(self) -> None: ... | ||||||
|     def resolve_provider_name(self, provider_to_resolve: Provider) -> Optional[str]: ... |  | ||||||
|     def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> None: ... |     def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> None: ... | ||||||
|     def unwire(self) -> None: ... |     def unwire(self) -> None: ... | ||||||
|     def init_resources(self) -> Optional[Awaitable]: ... |     def init_resources(self) -> Optional[Awaitable]: ... | ||||||
|  | @ -46,10 +46,20 @@ class Container: | ||||||
|     def apply_container_providers_overridings(self) -> None: ... |     def apply_container_providers_overridings(self) -> None: ... | ||||||
|     def reset_singletons(self) -> None: ... |     def reset_singletons(self) -> None: ... | ||||||
|     @overload |     @overload | ||||||
|     def traverse(self, types: Optional[Iterable[Type[TT]]] = None) -> _Iterator[TT]: ... |     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||||
|     @classmethod |     @classmethod | ||||||
|     @overload |     @overload | ||||||
|     def traverse(self, types: Optional[Iterable[Type[TT]]] = None) -> _Iterator[TT]: ... |     def resolve_provider_name(cls, provider: Provider) -> str: ... | ||||||
|  |     @property | ||||||
|  |     def parent(self) -> Optional[ProviderParent]: ... | ||||||
|  |     @property | ||||||
|  |     def parent_name(self) -> Optional[str]: ... | ||||||
|  |     def assign_parent(self, parent: ProviderParent) -> None: ... | ||||||
|  |     @overload | ||||||
|  |     def traverse(self, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ... | ||||||
|  |     @classmethod | ||||||
|  |     @overload | ||||||
|  |     def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DynamicContainer(Container): ... | class DynamicContainer(Container): ... | ||||||
|  |  | ||||||
|  | @ -66,6 +66,7 @@ class DynamicContainer(Container): | ||||||
|         self.provider_type = providers.Provider |         self.provider_type = providers.Provider | ||||||
|         self.providers = {} |         self.providers = {} | ||||||
|         self.overridden = tuple() |         self.overridden = tuple() | ||||||
|  |         self.parent = None | ||||||
|         self.declarative_parent = None |         self.declarative_parent = None | ||||||
|         self.wired_to_modules = [] |         self.wired_to_modules = [] | ||||||
|         self.wired_to_packages = [] |         self.wired_to_packages = [] | ||||||
|  | @ -92,6 +93,8 @@ class DynamicContainer(Container): | ||||||
|         for name, provider in providers.deepcopy(self.providers, memo).items(): |         for name, provider in providers.deepcopy(self.providers, memo).items(): | ||||||
|             copied.set_provider(name, provider) |             copied.set_provider(name, provider) | ||||||
| 
 | 
 | ||||||
|  |         copied.parent = providers.deepcopy(self.parent, memo) | ||||||
|  | 
 | ||||||
|         return copied |         return copied | ||||||
| 
 | 
 | ||||||
|     def __setattr__(self, str name, object value): |     def __setattr__(self, str name, object value): | ||||||
|  | @ -108,9 +111,16 @@ class DynamicContainer(Container): | ||||||
| 
 | 
 | ||||||
|         :rtype: None |         :rtype: None | ||||||
|         """ |         """ | ||||||
|         if isinstance(value, providers.Provider) and not isinstance(value, providers.Self): |         if isinstance(value, providers.Provider) \ | ||||||
|  |                 and not isinstance(value, providers.Self) \ | ||||||
|  |                 and name != 'parent': | ||||||
|             _check_provider_type(self, value) |             _check_provider_type(self, value) | ||||||
|  | 
 | ||||||
|             self.providers[name] = value |             self.providers[name] = value | ||||||
|  | 
 | ||||||
|  |             if isinstance(value, providers.CHILD_PROVIDERS): | ||||||
|  |                 value.assign_parent(self) | ||||||
|  | 
 | ||||||
|         super(DynamicContainer, self).__setattr__(name, value) |         super(DynamicContainer, self).__setattr__(name, value) | ||||||
| 
 | 
 | ||||||
|     def __delattr__(self, str name): |     def __delattr__(self, str name): | ||||||
|  | @ -295,6 +305,29 @@ class DynamicContainer(Container): | ||||||
|         for provider in self.traverse(types=[providers.BaseSingleton]): |         for provider in self.traverse(types=[providers.BaseSingleton]): | ||||||
|             provider.reset() |             provider.reset() | ||||||
| 
 | 
 | ||||||
|  |     def resolve_provider_name(self, provider): | ||||||
|  |         """Try to resolve provider name.""" | ||||||
|  |         for provider_name, container_provider in self.providers.items(): | ||||||
|  |             if container_provider is provider: | ||||||
|  |                 return provider_name | ||||||
|  |         else: | ||||||
|  |             raise errors.Error(f'Can not resolve name for provider "{provider}"') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent_name(self): | ||||||
|  |         """Return parent name.""" | ||||||
|  |         if self.parent: | ||||||
|  |             return self.parent.parent_name | ||||||
|  | 
 | ||||||
|  |         if self.declarative_parent: | ||||||
|  |             return self.declarative_parent.__name__ | ||||||
|  | 
 | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     def assign_parent(self, parent): | ||||||
|  |         """Assign parent.""" | ||||||
|  |         self.parent = parent | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class DeclarativeContainerMetaClass(type): | class DeclarativeContainerMetaClass(type): | ||||||
|     """Declarative inversion of control container meta class.""" |     """Declarative inversion of control container meta class.""" | ||||||
|  | @ -341,6 +374,10 @@ class DeclarativeContainerMetaClass(type): | ||||||
|         for provider in six.itervalues(cls.providers): |         for provider in six.itervalues(cls.providers): | ||||||
|             _check_provider_type(cls, provider) |             _check_provider_type(cls, provider) | ||||||
| 
 | 
 | ||||||
|  |         for provider in six.itervalues(cls.cls_providers): | ||||||
|  |             if isinstance(provider, providers.CHILD_PROVIDERS): | ||||||
|  |                 provider.assign_parent(cls) | ||||||
|  | 
 | ||||||
|         return cls |         return cls | ||||||
| 
 | 
 | ||||||
|     def __setattr__(cls, str name, object value): |     def __setattr__(cls, str name, object value): | ||||||
|  | @ -359,6 +396,10 @@ class DeclarativeContainerMetaClass(type): | ||||||
|         """ |         """ | ||||||
|         if isinstance(value, providers.Provider) and name != '__self__': |         if isinstance(value, providers.Provider) and name != '__self__': | ||||||
|             _check_provider_type(cls, value) |             _check_provider_type(cls, value) | ||||||
|  | 
 | ||||||
|  |             if isinstance(value, providers.CHILD_PROVIDERS): | ||||||
|  |                 value.assign_parent(cls) | ||||||
|  | 
 | ||||||
|             cls.providers[name] = value |             cls.providers[name] = value | ||||||
|             cls.cls_providers[name] = value |             cls.cls_providers[name] = value | ||||||
|         super(DeclarativeContainerMetaClass, cls).__setattr__(name, value) |         super(DeclarativeContainerMetaClass, cls).__setattr__(name, value) | ||||||
|  | @ -399,6 +440,19 @@ class DeclarativeContainerMetaClass(type): | ||||||
|         """Return providers traversal generator.""" |         """Return providers traversal generator.""" | ||||||
|         yield from providers.traverse(*cls.providers.values(), types=types) |         yield from providers.traverse(*cls.providers.values(), types=types) | ||||||
| 
 | 
 | ||||||
|  |     def resolve_provider_name(cls, provider): | ||||||
|  |         """Try to resolve provider name.""" | ||||||
|  |         for provider_name, container_provider in cls.providers.items(): | ||||||
|  |             if container_provider is provider: | ||||||
|  |                 return provider_name | ||||||
|  |         else: | ||||||
|  |             raise errors.Error(f'Can not resolve name for provider "{provider}"') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent_name(cls): | ||||||
|  |         """Return parent name.""" | ||||||
|  |         return cls.__name__ | ||||||
|  | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def __fetch_self(attributes): |     def __fetch_self(attributes): | ||||||
|         self = None |         self = None | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -41,6 +41,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 object __default | ||||||
|  |     cdef object __parent | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| cdef class ExternalDependency(Dependency): | cdef class ExternalDependency(Dependency): | ||||||
|  | @ -49,6 +50,7 @@ cdef class ExternalDependency(Dependency): | ||||||
| 
 | 
 | ||||||
| cdef class DependenciesContainer(Object): | cdef class DependenciesContainer(Object): | ||||||
|     cdef dict __providers |     cdef dict __providers | ||||||
|  |     cdef object __parent | ||||||
| 
 | 
 | ||||||
|     cpdef object _override_providers(self, object container) |     cpdef object _override_providers(self, object container) | ||||||
| 
 | 
 | ||||||
|  | @ -225,6 +227,7 @@ cdef class Container(Provider): | ||||||
|     cdef object __container_cls |     cdef object __container_cls | ||||||
|     cdef dict __overriding_providers |     cdef dict __overriding_providers | ||||||
|     cdef object __container |     cdef object __container | ||||||
|  |     cdef object __parent | ||||||
| 
 | 
 | ||||||
|     cpdef object _provide(self, tuple args, dict kwargs) |     cpdef object _provide(self, tuple args, dict kwargs) | ||||||
| 
 | 
 | ||||||
|  | @ -305,6 +308,12 @@ cpdef bint is_delegated(object instance) | ||||||
| cpdef str represent_provider(object provider, object provides) | cpdef str represent_provider(object provider, object provides) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | cpdef bint is_container_instance(object instance) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cpdef bint is_container_class(object instance) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| cpdef object deepcopy(object instance, dict memo=*) | cpdef object deepcopy(object instance, dict memo=*) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ from . import resources | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Injection = Any | Injection = Any | ||||||
|  | ProviderParent = Union['Provider', Any] | ||||||
| T = TypeVar('T') | T = TypeVar('T') | ||||||
| TT = TypeVar('TT') | TT = TypeVar('TT') | ||||||
| 
 | 
 | ||||||
|  | @ -106,6 +107,11 @@ class Dependency(Provider[T]): | ||||||
|     @property |     @property | ||||||
|     def default(self) -> Provider[T]: ... |     def default(self) -> Provider[T]: ... | ||||||
|     def provided_by(self, provider: Provider) -> OverridingContext: ... |     def provided_by(self, provider: Provider) -> OverridingContext: ... | ||||||
|  |     @property | ||||||
|  |     def parent(self) -> Optional[ProviderParent]: ... | ||||||
|  |     @property | ||||||
|  |     def parent_name(self) -> Optional[str]: ... | ||||||
|  |     def assign_parent(self, parent: ProviderParent) -> None: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ExternalDependency(Dependency[T]): ... | class ExternalDependency(Dependency[T]): ... | ||||||
|  | @ -116,6 +122,12 @@ class DependenciesContainer(Object): | ||||||
|     def __getattr__(self, name: str) -> Provider: ... |     def __getattr__(self, name: str) -> Provider: ... | ||||||
|     @property |     @property | ||||||
|     def providers(self) -> _Dict[str, Provider]: ... |     def providers(self) -> _Dict[str, Provider]: ... | ||||||
|  |     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||||
|  |     @property | ||||||
|  |     def parent(self) -> Optional[ProviderParent]: ... | ||||||
|  |     @property | ||||||
|  |     def parent_name(self) -> Optional[str]: ... | ||||||
|  |     def assign_parent(self, parent: ProviderParent) -> None: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Callable(Provider[T]): | class Callable(Provider[T]): | ||||||
|  | @ -358,6 +370,12 @@ class Container(Provider[T]): | ||||||
|     def __getattr__(self, name: str) -> Provider: ... |     def __getattr__(self, name: str) -> Provider: ... | ||||||
|     @property |     @property | ||||||
|     def container(self) -> T: ... |     def container(self) -> T: ... | ||||||
|  |     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||||
|  |     @property | ||||||
|  |     def parent(self) -> Optional[ProviderParent]: ... | ||||||
|  |     @property | ||||||
|  |     def parent_name(self) -> Optional[str]: ... | ||||||
|  |     def assign_parent(self, parent: ProviderParent) -> None: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Selector(Provider[Any]): | class Selector(Provider[Any]): | ||||||
|  | @ -419,3 +437,6 @@ if pydantic: | ||||||
|     PydanticSettings = pydantic.BaseSettings |     PydanticSettings = pydantic.BaseSettings | ||||||
| else: | else: | ||||||
|     PydanticSettings = Any |     PydanticSettings = Any | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | CHILD_PROVIDERS: Tuple[Provider] | ||||||
|  |  | ||||||
|  | @ -474,6 +474,8 @@ cdef class Self(Provider): | ||||||
|             return copied |             return copied | ||||||
| 
 | 
 | ||||||
|         copied = self.__class__() |         copied = self.__class__() | ||||||
|  |         memo[id(self)] = copied | ||||||
|  | 
 | ||||||
|         copied.set_container(deepcopy(self.__container, memo)) |         copied.set_container(deepcopy(self.__container, memo)) | ||||||
|         copied.set_alt_names(self.__alt_names) |         copied.set_alt_names(self.__alt_names) | ||||||
| 
 | 
 | ||||||
|  | @ -617,6 +619,8 @@ cdef class Dependency(Provider): | ||||||
|             default = Object(default) |             default = Object(default) | ||||||
|         self.__default = default |         self.__default = default | ||||||
| 
 | 
 | ||||||
|  |         self.__parent = None | ||||||
|  | 
 | ||||||
|         super(Dependency, self).__init__() |         super(Dependency, self).__init__() | ||||||
| 
 | 
 | ||||||
|     def __deepcopy__(self, memo): |     def __deepcopy__(self, memo): | ||||||
|  | @ -625,9 +629,16 @@ cdef class Dependency(Provider): | ||||||
|         if copied is not None: |         if copied is not None: | ||||||
|             return copied |             return copied | ||||||
| 
 | 
 | ||||||
|         copied_default = deepcopy(self.__default, memo) if self.__default is not UNDEFINED else UNDEFINED |         copied_default = ( | ||||||
|         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) | ||||||
|  |         memo[id(self)] = copied | ||||||
|  | 
 | ||||||
|  |         self._copy_parent(copied, memo) | ||||||
|         self._copy_overridings(copied, memo) |         self._copy_overridings(copied, memo) | ||||||
| 
 | 
 | ||||||
|         return copied |         return copied | ||||||
|  | @ -644,7 +655,7 @@ cdef class Dependency(Provider): | ||||||
|         elif not self.__last_overriding and self.__default is not UNDEFINED: |         elif not self.__last_overriding and self.__default is not UNDEFINED: | ||||||
|             result = self.__default(*args, **kwargs) |             result = self.__default(*args, **kwargs) | ||||||
|         else: |         else: | ||||||
|             raise Error('Dependency is not defined') |             self._raise_undefined_error() | ||||||
| 
 | 
 | ||||||
|         if self.is_async_mode_disabled(): |         if self.is_async_mode_disabled(): | ||||||
|             self._check_instance_type(result) |             self._check_instance_type(result) | ||||||
|  | @ -697,13 +708,6 @@ cdef class Dependency(Provider): | ||||||
|         """Return default provider.""" |         """Return default provider.""" | ||||||
|         return self.__default |         return self.__default | ||||||
| 
 | 
 | ||||||
|     @property |  | ||||||
|     def related(self): |  | ||||||
|         """Return related providers generator.""" |  | ||||||
|         if self.__default is not UNDEFINED: |  | ||||||
|             yield self.__default |  | ||||||
|         yield from super().related |  | ||||||
| 
 |  | ||||||
|     def provided_by(self, provider): |     def provided_by(self, provider): | ||||||
|         """Set external dependency provider. |         """Set external dependency provider. | ||||||
| 
 | 
 | ||||||
|  | @ -714,6 +718,38 @@ cdef class Dependency(Provider): | ||||||
|         """ |         """ | ||||||
|         return self.override(provider) |         return self.override(provider) | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def related(self): | ||||||
|  |         """Return related providers generator.""" | ||||||
|  |         if self.__default is not UNDEFINED: | ||||||
|  |             yield self.__default | ||||||
|  |         yield from super().related | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent(self): | ||||||
|  |         """Return parent.""" | ||||||
|  |         return self.__parent | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent_name(self): | ||||||
|  |         """Return parent name.""" | ||||||
|  |         if not self.__parent: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         name = '' | ||||||
|  |         if self.__parent.parent_name: | ||||||
|  |             name += f'{self.__parent.parent_name}.' | ||||||
|  |         name += f'{self.__parent.resolve_provider_name(self)}' | ||||||
|  | 
 | ||||||
|  |         return name | ||||||
|  | 
 | ||||||
|  |     def assign_parent(self, parent): | ||||||
|  |         """Assign parent.""" | ||||||
|  |         self.__parent = parent | ||||||
|  | 
 | ||||||
|  |     def _copy_parent(self, copied, memo): | ||||||
|  |         _copy_parent(self, copied, memo) | ||||||
|  | 
 | ||||||
|     def _async_provide(self, future_result, future): |     def _async_provide(self, future_result, future): | ||||||
|         instance = future.result() |         instance = future.result() | ||||||
|         try: |         try: | ||||||
|  | @ -727,6 +763,11 @@ cdef class Dependency(Provider): | ||||||
|         if not isinstance(instance, self.instance_of): |         if not isinstance(instance, self.instance_of): | ||||||
|             raise Error('{0} is not an instance of {1}'.format(instance, self.instance_of)) |             raise Error('{0} is not an instance of {1}'.format(instance, self.instance_of)) | ||||||
| 
 | 
 | ||||||
|  |     def _raise_undefined_error(self): | ||||||
|  |         if self.parent_name: | ||||||
|  |             raise Error(f'Dependency "{self.parent_name}" is not defined') | ||||||
|  |         raise Error('Dependency is not defined') | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| cdef class ExternalDependency(Dependency): | cdef class ExternalDependency(Dependency): | ||||||
|     """:py:class:`ExternalDependency` provider describes dependency interface. |     """:py:class:`ExternalDependency` provider describes dependency interface. | ||||||
|  | @ -792,7 +833,13 @@ cdef class DependenciesContainer(Object): | ||||||
| 
 | 
 | ||||||
|     def __init__(self, **dependencies): |     def __init__(self, **dependencies): | ||||||
|         """Initializer.""" |         """Initializer.""" | ||||||
|  |         for provider in dependencies.values(): | ||||||
|  |             if isinstance(provider, CHILD_PROVIDERS): | ||||||
|  |                 provider.assign_parent(self) | ||||||
|  | 
 | ||||||
|         self.__providers = dependencies |         self.__providers = dependencies | ||||||
|  |         self.__parent = None | ||||||
|  | 
 | ||||||
|         super(DependenciesContainer, self).__init__(None) |         super(DependenciesContainer, self).__init__(None) | ||||||
| 
 | 
 | ||||||
|     def __deepcopy__(self, memo): |     def __deepcopy__(self, memo): | ||||||
|  | @ -804,9 +851,12 @@ cdef class DependenciesContainer(Object): | ||||||
|             return copied |             return copied | ||||||
| 
 | 
 | ||||||
|         copied = self.__class__() |         copied = self.__class__() | ||||||
|  |         memo[id(self)] = copied | ||||||
|  | 
 | ||||||
|         copied.__provides = deepcopy(self.__provides, memo) |         copied.__provides = deepcopy(self.__provides, memo) | ||||||
|         copied.__providers = deepcopy(self.__providers, memo) |         copied.__providers = deepcopy(self.__providers, memo) | ||||||
| 
 | 
 | ||||||
|  |         self._copy_parent(copied, memo) | ||||||
|         self._copy_overridings(copied, memo) |         self._copy_overridings(copied, memo) | ||||||
| 
 | 
 | ||||||
|         return copied |         return copied | ||||||
|  | @ -822,6 +872,8 @@ cdef class DependenciesContainer(Object): | ||||||
|         provider = self.__providers.get(name) |         provider = self.__providers.get(name) | ||||||
|         if not provider: |         if not provider: | ||||||
|             provider = Dependency() |             provider = Dependency() | ||||||
|  |             provider.assign_parent(self) | ||||||
|  | 
 | ||||||
|             self.__providers[name] = provider |             self.__providers[name] = provider | ||||||
| 
 | 
 | ||||||
|             container = self.__call__() |             container = self.__call__() | ||||||
|  | @ -881,6 +933,39 @@ cdef class DependenciesContainer(Object): | ||||||
|         yield from self.providers.values() |         yield from self.providers.values() | ||||||
|         yield from super().related |         yield from super().related | ||||||
| 
 | 
 | ||||||
|  |     def resolve_provider_name(self, provider): | ||||||
|  |         """Try to resolve provider name.""" | ||||||
|  |         for provider_name, container_provider in self.providers.items(): | ||||||
|  |             if container_provider is provider: | ||||||
|  |                 return provider_name | ||||||
|  |         else: | ||||||
|  |             raise Error(f'Can not resolve name for provider "{provider}"') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent(self): | ||||||
|  |         """Return parent.""" | ||||||
|  |         return self.__parent | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent_name(self): | ||||||
|  |         """Return parent name.""" | ||||||
|  |         if not self.__parent: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         name = '' | ||||||
|  |         if self.__parent.parent_name: | ||||||
|  |             name += f'{self.__parent.parent_name}.' | ||||||
|  |         name += f'{self.__parent.resolve_provider_name(self)}' | ||||||
|  | 
 | ||||||
|  |         return name | ||||||
|  | 
 | ||||||
|  |     def assign_parent(self, parent): | ||||||
|  |         """Assign parent.""" | ||||||
|  |         self.__parent = parent | ||||||
|  | 
 | ||||||
|  |     def _copy_parent(self, copied, memo): | ||||||
|  |         _copy_parent(self, copied, memo) | ||||||
|  | 
 | ||||||
|     cpdef object _override_providers(self, object container): |     cpdef object _override_providers(self, object container): | ||||||
|         """Override providers with providers from provided container.""" |         """Override providers with providers from provided container.""" | ||||||
|         for name, dependency_provider in container.providers.items(): |         for name, dependency_provider in container.providers.items(): | ||||||
|  | @ -3443,23 +3528,32 @@ cdef class Container(Provider): | ||||||
| 
 | 
 | ||||||
|         if container is None: |         if container is None: | ||||||
|             container = container_cls() |             container = container_cls() | ||||||
|  |             container.assign_parent(self) | ||||||
|         self.__container = container |         self.__container = container | ||||||
| 
 | 
 | ||||||
|         self.apply_overridings() |         if self.__container and self.__overriding_providers: | ||||||
|  |             self.apply_overridings() | ||||||
|  | 
 | ||||||
|  |         self.__parent = None | ||||||
| 
 | 
 | ||||||
|         super(Container, self).__init__() |         super(Container, self).__init__() | ||||||
| 
 | 
 | ||||||
|     def __deepcopy__(self, memo): |     def __deepcopy__(self, memo): | ||||||
|         """Create and return full copy of provider.""" |         """Create and return full copy of provider.""" | ||||||
|  |         cdef Container copied | ||||||
|  | 
 | ||||||
|         copied = memo.get(id(self)) |         copied = memo.get(id(self)) | ||||||
|         if copied is not None: |         if copied is not None: | ||||||
|             return copied |             return copied | ||||||
| 
 | 
 | ||||||
|         copied = self.__class__( |         copied = self.__class__(self.__container_cls, UNDEFINED) | ||||||
|             self.__container_cls, |         memo[id(self)] = copied | ||||||
|             deepcopy(self.__container, memo), | 
 | ||||||
|             **deepcopy(self.__overriding_providers, memo), |         copied.__container = deepcopy(self.__container, memo) | ||||||
|         ) |         copied.__overriding_providers = deepcopy(self.__overriding_providers, memo) | ||||||
|  |         copied.apply_overridings() | ||||||
|  | 
 | ||||||
|  |         self._copy_parent(copied, memo) | ||||||
| 
 | 
 | ||||||
|         return copied |         return copied | ||||||
| 
 | 
 | ||||||
|  | @ -3501,6 +3595,39 @@ cdef class Container(Provider): | ||||||
|         yield from self.providers.values() |         yield from self.providers.values() | ||||||
|         yield from super().related |         yield from super().related | ||||||
| 
 | 
 | ||||||
|  |     def resolve_provider_name(self, provider): | ||||||
|  |         """Try to resolve provider name.""" | ||||||
|  |         for provider_name, container_provider in self.providers.items(): | ||||||
|  |             if container_provider is provider: | ||||||
|  |                 return provider_name | ||||||
|  |         else: | ||||||
|  |             raise Error(f'Can not resolve name for provider "{provider}"') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent(self): | ||||||
|  |         """Return parent.""" | ||||||
|  |         return self.__parent | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parent_name(self): | ||||||
|  |         """Return parent name.""" | ||||||
|  |         if not self.__parent: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         name = '' | ||||||
|  |         if self.__parent.parent_name: | ||||||
|  |             name += f'{self.__parent.parent_name}.' | ||||||
|  |         name += f'{self.__parent.resolve_provider_name(self)}' | ||||||
|  | 
 | ||||||
|  |         return name | ||||||
|  | 
 | ||||||
|  |     def assign_parent(self, parent): | ||||||
|  |         """Assign parent.""" | ||||||
|  |         self.__parent = parent | ||||||
|  | 
 | ||||||
|  |     def _copy_parent(self, copied, memo): | ||||||
|  |         _copy_parent(self, copied, memo) | ||||||
|  | 
 | ||||||
|     cpdef object _provide(self, tuple args, dict kwargs): |     cpdef object _provide(self, tuple args, dict kwargs): | ||||||
|         """Return single instance.""" |         """Return single instance.""" | ||||||
|         return self.__container |         return self.__container | ||||||
|  | @ -4033,6 +4160,9 @@ cpdef tuple parse_named_injections(dict kwargs): | ||||||
|     return tuple(injections) |     return tuple(injections) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| cpdef bint is_provider(object instance): | cpdef bint is_provider(object instance): | ||||||
|     """Check if instance is provider instance. |     """Check if instance is provider instance. | ||||||
| 
 | 
 | ||||||
|  | @ -4093,6 +4223,30 @@ cpdef str represent_provider(object provider, object provides): | ||||||
|         address=hex(id(provider))) |         address=hex(id(provider))) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | cpdef bint is_container_instance(object instance): | ||||||
|  |     """Check if instance is container instance. | ||||||
|  | 
 | ||||||
|  |     :param instance: Instance to be checked. | ||||||
|  |     :type instance: object | ||||||
|  | 
 | ||||||
|  |     :rtype: bool | ||||||
|  |     """ | ||||||
|  |     return (not isinstance(instance, CLASS_TYPES) and | ||||||
|  |             getattr(instance, '__IS_CONTAINER__', False) is True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cpdef bint is_container_class(object instance): | ||||||
|  |     """Check if instance is container class. | ||||||
|  | 
 | ||||||
|  |     :param instance: Instance to be checked. | ||||||
|  |     :type instance: object | ||||||
|  | 
 | ||||||
|  |     :rtype: bool | ||||||
|  |     """ | ||||||
|  |     return (isinstance(instance, CLASS_TYPES) and | ||||||
|  |             getattr(instance, '__IS_CONTAINER__', False) is True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| cpdef object deepcopy(object instance, dict memo=None): | cpdef object deepcopy(object instance, dict memo=None): | ||||||
|     """Return full copy of provider or container with providers.""" |     """Return full copy of provider or container with providers.""" | ||||||
|     if memo is None: |     if memo is None: | ||||||
|  | @ -4102,6 +4256,7 @@ cpdef object deepcopy(object instance, dict memo=None): | ||||||
| 
 | 
 | ||||||
|     return copy.deepcopy(instance, memo) |     return copy.deepcopy(instance, memo) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def __add_sys_streams(memo): | def __add_sys_streams(memo): | ||||||
|     """Add system streams to memo dictionary. |     """Add system streams to memo dictionary. | ||||||
| 
 | 
 | ||||||
|  | @ -4188,3 +4343,13 @@ def isasyncgenfunction(obj): | ||||||
|         return inspect.isasyncgenfunction(obj) |         return inspect.isasyncgenfunction(obj) | ||||||
|     except AttributeError: |     except AttributeError: | ||||||
|         return False |         return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cpdef _copy_parent(object from_, object to, dict memo): | ||||||
|  |     """Copy and assign provider parent.""" | ||||||
|  |     copied_parent = ( | ||||||
|  |         deepcopy(from_.parent, memo) | ||||||
|  |         if is_provider(from_.parent) or is_container_instance(from_.parent) | ||||||
|  |         else from_.parent | ||||||
|  |     ) | ||||||
|  |     to.assign_parent(copied_parent) | ||||||
|  |  | ||||||
|  | @ -224,7 +224,7 @@ class DeclarativeContainerTests(unittest.TestCase): | ||||||
|                          (_OverridingContainer1.p11, |                          (_OverridingContainer1.p11, | ||||||
|                           _OverridingContainer2.p11)) |                           _OverridingContainer2.p11)) | ||||||
| 
 | 
 | ||||||
|     def test_reset_last_overridding(self): |     def test_reset_last_overriding(self): | ||||||
|         class _Container(containers.DeclarativeContainer): |         class _Container(containers.DeclarativeContainer): | ||||||
|             p11 = providers.Provider() |             p11 = providers.Provider() | ||||||
| 
 | 
 | ||||||
|  | @ -244,7 +244,7 @@ class DeclarativeContainerTests(unittest.TestCase): | ||||||
|         self.assertEqual(_Container.p11.overridden, |         self.assertEqual(_Container.p11.overridden, | ||||||
|                          (_OverridingContainer1.p11,)) |                          (_OverridingContainer1.p11,)) | ||||||
| 
 | 
 | ||||||
|     def test_reset_last_overridding_when_not_overridden(self): |     def test_reset_last_overriding_when_not_overridden(self): | ||||||
|         with self.assertRaises(errors.Error): |         with self.assertRaises(errors.Error): | ||||||
|             ContainerA.reset_last_overriding() |             ContainerA.reset_last_overriding() | ||||||
| 
 | 
 | ||||||
|  | @ -431,3 +431,68 @@ class DeclarativeContainerTests(unittest.TestCase): | ||||||
|         self.assertIsInstance(container.p31, providers.Provider) |         self.assertIsInstance(container.p31, providers.Provider) | ||||||
|         self.assertIsInstance(container.p32, providers.Provider) |         self.assertIsInstance(container.p32, providers.Provider) | ||||||
|         self.assertIs(container.p11.last_overriding, provider) |         self.assertIs(container.p11.last_overriding, provider) | ||||||
|  | 
 | ||||||
|  |     def test_parent_set_in__new__(self): | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             dependency = providers.Dependency() | ||||||
|  |             dependencies_container = providers.DependenciesContainer() | ||||||
|  |             container = providers.Container(ContainerA) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(Container.dependency.parent, Container) | ||||||
|  |         self.assertIs(Container.dependencies_container.parent, Container) | ||||||
|  |         self.assertIs(Container.container.parent, Container) | ||||||
|  | 
 | ||||||
|  |     def test_parent_set_in__setattr__(self): | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         Container.dependency = providers.Dependency() | ||||||
|  |         Container.dependencies_container = providers.DependenciesContainer() | ||||||
|  |         Container.container = providers.Container(ContainerA) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(Container.dependency.parent, Container) | ||||||
|  |         self.assertIs(Container.dependencies_container.parent, Container) | ||||||
|  |         self.assertIs(Container.container.parent, Container) | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name(self): | ||||||
|  |         self.assertEqual(ContainerA.resolve_provider_name(ContainerA.p11), 'p11') | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name_no_provider(self): | ||||||
|  |         with self.assertRaises(errors.Error): | ||||||
|  |             ContainerA.resolve_provider_name(providers.Provider()) | ||||||
|  | 
 | ||||||
|  |     def test_child_dependency_parent_name(self): | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             dependency = providers.Dependency() | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             Container.dependency() | ||||||
|  |         self.assertEqual( | ||||||
|  |             str(context.exception), | ||||||
|  |             'Dependency "Container.dependency" is not defined', | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def test_child_dependencies_container_parent_name(self): | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             dependencies_container = providers.DependenciesContainer() | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             Container.dependencies_container.dependency() | ||||||
|  |         self.assertEqual( | ||||||
|  |             str(context.exception), | ||||||
|  |             'Dependency "Container.dependencies_container.dependency" is not defined', | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def test_child_container_parent_name(self): | ||||||
|  |         class ChildContainer(containers.DeclarativeContainer): | ||||||
|  |             dependency = providers.Dependency() | ||||||
|  | 
 | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             child_container = providers.Container(ChildContainer) | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             Container.child_container.dependency() | ||||||
|  |         self.assertEqual( | ||||||
|  |             str(context.exception), | ||||||
|  |             'Dependency "Container.child_container.dependency" is not defined', | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  | @ -336,6 +336,62 @@ class DeclarativeContainerInstanceTests(unittest.TestCase): | ||||||
|         self.assertIs(obj32, obj42) |         self.assertIs(obj32, obj42) | ||||||
|         self.assertIs(obj33, obj43) |         self.assertIs(obj33, obj43) | ||||||
| 
 | 
 | ||||||
|  |     def test_assign_parent(self): | ||||||
|  |         parent = providers.DependenciesContainer() | ||||||
|  |         container = ContainerA() | ||||||
|  | 
 | ||||||
|  |         container.assign_parent(parent) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(container.parent, parent) | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_declarative_parent(self): | ||||||
|  |         container = ContainerA() | ||||||
|  |         self.assertEqual(container.parent_name, 'ContainerA') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name(self): | ||||||
|  |         container = ContainerA() | ||||||
|  |         self.assertEqual(container.parent_name, 'ContainerA') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_with_deep_parenting(self): | ||||||
|  |         class Container2(containers.DeclarativeContainer): | ||||||
|  | 
 | ||||||
|  |             name = providers.Container(ContainerA) | ||||||
|  | 
 | ||||||
|  |         class Container1(containers.DeclarativeContainer): | ||||||
|  | 
 | ||||||
|  |             container = providers.Container(Container2) | ||||||
|  | 
 | ||||||
|  |         container = Container1() | ||||||
|  |         self.assertEqual(container.container().name.parent_name, 'Container1.container.name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_is_none(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         self.assertIsNone(container.parent_name) | ||||||
|  | 
 | ||||||
|  |     def test_parent_deepcopy(self): | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             container = providers.Container(ContainerA) | ||||||
|  | 
 | ||||||
|  |         container = Container() | ||||||
|  | 
 | ||||||
|  |         copied = providers.deepcopy(container) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(container.container.parent, container) | ||||||
|  |         self.assertIs(copied.container.parent, copied) | ||||||
|  | 
 | ||||||
|  |         self.assertIsNot(container, copied) | ||||||
|  |         self.assertIsNot(container.container, copied.container) | ||||||
|  |         self.assertIsNot(container.container.parent, copied.container.parent) | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name(self): | ||||||
|  |         container = ContainerA() | ||||||
|  |         self.assertEqual(container.resolve_provider_name(container.p11), 'p11') | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name_no_provider(self): | ||||||
|  |         container = ContainerA() | ||||||
|  |         with self.assertRaises(errors.Error): | ||||||
|  |             container.resolve_provider_name(providers.Provider()) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class SelfTests(unittest.TestCase): | class SelfTests(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -360,8 +360,128 @@ class DependencyTests(unittest.TestCase): | ||||||
|         self.provider.provided_by(providers.Factory(dict)) |         self.provider.provided_by(providers.Factory(dict)) | ||||||
|         self.assertRaises(errors.Error, self.provider) |         self.assertRaises(errors.Error, self.provider) | ||||||
| 
 | 
 | ||||||
|     def test_call_not_overridden(self): |     def test_call_undefined(self): | ||||||
|         self.assertRaises(errors.Error, self.provider) |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             self.provider() | ||||||
|  |         self.assertEqual(str(context.exception), 'Dependency is not defined') | ||||||
|  | 
 | ||||||
|  |     def test_call_undefined_error_message_with_container_instance_parent(self): | ||||||
|  |         class UserService: | ||||||
|  |             def __init__(self, database): | ||||||
|  |                 self.database = database | ||||||
|  | 
 | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             database = providers.Dependency() | ||||||
|  | 
 | ||||||
|  |             user_service = providers.Factory( | ||||||
|  |                 UserService, | ||||||
|  |                 database=database,  # <---- missing dependency | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         container = Container() | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             container.user_service() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(str(context.exception), 'Dependency "Container.database" is not defined') | ||||||
|  | 
 | ||||||
|  |     def test_call_undefined_error_message_with_container_provider_parent_deep(self): | ||||||
|  |         class Database: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         class UserService: | ||||||
|  |             def __init__(self, db): | ||||||
|  |                 self.db = db | ||||||
|  | 
 | ||||||
|  |         class Gateways(containers.DeclarativeContainer): | ||||||
|  |             database_client = providers.Singleton(Database) | ||||||
|  | 
 | ||||||
|  |         class Services(containers.DeclarativeContainer): | ||||||
|  |             gateways = providers.DependenciesContainer() | ||||||
|  | 
 | ||||||
|  |             user = providers.Factory( | ||||||
|  |                 UserService, | ||||||
|  |                 db=gateways.database_client, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         class Container(containers.DeclarativeContainer): | ||||||
|  |             gateways = providers.Container(Gateways) | ||||||
|  | 
 | ||||||
|  |             services = providers.Container( | ||||||
|  |                 Services, | ||||||
|  |                 # gateways=gateways,  # <---- missing dependency | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         container = Container() | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             container.services().user() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             str(context.exception), | ||||||
|  |             'Dependency "Container.services.gateways.database_client" is not defined', | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def test_call_undefined_error_message_with_dependenciescontainer_provider_parent(self): | ||||||
|  |         class UserService: | ||||||
|  |             def __init__(self, db): | ||||||
|  |                 self.db = db | ||||||
|  | 
 | ||||||
|  |         class Services(containers.DeclarativeContainer): | ||||||
|  |             gateways = providers.DependenciesContainer() | ||||||
|  | 
 | ||||||
|  |             user = providers.Factory( | ||||||
|  |                 UserService, | ||||||
|  |                 db=gateways.database_client,  # <---- missing dependency | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         services = Services() | ||||||
|  | 
 | ||||||
|  |         with self.assertRaises(errors.Error) as context: | ||||||
|  |             services.user() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             str(context.exception), | ||||||
|  |             'Dependency "Services.gateways.database_client" is not defined', | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def test_assign_parent(self): | ||||||
|  |         parent = providers.DependenciesContainer() | ||||||
|  |         provider = providers.Dependency() | ||||||
|  | 
 | ||||||
|  |         provider.assign_parent(parent) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(provider.parent, parent) | ||||||
|  | 
 | ||||||
|  |     def test_parent_name(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.Dependency() | ||||||
|  |         container.name = provider | ||||||
|  |         self.assertEqual(provider.parent_name, 'name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_with_deep_parenting(self): | ||||||
|  |         provider = providers.Dependency() | ||||||
|  |         container = providers.DependenciesContainer(name=provider) | ||||||
|  |         _ = providers.DependenciesContainer(container=container) | ||||||
|  |         self.assertEqual(provider.parent_name, 'container.name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_is_none(self): | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         self.assertIsNone(provider.parent_name) | ||||||
|  | 
 | ||||||
|  |     def test_parent_deepcopy(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.Dependency() | ||||||
|  |         container.name = provider | ||||||
|  | 
 | ||||||
|  |         copied = providers.deepcopy(container) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(container.name.parent, container) | ||||||
|  |         self.assertIs(copied.name.parent, copied) | ||||||
|  | 
 | ||||||
|  |         self.assertIsNot(container, copied) | ||||||
|  |         self.assertIsNot(container.name, copied.name) | ||||||
|  |         self.assertIsNot(container.name.parent, copied.name.parent) | ||||||
| 
 | 
 | ||||||
|     def test_deepcopy(self): |     def test_deepcopy(self): | ||||||
|         provider = providers.Dependency(int) |         provider = providers.Dependency(int) | ||||||
|  | @ -523,3 +643,61 @@ class DependenciesContainerTests(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|         self.assertFalse(dependency.overridden) |         self.assertFalse(dependency.overridden) | ||||||
|         self.assertFalse(dependency.overridden) |         self.assertFalse(dependency.overridden) | ||||||
|  | 
 | ||||||
|  |     def test_assign_parent(self): | ||||||
|  |         parent = providers.DependenciesContainer() | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  | 
 | ||||||
|  |         provider.assign_parent(parent) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(provider.parent, parent) | ||||||
|  | 
 | ||||||
|  |     def test_parent_name(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         container.name = provider | ||||||
|  |         self.assertEqual(provider.parent_name, 'name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_with_deep_parenting(self): | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         container = providers.DependenciesContainer(name=provider) | ||||||
|  |         _ = providers.DependenciesContainer(container=container) | ||||||
|  |         self.assertEqual(provider.parent_name, 'container.name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_is_none(self): | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         self.assertIsNone(provider.parent_name) | ||||||
|  | 
 | ||||||
|  |     def test_parent_deepcopy(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         container.name = provider | ||||||
|  | 
 | ||||||
|  |         copied = providers.deepcopy(container) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(container.name.parent, container) | ||||||
|  |         self.assertIs(copied.name.parent, copied) | ||||||
|  | 
 | ||||||
|  |         self.assertIsNot(container, copied) | ||||||
|  |         self.assertIsNot(container.name, copied.name) | ||||||
|  |         self.assertIsNot(container.name.parent, copied.name.parent) | ||||||
|  | 
 | ||||||
|  |     def test_parent_set_on__getattr__(self): | ||||||
|  |         provider = providers.DependenciesContainer() | ||||||
|  |         self.assertIsInstance(provider.name, providers.Dependency) | ||||||
|  |         self.assertIs(provider.name.parent, provider) | ||||||
|  | 
 | ||||||
|  |     def test_parent_set_on__init__(self): | ||||||
|  |         provider = providers.Dependency() | ||||||
|  |         container = providers.DependenciesContainer(name=provider) | ||||||
|  |         self.assertIs(container.name, provider) | ||||||
|  |         self.assertIs(container.name.parent, container) | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name(self): | ||||||
|  |         container = providers.DependenciesContainer() | ||||||
|  |         self.assertEqual(container.resolve_provider_name(container.name), 'name') | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name_no_provider(self): | ||||||
|  |         container = providers.DependenciesContainer() | ||||||
|  |         with self.assertRaises(errors.Error): | ||||||
|  |             container.resolve_provider_name(providers.Provider()) | ||||||
|  |  | ||||||
|  | @ -175,3 +175,50 @@ class ContainerTests(unittest.TestCase): | ||||||
|         b = B(d=D()) |         b = B(d=D()) | ||||||
|         result = b.a().c().bar() |         result = b.a().c().bar() | ||||||
|         self.assertEqual(result, 'foo++') |         self.assertEqual(result, 'foo++') | ||||||
|  | 
 | ||||||
|  |     def test_assign_parent(self): | ||||||
|  |         parent = providers.DependenciesContainer() | ||||||
|  |         provider = providers.Container(TestCore) | ||||||
|  | 
 | ||||||
|  |         provider.assign_parent(parent) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(provider.parent, parent) | ||||||
|  | 
 | ||||||
|  |     def test_parent_name(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.Container(TestCore) | ||||||
|  |         container.name = provider | ||||||
|  |         self.assertEqual(provider.parent_name, 'name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_with_deep_parenting(self): | ||||||
|  |         provider = providers.Container(TestCore) | ||||||
|  |         container = providers.DependenciesContainer(name=provider) | ||||||
|  |         _ = providers.DependenciesContainer(container=container) | ||||||
|  |         self.assertEqual(provider.parent_name, 'container.name') | ||||||
|  | 
 | ||||||
|  |     def test_parent_name_is_none(self): | ||||||
|  |         provider = providers.Container(TestCore) | ||||||
|  |         self.assertIsNone(provider.parent_name) | ||||||
|  | 
 | ||||||
|  |     def test_parent_deepcopy(self): | ||||||
|  |         container = containers.DynamicContainer() | ||||||
|  |         provider = providers.Container(TestCore) | ||||||
|  |         container.name = provider | ||||||
|  | 
 | ||||||
|  |         copied = providers.deepcopy(container) | ||||||
|  | 
 | ||||||
|  |         self.assertIs(container.name.parent, container) | ||||||
|  |         self.assertIs(copied.name.parent, copied) | ||||||
|  | 
 | ||||||
|  |         self.assertIsNot(container, copied) | ||||||
|  |         self.assertIsNot(container.name, copied.name) | ||||||
|  |         self.assertIsNot(container.name.parent, copied.name.parent) | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name(self): | ||||||
|  |         container = providers.Container(TestCore) | ||||||
|  |         self.assertEqual(container.resolve_provider_name(container.value_getter), 'value_getter') | ||||||
|  | 
 | ||||||
|  |     def test_resolve_provider_name_no_provider(self): | ||||||
|  |         container = providers.Container(TestCore) | ||||||
|  |         with self.assertRaises(errors.Error): | ||||||
|  |             container.resolve_provider_name(providers.Provider()) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user