367 Fix declarative container metaclass bug with child providers

This commit is contained in:
Roman Mogylatov 2021-01-29 16:37:50 -05:00
parent f188811d87
commit 39cb963351
5 changed files with 2181 additions and 11007 deletions

View File

@ -7,6 +7,12 @@ 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`_
Development version
-------------------
- Fix declarative container metaclass bug: parent container providers replaced child container providers.
See issue: `#367 <https://github.com/ets-labs/python-dependency-injector/issues/367>`_.
Many thanks to `Shaun Cutts <https://github.com/shaunc>`_ for finding and report the issue.
4.13.0 4.13.0
------ ------
- Add ``default`` argument to the dependency provider: ``Dependency(..., default=...)``. - Add ``default`` argument to the dependency provider: ``Dependency(..., default=...)``.

File diff suppressed because it is too large Load Diff

View File

@ -284,34 +284,33 @@ class DeclarativeContainerMetaClass(type):
def __new__(type mcs, str class_name, tuple bases, dict attributes): def __new__(type mcs, str class_name, tuple bases, dict attributes):
"""Declarative container class factory.""" """Declarative container class factory."""
cdef tuple cls_providers containers = {
cdef tuple inherited_providers name: container
cdef type cls
containers = tuple(
(name, container)
for name, container in six.iteritems(attributes) for name, container in six.iteritems(attributes)
if is_container(container) if is_container(container)
) }
attributes['containers'] = dict(containers) cls_providers = {
name: provider
cls_providers = tuple(
(name, provider)
for name, provider in six.iteritems(attributes) for name, provider in six.iteritems(attributes)
if isinstance(provider, Provider) if isinstance(provider, Provider)
) }
inherited_providers = tuple( inherited_providers = {
(name, provider) name: provider
for base in bases for base in bases
if is_container(base) and base is not DynamicContainer if is_container(base) and base is not DynamicContainer
for name, provider in six.iteritems(base.providers) for name, provider in six.iteritems(base.providers)
) }
attributes['cls_providers'] = dict(cls_providers) providers = {}
attributes['inherited_providers'] = dict(inherited_providers) providers.update(inherited_providers)
attributes['providers'] = dict(cls_providers + inherited_providers) providers.update(cls_providers)
attributes['containers'] = containers
attributes['inherited_providers'] = inherited_providers
attributes['cls_providers'] = cls_providers
attributes['providers'] = providers
cls = <type>type.__new__(mcs, class_name, bases, attributes) cls = <type>type.__new__(mcs, class_name, bases, attributes)

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,29 @@ class DeclarativeContainerTests(unittest.TestCase):
p31=ContainerC.p31, p31=ContainerC.p31,
p32=ContainerC.p32)) p32=ContainerC.p32))
def test_providers_attribute_with_redefinition(self):
p1 = providers.Provider()
p2 = providers.Provider()
class ContainerA2(ContainerA):
p11 = p1
p12 = p2
self.assertEqual(
ContainerA.providers,
{
'p11': ContainerA.p11,
'p12': ContainerA.p12,
},
)
self.assertEqual(
ContainerA2.providers,
{
'p11': p1,
'p12': p2,
},
)
def test_cls_providers_attribute(self): def test_cls_providers_attribute(self):
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11, self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
p12=ContainerA.p12)) p12=ContainerA.p12))