Merge branch 'release/4.35.3' into master

This commit is contained in:
Roman Mogylatov 2021-08-11 21:25:25 -04:00
commit 48df949cd5
7 changed files with 582 additions and 518 deletions

View File

@ -7,6 +7,13 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
4.35.3
------
- Fix ``@containers.copy()`` decorator to respect dependencies on parent providers.
See issue `#477 <https://github.com/ets-labs/python-dependency-injector/issues/477>`_.
Thanks to `Andrey Torsunov @gtors <https://github.com/gtors>`_ for reporting the issue.
- Fix typing stub for ``container.override_providers()`` to accept other types besides ``Provider``.
4.35.2
------
- Update wiring to support modules provided as packages.

View File

@ -1,6 +1,6 @@
"""Top-level package."""
__version__ = '4.35.2'
__version__ = '4.35.3'
"""Version number.
:type: str

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,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: Provider) -> None: ...
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> None: ...
def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ...
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> None: ...

View File

@ -733,43 +733,44 @@ def override(object container):
return _decorator
def copy(object container):
def copy(object base_container):
""":py:class:`DeclarativeContainer` copying decorator.
This decorator copies all providers from provided container to decorated one.
If one of the decorated container providers matches to source container
providers by name, it would be replaced by reference.
:param container: Container that should be copied by decorated container.
:type container: :py:class:`DeclarativeContainer`
:param base_container: Container that should be copied by decorated container.
:type base_container: :py:class:`DeclarativeContainer`
:return: Declarative container's copying decorator.
:rtype: callable(:py:class:`DeclarativeContainer`)
"""
def _get_providers_memo(from_providers, source_providers):
memo = dict()
for name, provider in from_providers.items():
try:
source_provider = source_providers[name]
except KeyError:
def _get_memo_for_matching_names(new_providers, base_providers):
memo = {}
for new_provider_name, new_provider in six.iteritems(new_providers):
if new_provider_name not in base_providers:
continue
else:
memo[id(source_provider)] = provider
source_provider = base_providers[new_provider_name]
memo[id(source_provider)] = new_provider
if hasattr(provider, 'providers') and hasattr(source_provider, 'providers'):
sub_memo = _get_providers_memo(provider.providers, source_provider.providers)
if hasattr(new_provider, 'providers') and hasattr(source_provider, 'providers'):
sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
memo.update(sub_memo)
return memo
def _decorator(copied_container):
memo = _get_providers_memo(copied_container.cls_providers, container.providers)
def _decorator(new_container):
memo = {}
memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
providers_copy = providers.deepcopy(container.providers, memo)
for name, provider in six.iteritems(providers_copy):
setattr(copied_container, name, provider)
new_providers = {}
new_providers.update(providers.deepcopy(base_container.providers, memo))
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
for name, provider in six.iteritems(new_providers):
setattr(new_container, name, provider)
return new_container
return copied_container
return _decorator

View File

@ -56,3 +56,11 @@ class Container6(containers.DeclarativeContainer):
container6: containers.Container = Container6()
# Test 7: to override()
class Container7(containers.DeclarativeContainer):
provider = providers.Factory(str)
container7 = Container7()
container7.override_providers(provider='new_value')

View File

@ -308,17 +308,31 @@ class DeclarativeContainerTests(unittest.TestCase):
self.assertIsNot(_Container1.p11, _Container2.p11)
self.assertIsNot(_Container1.p12, _Container2.p12)
self.assertIs(_Container.p12.kwargs['p11'], _Container.p11)
self.assertIs(_Container1.p12.kwargs['p11'], _Container1.p11)
self.assertIs(_Container2.p12.kwargs['p11'], _Container2.p11)
self.assertEqual(_Container.p12(), dict(p11=0))
self.assertEqual(_Container1.p12(), dict(p11=1))
self.assertEqual(_Container2.p12(), dict(p11=2))
self.assertEqual(_Container.p12(), {'p11': 0})
self.assertEqual(_Container1.p12(), {'p11': 1})
self.assertEqual(_Container2.p12(), {'p11': 2})
self.assertEqual(_Container1.p13(), 11)
self.assertEqual(_Container2.p13(), 22)
def test_copy_with_parent_dependency(self):
# See: https://github.com/ets-labs/python-dependency-injector/issues/477
class Base(containers.DeclarativeContainer):
p11 = providers.Object(0)
p12 = providers.Factory(dict, p11=p11)
@containers.copy(Base)
class New(Base):
p13 = providers.Factory(dict, p12=Base.p12)
new1 = New()
new2 = New(p11=1)
new3 = New(p11=2)
self.assertEqual(new1.p13(), {'p12': {'p11': 0}})
self.assertEqual(new2.p13(), {'p12': {'p11': 1}})
self.assertEqual(new3.p13(), {'p12': {'p11': 2}})
def test_copy_with_replacing_subcontainer_providers(self):
# See: https://github.com/ets-labs/python-dependency-injector/issues/374
class X(containers.DeclarativeContainer):