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 From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ 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 4.35.2
------ ------
- Update wiring to support modules provided as packages. - Update wiring to support modules provided as packages.

View File

@ -1,6 +1,6 @@
"""Top-level package.""" """Top-level package."""
__version__ = '4.35.2' __version__ = '4.35.3'
"""Version number. """Version number.
:type: str :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_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: Provider) -> None: ... def override_providers(self, **overriding_providers: Union[Provider, Any]) -> None: ...
def reset_last_overriding(self) -> None: ... def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ... def reset_override(self) -> None: ...
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> 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 return _decorator
def copy(object container): def copy(object base_container):
""":py:class:`DeclarativeContainer` copying decorator. """:py:class:`DeclarativeContainer` copying decorator.
This decorator copies all providers from provided container to decorated one. This decorator copies all providers from provided container to decorated one.
If one of the decorated container providers matches to source container If one of the decorated container providers matches to source container
providers by name, it would be replaced by reference. providers by name, it would be replaced by reference.
:param container: Container that should be copied by decorated container. :param base_container: Container that should be copied by decorated container.
:type container: :py:class:`DeclarativeContainer` :type base_container: :py:class:`DeclarativeContainer`
:return: Declarative container's copying decorator. :return: Declarative container's copying decorator.
:rtype: callable(:py:class:`DeclarativeContainer`) :rtype: callable(:py:class:`DeclarativeContainer`)
""" """
def _get_providers_memo(from_providers, source_providers): def _get_memo_for_matching_names(new_providers, base_providers):
memo = dict() memo = {}
for new_provider_name, new_provider in six.iteritems(new_providers):
for name, provider in from_providers.items(): if new_provider_name not in base_providers:
try:
source_provider = source_providers[name]
except KeyError:
continue continue
else: source_provider = base_providers[new_provider_name]
memo[id(source_provider)] = provider memo[id(source_provider)] = new_provider
if hasattr(provider, 'providers') and hasattr(source_provider, 'providers'): if hasattr(new_provider, 'providers') and hasattr(source_provider, 'providers'):
sub_memo = _get_providers_memo(provider.providers, source_provider.providers) sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
memo.update(sub_memo) memo.update(sub_memo)
return memo return memo
def _decorator(copied_container): def _decorator(new_container):
memo = _get_providers_memo(copied_container.cls_providers, container.providers) memo = {}
memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
providers_copy = providers.deepcopy(container.providers, memo) new_providers = {}
for name, provider in six.iteritems(providers_copy): new_providers.update(providers.deepcopy(base_container.providers, memo))
setattr(copied_container, name, provider) 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 return _decorator

View File

@ -56,3 +56,11 @@ class Container6(containers.DeclarativeContainer):
container6: containers.Container = Container6() 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.p11, _Container2.p11)
self.assertIsNot(_Container1.p12, _Container2.p12) self.assertIsNot(_Container1.p12, _Container2.p12)
self.assertIs(_Container.p12.kwargs['p11'], _Container.p11) self.assertEqual(_Container.p12(), {'p11': 0})
self.assertIs(_Container1.p12.kwargs['p11'], _Container1.p11) self.assertEqual(_Container1.p12(), {'p11': 1})
self.assertIs(_Container2.p12.kwargs['p11'], _Container2.p11) self.assertEqual(_Container2.p12(), {'p11': 2})
self.assertEqual(_Container.p12(), dict(p11=0))
self.assertEqual(_Container1.p12(), dict(p11=1))
self.assertEqual(_Container2.p12(), dict(p11=2))
self.assertEqual(_Container1.p13(), 11) self.assertEqual(_Container1.p13(), 11)
self.assertEqual(_Container2.p13(), 22) 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): def test_copy_with_replacing_subcontainer_providers(self):
# See: https://github.com/ets-labs/python-dependency-injector/issues/374 # See: https://github.com/ets-labs/python-dependency-injector/issues/374
class X(containers.DeclarativeContainer): class X(containers.DeclarativeContainer):