Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion docs/containers/declarative.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,39 @@ Injections in the declarative container are done the usual way:
:language: python
: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
:language: python
: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::
1 change: 1 addition & 0 deletions docs/main/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Develop
- Improve wiring with adding importing modules and packages from a string
``container.wire(modules=["yourapp.module1"])``.
- Add container wiring configuration ``wiring_config = containers.WiringConfiguration()``.
- Add support of ``with`` statement for ``container.override_providers()`` method.
- Update documentation and fix typos.

4.36.2
Expand Down
4,366 changes: 2,484 additions & 1,882 deletions src/dependency_injector/containers.c

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion src/dependency_injector/containers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Container:
def set_providers(self, **providers: Provider): ...
def set_provider(self, name: str, provider: Provider) -> 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_override(self) -> None: ...
def is_auto_wiring_enabled(self) -> bool: ...
Expand Down Expand Up @@ -90,6 +90,12 @@ class DeclarativeContainer(Container):
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]):
def __init__(self, container: T): ...
def __enter__(self) -> T: ...
Expand Down
18 changes: 18 additions & 0 deletions src/dependency_injector/containers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,12 @@ class DynamicContainer(Container):

:rtype: None
"""
overridden_providers = []
for name, overriding_provider in six.iteritems(overriding_providers):
container_provider = getattr(self, name)
container_provider.override(overriding_provider)
overridden_providers.append(container_provider)
return ProvidersOverridingContext(self, overridden_providers)

def reset_last_overriding(self):
"""Reset last overriding provider for each container providers.
Expand Down Expand Up @@ -784,6 +787,21 @@ class SingletonResetContext:
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):
""":py:class:`DeclarativeContainer` overriding decorator.

Expand Down
4 changes: 3 additions & 1 deletion tests/typing/declarative_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ class Container6(containers.DeclarativeContainer):
container6: containers.Container = Container6()


# Test 7: to override()
# Test 7: to override_providers()
class Container7(containers.DeclarativeContainer):
provider = providers.Factory(str)

container7 = Container7()
container7.override_providers(provider="new_value")
with container7.override_providers(a=providers.Provider()):
...
2 changes: 2 additions & 0 deletions tests/typing/dynamic_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# Test 3: to check override_providers()
container3 = containers.DynamicContainer()
container3.override_providers(a=providers.Provider())
with container3.override_providers(a=providers.Provider()):
...

# Test 4: to check set_providers()
container4 = containers.DynamicContainer()
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/containers/test_dynamic_py2_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,19 @@ def test_override_providers(self):
self.assertIs(container_a.p11.last_overriding, p1)
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):
container_a = ContainerA()

Expand Down