235 Fix Delegate cannot be used in overridden container (#236)

* Add test for bug #235

* Fix issue + refactoring

* Update changelog
This commit is contained in:
Roman Mogylatov 2019-10-09 10:45:14 -04:00 committed by GitHub
parent fb264a8379
commit 08de710b2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 3268 additions and 2583 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`_
Development version
-------------------
+ Fix ``3.14.11`` degradation issue causing inability of using ``Delegate`` provider in
``DeclarativeContainer`` when this container is instantiated with overriding of delegating
provider (thanks to `GitterRemote <https://github .com/GitterRemote>`_, issue details are
`here <https://github.com/ets-labs/python-dependency-injector/issues/235>`_).
3.14.11
-------
+ Fix issue causing creation of a copy of provided object by ``Object`` provider when it was a

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,10 @@ cdef class Object(Provider):
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Delegate(Object):
pass
cdef class Delegate(Provider):
cdef object __provides
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Dependency(Provider):

View File

@ -291,7 +291,7 @@ cdef class Object(Provider):
return self.__provides
cdef class Delegate(Object):
cdef class Delegate(Provider):
"""Delegate provider returns provider "as is".
.. py:attribute:: provides
@ -305,9 +305,49 @@ cdef class Delegate(Object):
"""Initializer.
:param provides: Value that have to be provided.
:type provides: object
:type provides: :py:class:`Provider`
"""
super(Delegate, self).__init__(ensure_is_provider(provides))
self.__provides = ensure_is_provider(provides)
super(Delegate, self).__init__()
def __deepcopy__(self, memo):
"""Create and return full copy of provider."""
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(deepcopy(self.__provides, memo))
self._copy_overridings(copied, memo)
return copied
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.__provides)
def __repr__(self):
"""Return string representation of provider.
:rtype: str
"""
return self.__str__()
cpdef object _provide(self, tuple args, dict kwargs):
"""Return provided instance.
:param args: Tuple of context positional arguments.
:type args: tuple[object]
:param kwargs: Dictionary of context keyword arguments.
:type kwargs: dict[str, object]
:rtype: object
"""
return self.__provides
cdef class Dependency(Provider):
@ -834,7 +874,7 @@ cdef class CallableDelegate(Delegate):
if isinstance(callable, Callable) is False:
raise Error('{0} can wrap only {1} providers'.format(
self.__class__, Callable))
super(Delegate, self).__init__(callable)
super(CallableDelegate, self).__init__(callable)
cdef class Coroutine(Callable):
@ -1416,7 +1456,7 @@ cdef class FactoryDelegate(Delegate):
if isinstance(factory, Factory) is False:
raise Error('{0} can wrap only {1} providers'.format(
self.__class__, Factory))
super(Delegate, self).__init__(factory)
super(FactoryDelegate, self).__init__(factory)
cdef class FactoryAggregate(Provider):
@ -1923,7 +1963,7 @@ cdef class SingletonDelegate(Delegate):
if isinstance(singleton, BaseSingleton) is False:
raise Error('{0} can wrap only {1} providers'.format(
self.__class__, BaseSingleton))
super(Delegate, self).__init__(singleton)
super(SingletonDelegate, self).__init__(singleton)
cdef class Injection(object):

View File

@ -1,5 +1,6 @@
"""Dependency injector declarative container unit tests."""
import collections
import unittest2 as unittest
from dependency_injector import (
@ -313,3 +314,19 @@ class DeclarativeContainerTests(unittest.TestCase):
self.assertIs(container.p2.cls, container.p1)
self.assertIs(_Container.p2.cls, _Container.p1)
self.assertIsNot(container.p2.cls, _Container.p1)
def test_init_with_dependency_delegation(self):
# Bug:
# https://github.com/ets-labs/python-dependency-injector/issues/235
A = collections.namedtuple('A', [])
B = collections.namedtuple('B', ['fa'])
C = collections.namedtuple('B', ['a'])
class Services(containers.DeclarativeContainer):
a = providers.Dependency()
c = providers.Factory(C, a=a)
b = providers.Factory(B, fa=a.delegate())
a = providers.Factory(A)
assert isinstance(Services(a=a).c().a, A) # ok
Services(a=a).b().fa()