Skip to content

Commit 48df949

Browse files
committed
Merge branch 'release/4.35.3' into master
2 parents cde7dee + 9637d97 commit 48df949

File tree

7 files changed

+558
-494
lines changed

7 files changed

+558
-494
lines changed

docs/main/changelog.rst

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ that were made in every particular version.
77
From version 0.7.6 *Dependency Injector* framework strictly
88
follows `Semantic versioning`_
99

10+
4.35.3
11+
------
12+
- Fix ``@containers.copy()`` decorator to respect dependencies on parent providers.
13+
See issue `#477 <https://github.com/ets-labs/python-dependency-injector/issues/477>`_.
14+
Thanks to `Andrey Torsunov @gtors <https://github.com/gtors>`_ for reporting the issue.
15+
- Fix typing stub for ``container.override_providers()`` to accept other types besides ``Provider``.
16+
1017
4.35.2
1118
------
1219
- Update wiring to support modules provided as packages.

src/dependency_injector/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Top-level package."""
22

3-
__version__ = '4.35.2'
3+
__version__ = '4.35.3'
44
"""Version number.
55
66
:type: str

src/dependency_injector/containers.c

+498-464
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dependency_injector/containers.pyi

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Container:
4040
def set_providers(self, **providers: Provider): ...
4141
def set_provider(self, name: str, provider: Provider) -> None: ...
4242
def override(self, overriding: C_Base) -> None: ...
43-
def override_providers(self, **overriding_providers: Provider) -> None: ...
43+
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> None: ...
4444
def reset_last_overriding(self) -> None: ...
4545
def reset_override(self) -> None: ...
4646
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> None: ...

src/dependency_injector/containers.pyx

+22-21
Original file line numberDiff line numberDiff line change
@@ -733,43 +733,44 @@ def override(object container):
733733
return _decorator
734734

735735

736-
def copy(object container):
736+
def copy(object base_container):
737737
""":py:class:`DeclarativeContainer` copying decorator.
738738
739739
This decorator copies all providers from provided container to decorated one.
740740
If one of the decorated container providers matches to source container
741741
providers by name, it would be replaced by reference.
742742
743-
:param container: Container that should be copied by decorated container.
744-
:type container: :py:class:`DeclarativeContainer`
743+
:param base_container: Container that should be copied by decorated container.
744+
:type base_container: :py:class:`DeclarativeContainer`
745745
746746
:return: Declarative container's copying decorator.
747747
:rtype: callable(:py:class:`DeclarativeContainer`)
748748
"""
749-
def _get_providers_memo(from_providers, source_providers):
750-
memo = dict()
751-
752-
for name, provider in from_providers.items():
753-
try:
754-
source_provider = source_providers[name]
755-
except KeyError:
749+
def _get_memo_for_matching_names(new_providers, base_providers):
750+
memo = {}
751+
for new_provider_name, new_provider in six.iteritems(new_providers):
752+
if new_provider_name not in base_providers:
756753
continue
757-
else:
758-
memo[id(source_provider)] = provider
754+
source_provider = base_providers[new_provider_name]
755+
memo[id(source_provider)] = new_provider
759756

760-
if hasattr(provider, 'providers') and hasattr(source_provider, 'providers'):
761-
sub_memo = _get_providers_memo(provider.providers, source_provider.providers)
762-
memo.update(sub_memo)
757+
if hasattr(new_provider, 'providers') and hasattr(source_provider, 'providers'):
758+
sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
759+
memo.update(sub_memo)
763760
return memo
764761

765-
def _decorator(copied_container):
766-
memo = _get_providers_memo(copied_container.cls_providers, container.providers)
762+
def _decorator(new_container):
763+
memo = {}
764+
memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
765+
766+
new_providers = {}
767+
new_providers.update(providers.deepcopy(base_container.providers, memo))
768+
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
767769

768-
providers_copy = providers.deepcopy(container.providers, memo)
769-
for name, provider in six.iteritems(providers_copy):
770-
setattr(copied_container, name, provider)
770+
for name, provider in six.iteritems(new_providers):
771+
setattr(new_container, name, provider)
772+
return new_container
771773

772-
return copied_container
773774
return _decorator
774775

775776

tests/typing/declarative_container.py

+8
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ class Container6(containers.DeclarativeContainer):
5656

5757

5858
container6: containers.Container = Container6()
59+
60+
61+
# Test 7: to override()
62+
class Container7(containers.DeclarativeContainer):
63+
provider = providers.Factory(str)
64+
65+
container7 = Container7()
66+
container7.override_providers(provider='new_value')

tests/unit/containers/test_declarative_py2_py3.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -308,17 +308,31 @@ class _Container2(_Container):
308308
self.assertIsNot(_Container1.p11, _Container2.p11)
309309
self.assertIsNot(_Container1.p12, _Container2.p12)
310310

311-
self.assertIs(_Container.p12.kwargs['p11'], _Container.p11)
312-
self.assertIs(_Container1.p12.kwargs['p11'], _Container1.p11)
313-
self.assertIs(_Container2.p12.kwargs['p11'], _Container2.p11)
314-
315-
self.assertEqual(_Container.p12(), dict(p11=0))
316-
self.assertEqual(_Container1.p12(), dict(p11=1))
317-
self.assertEqual(_Container2.p12(), dict(p11=2))
311+
self.assertEqual(_Container.p12(), {'p11': 0})
312+
self.assertEqual(_Container1.p12(), {'p11': 1})
313+
self.assertEqual(_Container2.p12(), {'p11': 2})
318314

319315
self.assertEqual(_Container1.p13(), 11)
320316
self.assertEqual(_Container2.p13(), 22)
321317

318+
def test_copy_with_parent_dependency(self):
319+
# See: https://github.com/ets-labs/python-dependency-injector/issues/477
320+
class Base(containers.DeclarativeContainer):
321+
p11 = providers.Object(0)
322+
p12 = providers.Factory(dict, p11=p11)
323+
324+
@containers.copy(Base)
325+
class New(Base):
326+
p13 = providers.Factory(dict, p12=Base.p12)
327+
328+
new1 = New()
329+
new2 = New(p11=1)
330+
new3 = New(p11=2)
331+
332+
self.assertEqual(new1.p13(), {'p12': {'p11': 0}})
333+
self.assertEqual(new2.p13(), {'p12': {'p11': 1}})
334+
self.assertEqual(new3.p13(), {'p12': {'p11': 2}})
335+
322336
def test_copy_with_replacing_subcontainer_providers(self):
323337
# See: https://github.com/ets-labs/python-dependency-injector/issues/374
324338
class X(containers.DeclarativeContainer):

0 commit comments

Comments
 (0)