Implement lazy initialization and improve copying for MethodCaller provder

This commit is contained in:
Roman Mogylatov 2021-03-10 08:58:45 -05:00
parent 773a7d86f7
commit 052413d3f4
6 changed files with 1774 additions and 1441 deletions

View File

@ -1430,12 +1430,12 @@ struct __pyx_obj_19dependency_injector_9providers_ItemGetter {
*
*
* cdef class MethodCaller(Provider): # <<<<<<<<<<<<<<
* cdef Provider __provider
* cdef object __provides
* cdef tuple __args
*/
struct __pyx_obj_19dependency_injector_9providers_MethodCaller {
struct __pyx_obj_19dependency_injector_9providers_Provider __pyx_base;
struct __pyx_obj_19dependency_injector_9providers_Provider *__pyx___provider;
PyObject *__pyx___provides;
PyObject *__pyx___args;
int __pyx___args_len;
PyObject *__pyx___kwargs;
@ -2186,7 +2186,7 @@ static struct __pyx_vtabstruct_19dependency_injector_9providers_ItemGetter *__py
*
*
* cdef class MethodCaller(Provider): # <<<<<<<<<<<<<<
* cdef Provider __provider
* cdef object __provides
* cdef tuple __args
*/

File diff suppressed because it is too large Load Diff

View File

@ -255,7 +255,7 @@ cdef class ItemGetter(Provider):
cdef class MethodCaller(Provider):
cdef Provider __provider
cdef object __provides
cdef tuple __args
cdef int __args_len
cdef tuple __kwargs

View File

@ -447,7 +447,7 @@ class ItemGetter(Provider, ProvidedInstanceFluentInterface):
class MethodCaller(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provider: Provider, *args: Injection, **kwargs: Injection) -> None: ...
def __init__(self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection) -> None: ...
class OverridingContext(Generic[T]):

View File

@ -3948,7 +3948,7 @@ cdef class AttributeGetter(Provider):
super().__init__()
def __repr__(self):
return f'{self.__class__.__name__}(\'{self.__name}\')'
return f'{self.__class__.__name__}(\'{self.name}\')'
def __deepcopy__(self, memo):
copied = memo.get(id(self))
@ -4099,35 +4099,33 @@ cdef class MethodCaller(Provider):
You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
"""
def __init__(self, provider, *args, **kwargs):
self.__provider = provider
def __init__(self, provides=None, *args, **kwargs):
self.__provides = None
self.set_provides(provides)
self.__args = parse_positional_injections(args)
self.__args_len = len(self.__args)
self.__args = tuple()
self.__args_len = 0
self.set_args(*args)
self.__kwargs = parse_named_injections(kwargs)
self.__kwargs_len = len(self.__kwargs)
self.__kwargs = tuple()
self.__kwargs_len = 0
self.set_kwargs(**kwargs)
super().__init__()
def __repr__(self):
return f'{self.__class__.__name__}({self.__provider})'
def __deepcopy__(self, memo=None):
cdef MethodCaller copied
return f'{self.__class__.__name__}({self.provides})'
def __deepcopy__(self, memo):
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(deepcopy(self.__provider, memo))
copied.__args = deepcopy(self.__args, memo)
copied.__args_len = self.__args_len
copied.__kwargs = deepcopy(self.__kwargs, memo)
copied.__kwargs_len = self.__kwargs_len
copied = _memorized_duplicate(self, memo)
copied.set_provides(_copy_if_provider(self.provides, memo))
copied.set_args(*deepcopy(self.args, memo))
copied.set_kwargs(**deepcopy(self.kwargs, memo))
self._copy_overridings(copied, memo)
return copied
def __getattr__(self, item):
@ -4136,10 +4134,18 @@ cdef class MethodCaller(Provider):
def __getitem__(self, item):
return ItemGetter(self, item)
def call(self, *args, **kwargs):
return MethodCaller(self, *args, **kwargs)
@property
def provides(self):
"""Return provider."""
return self.__provider
"""Return provider's provides."""
return self.__provides
def set_provides(self, provides):
"""Set provider's provides."""
self.__provides = provides
return self
@property
def args(self):
@ -4154,6 +4160,17 @@ cdef class MethodCaller(Provider):
args.append(arg.__value)
return tuple(args)
def set_args(self, *args):
"""Set positional argument injections.
Existing positional argument injections are dropped.
:return: Reference ``self``
"""
self.__args = parse_positional_injections(args)
self.__args_len = len(self.__args)
return self
@property
def kwargs(self):
"""Return keyword argument injections."""
@ -4167,19 +4184,28 @@ cdef class MethodCaller(Provider):
kwargs[kwarg.__name] = kwarg.__value
return kwargs
def call(self, *args, **kwargs):
return MethodCaller(self, *args, **kwargs)
def set_kwargs(self, **kwargs):
"""Set keyword argument injections.
Existing keyword argument injections are dropped.
:return: Reference ``self``
"""
self.__kwargs = parse_named_injections(kwargs)
self.__kwargs_len = len(self.__kwargs)
return self
@property
def related(self):
"""Return related providers generator."""
yield self.__provider
if is_provider(self.provides):
yield self.provides
yield from filter(is_provider, self.args)
yield from filter(is_provider, self.kwargs.values())
yield from super().related
cpdef object _provide(self, tuple args, dict kwargs):
call = self.__provider()
call = self.provides()
if __is_future_or_coroutine(call):
future_result = asyncio.Future()
call = asyncio.ensure_future(call)

View File

@ -155,6 +155,14 @@ class LazyInitTests(unittest.TestCase):
self.assertIs(provider.set_provides(providers.Provider()), provider)
self.assertIs(provider.set_name('foo'), provider)
def test_method_caller(self):
provides = providers.Object(lambda: 42)
provider = providers.MethodCaller()
provider.set_provides(provides)
self.assertIs(provider.provides, provides)
self.assertEqual(provider(), 42)
self.assertIs(provider.set_provides(providers.Provider()), provider)
class ProvidedInstancePuzzleTests(unittest.TestCase):