mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-25 19:14:00 +03:00
Wiring container injection (#353)
* Add container injections to wiring * Add example * Update docs * Update changelog * Improve typing
This commit is contained in:
parent
6e77a95909
commit
8dd8446d39
|
@ -7,6 +7,10 @@ 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 container injection support for wiring.
|
||||||
|
|
||||||
4.6.1
|
4.6.1
|
||||||
-----
|
-----
|
||||||
- Add Disqus comments widget to the provider's async injections docs page.
|
- Add Disqus comments widget to the provider's async injections docs page.
|
||||||
|
|
|
@ -77,6 +77,13 @@ You can use configuration, provided instance and sub-container providers as you
|
||||||
You can compound wiring and ``Resource`` provider to implement per-function execution scope.
|
You can compound wiring and ``Resource`` provider to implement per-function execution scope.
|
||||||
See :ref:`Resources, wiring and per-function execution scope <resource-provider-wiring-closing>` for details.
|
See :ref:`Resources, wiring and per-function execution scope <resource-provider-wiring-closing>` for details.
|
||||||
|
|
||||||
|
Also you can use ``Provide`` marker to inject a container.
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/wiring/example_container.py
|
||||||
|
:language: python
|
||||||
|
:emphasize-lines: 16-19
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
Wiring with modules and packages
|
Wiring with modules and packages
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
|
28
examples/wiring/example_container.py
Normal file
28
examples/wiring/example_container.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
"""Wiring container injection example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def main(container: Container = Provide[Container]):
|
||||||
|
service = container.service()
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
||||||
|
|
||||||
|
main()
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,7 @@ class Container:
|
||||||
provider_type: Type[Provider] = Provider
|
provider_type: Type[Provider] = Provider
|
||||||
providers: Dict[str, Provider]
|
providers: Dict[str, Provider]
|
||||||
overridden: Tuple[Provider]
|
overridden: Tuple[Provider]
|
||||||
|
__self__: Provider
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
|
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
|
||||||
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
|
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
|
||||||
|
|
|
@ -13,6 +13,7 @@ import six
|
||||||
from .errors import Error
|
from .errors import Error
|
||||||
from .providers cimport (
|
from .providers cimport (
|
||||||
Provider,
|
Provider,
|
||||||
|
Object,
|
||||||
Resource,
|
Resource,
|
||||||
deepcopy,
|
deepcopy,
|
||||||
)
|
)
|
||||||
|
@ -70,6 +71,7 @@ class DynamicContainer(object):
|
||||||
self.declarative_parent = None
|
self.declarative_parent = None
|
||||||
self.wired_to_modules = []
|
self.wired_to_modules = []
|
||||||
self.wired_to_packages = []
|
self.wired_to_packages = []
|
||||||
|
self.__self__ = Object(self)
|
||||||
super(DynamicContainer, self).__init__()
|
super(DynamicContainer, self).__init__()
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
|
@ -102,7 +104,7 @@ class DynamicContainer(object):
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Provider):
|
if isinstance(value, Provider) and name != '__self__':
|
||||||
_check_provider_type(self, value)
|
_check_provider_type(self, value)
|
||||||
self.providers[name] = value
|
self.providers[name] = value
|
||||||
super(DynamicContainer, self).__setattr__(name, value)
|
super(DynamicContainer, self).__setattr__(name, value)
|
||||||
|
@ -282,6 +284,8 @@ class DeclarativeContainerMetaClass(type):
|
||||||
|
|
||||||
cls = <type>type.__new__(mcs, class_name, bases, attributes)
|
cls = <type>type.__new__(mcs, class_name, bases, attributes)
|
||||||
|
|
||||||
|
cls.__self__ = Object(cls)
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
for provider in six.itervalues(cls.providers):
|
||||||
_check_provider_type(cls, provider)
|
_check_provider_type(cls, provider)
|
||||||
|
|
||||||
|
@ -301,7 +305,7 @@ class DeclarativeContainerMetaClass(type):
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Provider):
|
if isinstance(value, Provider) and name != '__self__':
|
||||||
_check_provider_type(cls, value)
|
_check_provider_type(cls, value)
|
||||||
cls.providers[name] = value
|
cls.providers[name] = value
|
||||||
cls.cls_providers[name] = value
|
cls.cls_providers[name] = value
|
||||||
|
@ -381,6 +385,12 @@ class DeclarativeContainer(object):
|
||||||
:type: tuple[:py:class:`DeclarativeContainer`]
|
:type: tuple[:py:class:`DeclarativeContainer`]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__self__ = None
|
||||||
|
"""Provider that provides current container.
|
||||||
|
|
||||||
|
:type: :py:class:`dependency_injector.providers.Provider`
|
||||||
|
"""
|
||||||
|
|
||||||
def __new__(cls, **overriding_providers):
|
def __new__(cls, **overriding_providers):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ from typing import (
|
||||||
Generic,
|
Generic,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Type,
|
Type,
|
||||||
|
Union,
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,8 +76,8 @@ class ProvidersMap:
|
||||||
def __init__(self, container):
|
def __init__(self, container):
|
||||||
self._container = container
|
self._container = container
|
||||||
self._map = self._create_providers_map(
|
self._map = self._create_providers_map(
|
||||||
current_providers=container.providers,
|
current_container=container,
|
||||||
original_providers=container.declarative_parent.providers,
|
original_container=container.declarative_parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_provider(
|
def resolve_provider(
|
||||||
|
@ -173,9 +174,15 @@ class ProvidersMap:
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_providers_map(
|
def _create_providers_map(
|
||||||
cls,
|
cls,
|
||||||
current_providers: Dict[str, providers.Provider],
|
current_container: Container,
|
||||||
original_providers: Dict[str, providers.Provider],
|
original_container: Container,
|
||||||
) -> Dict[providers.Provider, providers.Provider]:
|
) -> Dict[providers.Provider, providers.Provider]:
|
||||||
|
current_providers = current_container.providers
|
||||||
|
current_providers['__self__'] = current_container.__self__
|
||||||
|
|
||||||
|
original_providers = original_container.providers
|
||||||
|
original_providers['__self__'] = original_container.__self__
|
||||||
|
|
||||||
providers_map = {}
|
providers_map = {}
|
||||||
for provider_name, current_provider in current_providers.items():
|
for provider_name, current_provider in current_providers.items():
|
||||||
original_provider = original_providers[provider_name]
|
original_provider = original_providers[provider_name]
|
||||||
|
@ -184,8 +191,8 @@ class ProvidersMap:
|
||||||
if isinstance(current_provider, providers.Container) \
|
if isinstance(current_provider, providers.Container) \
|
||||||
and isinstance(original_provider, providers.Container):
|
and isinstance(original_provider, providers.Container):
|
||||||
subcontainer_map = cls._create_providers_map(
|
subcontainer_map = cls._create_providers_map(
|
||||||
current_providers=current_provider.container.providers,
|
current_container=current_provider.container,
|
||||||
original_providers=original_provider.container.providers,
|
original_container=original_provider.container,
|
||||||
)
|
)
|
||||||
providers_map.update(subcontainer_map)
|
providers_map.update(subcontainer_map)
|
||||||
|
|
||||||
|
@ -479,6 +486,12 @@ def _is_declarative_container_instance(instance: Any) -> bool:
|
||||||
and getattr(instance, 'declarative_parent', None) is not None)
|
and getattr(instance, 'declarative_parent', None) is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_declarative_container(instance: Any) -> bool:
|
||||||
|
return (isinstance(instance, type)
|
||||||
|
and getattr(instance, '__IS_CONTAINER__', False) is True
|
||||||
|
and getattr(instance, 'declarative_parent', None) is None)
|
||||||
|
|
||||||
|
|
||||||
class ClassGetItemMeta(GenericMeta):
|
class ClassGetItemMeta(GenericMeta):
|
||||||
def __getitem__(cls, item):
|
def __getitem__(cls, item):
|
||||||
# Spike for Python 3.6
|
# Spike for Python 3.6
|
||||||
|
@ -487,8 +500,10 @@ class ClassGetItemMeta(GenericMeta):
|
||||||
|
|
||||||
class _Marker(Generic[T], metaclass=ClassGetItemMeta):
|
class _Marker(Generic[T], metaclass=ClassGetItemMeta):
|
||||||
|
|
||||||
def __init__(self, provider: providers.Provider) -> None:
|
def __init__(self, provider: Union[providers.Provider, Container]) -> None:
|
||||||
self.provider = provider
|
if _is_declarative_container(provider):
|
||||||
|
provider = provider.__self__
|
||||||
|
self.provider: providers.Provider = provider
|
||||||
|
|
||||||
def __class_getitem__(cls, item) -> T:
|
def __class_getitem__(cls, item) -> T:
|
||||||
return cls(item)
|
return cls(item)
|
||||||
|
|
|
@ -91,3 +91,7 @@ class ClassDecorator:
|
||||||
@inject
|
@inject
|
||||||
def test_class_decorator(service: Service = Provide[Container.service]):
|
def test_class_decorator(service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def test_container(container: Container = Provide[Container]):
|
||||||
|
return container.service()
|
||||||
|
|
|
@ -239,6 +239,10 @@ class WiringTest(unittest.TestCase):
|
||||||
service = module.test_class_decorator()
|
service = module.test_class_decorator()
|
||||||
self.assertIsInstance(service, Service)
|
self.assertIsInstance(service, Service)
|
||||||
|
|
||||||
|
def test_container(self):
|
||||||
|
service = module.test_container()
|
||||||
|
self.assertIsInstance(service, Service)
|
||||||
|
|
||||||
|
|
||||||
class WiringAndFastAPITest(unittest.TestCase):
|
class WiringAndFastAPITest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user