477 Containers @copy fix and refactoring (#485)
* Rename local variables * Make code layout enhancements * Add fix and tests * Add more refactoring * Update changelog
This commit is contained in:
parent
cde7dee4b3
commit
7b19fa0964
|
@ -7,6 +7,12 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
Dev version
|
||||
------
|
||||
- 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.
|
||||
|
||||
4.35.2
|
||||
------
|
||||
- Update wiring to support modules provided as packages.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
memo.update(sub_memo)
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue