Implement lazy initialization and improve copying for Resource provider

This commit is contained in:
Roman Mogylatov 2021-03-09 08:26:02 -05:00
parent 17ecd6c8c0
commit f180f493fa
6 changed files with 3354 additions and 3037 deletions

View File

@ -1339,12 +1339,12 @@ struct __pyx_obj_19dependency_injector_9providers_Dict {
* *
* *
* cdef class Resource(Provider): # <<<<<<<<<<<<<< * cdef class Resource(Provider): # <<<<<<<<<<<<<<
* cdef object __initializer * cdef object __provides
* cdef bint __initialized * cdef bint __initialized
*/ */
struct __pyx_obj_19dependency_injector_9providers_Resource { struct __pyx_obj_19dependency_injector_9providers_Resource {
struct __pyx_obj_19dependency_injector_9providers_Provider __pyx_base; struct __pyx_obj_19dependency_injector_9providers_Provider __pyx_base;
PyObject *__pyx___initializer; PyObject *__pyx___provides;
int __pyx___initialized; int __pyx___initialized;
PyObject *__pyx___shutdowner; PyObject *__pyx___shutdowner;
PyObject *__pyx___resource; PyObject *__pyx___resource;
@ -2102,7 +2102,7 @@ static struct __pyx_vtabstruct_19dependency_injector_9providers_Dict *__pyx_vtab
* *
* *
* cdef class Resource(Provider): # <<<<<<<<<<<<<< * cdef class Resource(Provider): # <<<<<<<<<<<<<<
* cdef object __initializer * cdef object __provides
* cdef bint __initialized * cdef bint __initialized
*/ */

File diff suppressed because it is too large Load Diff

View File

@ -203,7 +203,7 @@ cdef class Dict(Provider):
cdef class Resource(Provider): cdef class Resource(Provider):
cdef object __initializer cdef object __provides
cdef bint __initialized cdef bint __initialized
cdef object __shutdowner cdef object __shutdowner
cdef object __resource cdef object __resource

View File

@ -351,19 +351,20 @@ class Dict(Provider[_Dict]):
class Resource(Provider[T]): class Resource(Provider[T]):
@overload @overload
def __init__(self, initializer: Type[resources.Resource[T]], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[Type[resources.Resource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload @overload
def __init__(self, initializer: Type[resources.AsyncResource[T]], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[Type[resources.AsyncResource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload @overload
def __init__(self, initializer: _Callable[..., _Iterator[T]], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[_Callable[..., _Iterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload @overload
def __init__(self, initializer: _Callable[..., _AsyncIterator[T]], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[_Callable[..., _AsyncIterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload @overload
def __init__(self, initializer: _Callable[..., _Coroutine[Injection, Injection, T]], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload @overload
def __init__(self, initializer: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ... def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@property @property
def initializer(self) -> _Callable[..., Any]: ... def provides(self) -> Optional[_Callable[..., Any]]: ...
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
@property @property
def args(self) -> Tuple[Injection]: ... def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Resource[T]: ... def add_args(self, *args: Injection) -> Resource[T]: ...

View File

@ -3218,8 +3218,10 @@ cdef class Dict(Provider):
cdef class Resource(Provider): cdef class Resource(Provider):
"""Resource provider provides a component with initialization and shutdown.""" """Resource provider provides a component with initialization and shutdown."""
def __init__(self, initializer, *args, **kwargs): def __init__(self, provides=None, *args, **kwargs):
self.__initializer = initializer self.__provides = None
self.set_provides(provides)
self.__initialized = False self.__initialized = False
self.__resource = None self.__resource = None
self.__shutdowner = None self.__shutdowner = None
@ -3243,11 +3245,11 @@ cdef class Resource(Provider):
if self.__initialized: if self.__initialized:
raise Error('Can not copy initialized resource') raise Error('Can not copy initialized resource')
copied = self.__class__( copied = _memorized_duplicate(self, memo)
self.__initializer, copied.set_provides(_copy_if_provider(self.provides, memo))
*deepcopy(self.args, memo), copied.set_args(*deepcopy(self.args, memo))
**deepcopy(self.kwargs, memo), copied.set_kwargs(**deepcopy(self.kwargs, memo))
)
self._copy_overridings(copied, memo) self._copy_overridings(copied, memo)
return copied return copied
@ -3257,12 +3259,17 @@ cdef class Resource(Provider):
:rtype: str :rtype: str
""" """
return represent_provider(provider=self, provides=self.__initializer) return represent_provider(provider=self, provides=self.provides)
@property @property
def initializer(self): def provides(self):
"""Return initializer.""" """Return provider's provides."""
return self.__initializer return self.__provides
def set_provides(self, provides):
"""Set provider's provides."""
self.__provides = provides
return self
@property @property
def args(self): def args(self):
@ -3387,7 +3394,7 @@ cdef class Resource(Provider):
@property @property
def related(self): def related(self):
"""Return related providers generator.""" """Return related providers generator."""
yield from filter(is_provider, [self.__initializer]) yield from filter(is_provider, [self.provides])
yield from filter(is_provider, self.args) yield from filter(is_provider, self.args)
yield from filter(is_provider, self.kwargs.values()) yield from filter(is_provider, self.kwargs.values())
yield from super().related yield from super().related
@ -3396,8 +3403,8 @@ cdef class Resource(Provider):
if self.__initialized: if self.__initialized:
return self.__resource return self.__resource
if self._is_resource_subclass(self.__initializer): if self._is_resource_subclass(self.__provides):
initializer = self.__initializer() initializer = self.__provides()
self.__resource = __call( self.__resource = __call(
initializer.init, initializer.init,
args, args,
@ -3408,8 +3415,8 @@ cdef class Resource(Provider):
self.__kwargs_len, self.__kwargs_len,
) )
self.__shutdowner = initializer.shutdown self.__shutdowner = initializer.shutdown
elif self._is_async_resource_subclass(self.__initializer): elif self._is_async_resource_subclass(self.__provides):
initializer = self.__initializer() initializer = self.__provides()
async_init = __call( async_init = __call(
initializer.init, initializer.init,
args, args,
@ -3421,9 +3428,9 @@ cdef class Resource(Provider):
) )
self.__initialized = True self.__initialized = True
return self._create_init_future(async_init, initializer.shutdown) return self._create_init_future(async_init, initializer.shutdown)
elif inspect.isgeneratorfunction(self.__initializer): elif inspect.isgeneratorfunction(self.__provides):
initializer = __call( initializer = __call(
self.__initializer, self.__provides,
args, args,
self.__args, self.__args,
self.__args_len, self.__args_len,
@ -3433,9 +3440,9 @@ cdef class Resource(Provider):
) )
self.__resource = next(initializer) self.__resource = next(initializer)
self.__shutdowner = initializer.send self.__shutdowner = initializer.send
elif iscoroutinefunction(self.__initializer): elif iscoroutinefunction(self.__provides):
initializer = __call( initializer = __call(
self.__initializer, self.__provides,
args, args,
self.__args, self.__args,
self.__args_len, self.__args_len,
@ -3445,9 +3452,9 @@ cdef class Resource(Provider):
) )
self.__initialized = True self.__initialized = True
return self._create_init_future(initializer) return self._create_init_future(initializer)
elif isasyncgenfunction(self.__initializer): elif isasyncgenfunction(self.__provides):
initializer = __call( initializer = __call(
self.__initializer, self.__provides,
args, args,
self.__args, self.__args,
self.__args_len, self.__args_len,
@ -3457,9 +3464,9 @@ cdef class Resource(Provider):
) )
self.__initialized = True self.__initialized = True
return self._create_async_gen_init_future(initializer) return self._create_async_gen_init_future(initializer)
elif callable(self.__initializer): elif callable(self.__provides):
self.__resource = __call( self.__resource = __call(
self.__initializer, self.__provides,
args, args,
self.__args, self.__args,
self.__args_len, self.__args_len,
@ -4506,6 +4513,12 @@ cpdef object _memorized_duplicate(object instance, dict memo):
return copied return copied
cpdef object _copy_if_provider(object instance, dict memo):
if not is_provider(instance):
return instance
return deepcopy(instance, memo)
cpdef str _class_qualname(object instance): cpdef str _class_qualname(object instance):
name = getattr(instance.__class__, '__qualname__', None) name = getattr(instance.__class__, '__qualname__', None)
if not name: if not name:

View File

@ -29,6 +29,16 @@ class ResourceTests(unittest.TestCase):
def test_is_provider(self): def test_is_provider(self):
self.assertTrue(providers.is_provider(providers.Resource(init_fn))) self.assertTrue(providers.is_provider(providers.Resource(init_fn)))
def test_init_optional_provides(self):
provider = providers.Resource()
provider.set_provides(init_fn)
self.assertIs(provider.provides, init_fn)
self.assertEqual(provider(), (tuple(), dict()))
def test_set_provides_returns_self(self):
provider = providers.Resource()
self.assertIs(provider.set_provides(init_fn), provider)
def test_provided_instance_provider(self): def test_provided_instance_provider(self):
provider = providers.Resource(init_fn) provider = providers.Resource(init_fn)
self.assertIsInstance(provider.provided, providers.ProvidedInstance) self.assertIsInstance(provider.provided, providers.ProvidedInstance)