Add with support for container.override_providers() (#517)

* Add implementation, tests, and typing stub

* Update documentation

* Update changelog
This commit is contained in:
Roman Mogylatov 2021-10-06 21:36:41 -04:00 committed by GitHub
parent 73a43e6191
commit 284dee6e58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 2537 additions and 1864 deletions

View File

@ -34,10 +34,39 @@ Injections in the declarative container are done the usual way:
:language: python :language: python
:lines: 3- :lines: 3-
You can override the container providers when you create the container instance: You can override container providers while creating a container instance:
.. literalinclude:: ../../examples/containers/declarative_override_providers.py .. literalinclude:: ../../examples/containers/declarative_override_providers.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 13
Alternatively, you can call ``container.override_providers()`` method when the container instance
already exists:
.. code-block:: python
:emphasize-lines: 3
container = Container()
container.override_providers(foo=mock.Mock(Foo), bar=mock.Mock(Bar))
assert isinstance(container.foo(), mock.Mock)
assert isinstance(container.bar(), mock.Mock)
You can also use ``container.override_providers()`` with a context manager to reset
provided overriding after the context is closed:
.. code-block:: python
:emphasize-lines: 3
container = Container()
with container.override_providers(foo=mock.Mock(Foo), bar=mock.Mock(Bar)):
assert isinstance(container.foo(), mock.Mock)
assert isinstance(container.bar(), mock.Mock)
assert isinstance(container.foo(), Foo)
assert isinstance(container.bar(), Bar)
.. disqus:: .. disqus::

View File

@ -12,6 +12,7 @@ Develop
- Improve wiring with adding importing modules and packages from a string - Improve wiring with adding importing modules and packages from a string
``container.wire(modules=["yourapp.module1"])``. ``container.wire(modules=["yourapp.module1"])``.
- Add container wiring configuration ``wiring_config = containers.WiringConfiguration()``. - Add container wiring configuration ``wiring_config = containers.WiringConfiguration()``.
- Add support of ``with`` statement for ``container.override_providers()`` method.
- Update documentation and fix typos. - Update documentation and fix typos.
4.36.2 4.36.2

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,7 @@ class Container:
def set_providers(self, **providers: Provider): ... def set_providers(self, **providers: Provider): ...
def set_provider(self, name: str, provider: Provider) -> None: ... def set_provider(self, name: str, provider: Provider) -> None: ...
def override(self, overriding: C_Base) -> None: ... def override(self, overriding: C_Base) -> None: ...
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> None: ... def override_providers(self, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
def reset_last_overriding(self) -> None: ... def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ... def reset_override(self) -> None: ...
def is_auto_wiring_enabled(self) -> bool: ... def is_auto_wiring_enabled(self) -> bool: ...
@ -90,6 +90,12 @@ class DeclarativeContainer(Container):
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ... def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
class ProvidersOverridingContext(Generic[T]):
def __init__(self, container: T, overridden_providers: Iterable[Union[Provider, Any]]) -> None: ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...
class SingletonResetContext(Generic[T]): class SingletonResetContext(Generic[T]):
def __init__(self, container: T): ... def __init__(self, container: T): ...
def __enter__(self) -> T: ... def __enter__(self) -> T: ...

View File

@ -240,9 +240,12 @@ class DynamicContainer(Container):
:rtype: None :rtype: None
""" """
overridden_providers = []
for name, overriding_provider in six.iteritems(overriding_providers): for name, overriding_provider in six.iteritems(overriding_providers):
container_provider = getattr(self, name) container_provider = getattr(self, name)
container_provider.override(overriding_provider) container_provider.override(overriding_provider)
overridden_providers.append(container_provider)
return ProvidersOverridingContext(self, overridden_providers)
def reset_last_overriding(self): def reset_last_overriding(self):
"""Reset last overriding provider for each container providers. """Reset last overriding provider for each container providers.
@ -784,6 +787,21 @@ class SingletonResetContext:
self._container.reset_singletons() self._container.reset_singletons()
class ProvidersOverridingContext:
def __init__(self, container, overridden_providers):
self._container = container
self._overridden_providers = overridden_providers
def __enter__(self):
return self._container
def __exit__(self, *_):
for provider in self._overridden_providers:
provider.reset_last_overriding()
def override(object container): def override(object container):
""":py:class:`DeclarativeContainer` overriding decorator. """:py:class:`DeclarativeContainer` overriding decorator.

View File

@ -58,9 +58,11 @@ class Container6(containers.DeclarativeContainer):
container6: containers.Container = Container6() container6: containers.Container = Container6()
# Test 7: to override() # Test 7: to override_providers()
class Container7(containers.DeclarativeContainer): class Container7(containers.DeclarativeContainer):
provider = providers.Factory(str) provider = providers.Factory(str)
container7 = Container7() container7 = Container7()
container7.override_providers(provider="new_value") container7.override_providers(provider="new_value")
with container7.override_providers(a=providers.Provider()):
...

View File

@ -14,6 +14,8 @@ container2.override(containers.DynamicContainer())
# Test 3: to check override_providers() # Test 3: to check override_providers()
container3 = containers.DynamicContainer() container3 = containers.DynamicContainer()
container3.override_providers(a=providers.Provider()) container3.override_providers(a=providers.Provider())
with container3.override_providers(a=providers.Provider()):
...
# Test 4: to check set_providers() # Test 4: to check set_providers()
container4 = containers.DynamicContainer() container4 = containers.DynamicContainer()

View File

@ -134,6 +134,19 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
self.assertIs(container_a.p11.last_overriding, p1) self.assertIs(container_a.p11.last_overriding, p1)
self.assertIs(container_a.p12.last_overriding, p2) self.assertIs(container_a.p12.last_overriding, p2)
def test_override_providers_context_manager(self):
p1 = providers.Provider()
p2 = providers.Provider()
container_a = ContainerA()
with container_a.override_providers(p11=p1, p12=p2) as container:
self.assertIs(container, container_a)
self.assertIs(container_a.p11.last_overriding, p1)
self.assertIs(container_a.p12.last_overriding, p2)
self.assertIsNone(container_a.p11.last_overriding)
self.assertIsNone(container_a.p12.last_overriding)
def test_override_providers_with_unknown_provider(self): def test_override_providers_with_unknown_provider(self):
container_a = ContainerA() container_a = ContainerA()