"""Providers module.""" try: import asyncio except ImportError: asyncio = None import functools cimport cython # Base providers cdef class Provider(object): cdef tuple __overridden cdef Provider __last_overriding cdef tuple __overrides cdef int __async_mode cpdef object _provide(self, tuple args, dict kwargs) cpdef void _copy_overridings(self, Provider copied, dict memo) cdef class Object(Provider): cdef object __provides cpdef object _provide(self, tuple args, dict kwargs) cdef class Self(Provider): cdef object __container cdef tuple __alt_names cdef class Delegate(Provider): cdef object __provides cpdef object _provide(self, tuple args, dict kwargs) cdef class Dependency(Provider): cdef object __instance_of cdef object __default cdef object __parent cdef class ExternalDependency(Dependency): pass cdef class DependenciesContainer(Object): cdef dict __providers cdef object __parent cpdef object _override_providers(self, object container) # Callable providers cdef class Callable(Provider): cdef object __provides cdef tuple __args cdef int __args_len cdef tuple __kwargs cdef int __kwargs_len cpdef object _provide(self, tuple args, dict kwargs) cdef class DelegatedCallable(Callable): pass cdef class AbstractCallable(Callable): cpdef object _provide(self, tuple args, dict kwargs) cdef class CallableDelegate(Delegate): pass # Coroutine providers cdef class Coroutine(Callable): pass cdef class DelegatedCoroutine(Coroutine): pass cdef class AbstractCoroutine(Coroutine): cpdef object _provide(self, tuple args, dict kwargs) cdef class CoroutineDelegate(Delegate): pass # Configuration providers cdef class ConfigurationOption(Provider): cdef tuple __name cdef Configuration __root cdef dict __children cdef bint __required cdef object __cache cdef class TypedConfigurationOption(Callable): pass cdef class Configuration(Object): cdef str __name cdef bint __strict cdef dict __children cdef object __weakref__ # Factory providers cdef class Factory(Provider): cdef Callable __instantiator cdef tuple __attributes cdef int __attributes_len cpdef object _provide(self, tuple args, dict kwargs) cdef class DelegatedFactory(Factory): pass cdef class AbstractFactory(Factory): cpdef object _provide(self, tuple args, dict kwargs) cdef class FactoryDelegate(Delegate): pass cdef class FactoryAggregate(Provider): cdef dict __factories cdef Factory __get_factory(self, str factory_name) # Singleton providers cdef class BaseSingleton(Provider): cdef Factory __instantiator cdef object __storage cdef class Singleton(BaseSingleton): cpdef object _provide(self, tuple args, dict kwargs) cdef class DelegatedSingleton(Singleton): pass cdef class ThreadSafeSingleton(BaseSingleton): cdef object __storage_lock cpdef object _provide(self, tuple args, dict kwargs) cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton): pass cdef class ThreadLocalSingleton(BaseSingleton): cpdef object _provide(self, tuple args, dict kwargs) cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton): pass cdef class AbstractSingleton(BaseSingleton): pass cdef class SingletonDelegate(Delegate): pass # Miscellaneous providers cdef class List(Provider): cdef tuple __args cdef int __args_len cpdef object _provide(self, tuple args, dict kwargs) cdef class Dict(Provider): cdef tuple __kwargs cdef int __kwargs_len cpdef object _provide(self, tuple args, dict kwargs) cdef class Resource(Provider): cdef object __provides cdef bint __initialized cdef object __shutdowner cdef object __resource cdef tuple __args cdef int __args_len cdef tuple __kwargs cdef int __kwargs_len cpdef object _provide(self, tuple args, dict kwargs) cdef class Container(Provider): cdef object __container_cls cdef dict __overriding_providers cdef object __container cdef object __parent cpdef object _provide(self, tuple args, dict kwargs) cdef class Selector(Provider): cdef object __selector cdef dict __providers cpdef object _provide(self, tuple args, dict kwargs) # Provided instance cdef class ProvidedInstance(Provider): cdef object __provides cpdef object _provide(self, tuple args, dict kwargs) cdef class AttributeGetter(Provider): cdef object __provides cdef object __name cpdef object _provide(self, tuple args, dict kwargs) cdef class ItemGetter(Provider): cdef object __provides cdef object __name cpdef object _provide(self, tuple args, dict kwargs) cdef class MethodCaller(Provider): cdef object __provides cdef tuple __args cdef int __args_len cdef tuple __kwargs cdef int __kwargs_len cpdef object _provide(self, tuple args, dict kwargs) # Injections cdef class Injection(object): cdef object __value cdef int __is_provider cdef int __is_delegated cdef int __call cdef class PositionalInjection(Injection): pass cdef class NamedInjection(Injection): cdef object __name cpdef tuple parse_positional_injections(tuple args) cpdef tuple parse_named_injections(dict kwargs) # Utils cdef class OverridingContext(object): cdef Provider __overridden cdef Provider __overriding cdef class BaseSingletonResetContext(object): cdef object __singleton cdef class SingletonResetContext(BaseSingletonResetContext): pass cdef class SingletonFullResetContext(BaseSingletonResetContext): pass cdef object CLASS_TYPES cpdef bint is_provider(object instance) cpdef object ensure_is_provider(object instance) cpdef bint is_delegated(object instance) cpdef str represent_provider(object provider, object provides) cpdef bint is_container_instance(object instance) cpdef bint is_container_class(object instance) cpdef object deepcopy(object instance, dict memo=*) # Inline helper functions cdef inline object __get_name(NamedInjection self): return self.__name cdef inline object __get_value(Injection self): if self.__call == 0: return self.__value return self.__value() cdef inline object __get_value_kwargs(Injection self, dict kwargs): if self.__call == 0: return self.__value return self.__value(**kwargs) cdef inline tuple __separate_prefixed_kwargs(dict kwargs): cdef dict plain_kwargs = {} cdef dict prefixed_kwargs = {} for key, value in kwargs.items(): if '__' not in key: plain_kwargs[key] = value continue index = key.index('__') prefix, name = key[:index], key[index+2:] if prefix not in prefixed_kwargs: prefixed_kwargs[prefix] = {} prefixed_kwargs[prefix][name] = value return plain_kwargs, prefixed_kwargs @cython.boundscheck(False) @cython.wraparound(False) cdef inline object __provide_positional_args( tuple args, tuple inj_args, int inj_args_len, ): cdef int index cdef list positional_args = [] cdef list future_args = [] cdef PositionalInjection injection if inj_args_len == 0: return args for index in range(inj_args_len): injection = inj_args[index] value = __get_value(injection) positional_args.append(value) if __is_future_or_coroutine(value): future_args.append((index, value)) positional_args.extend(args) if future_args: return __combine_future_injections(positional_args, future_args) return positional_args @cython.boundscheck(False) @cython.wraparound(False) cdef inline object __provide_keyword_args( dict kwargs, tuple inj_kwargs, int inj_kwargs_len, ): cdef int index cdef object name cdef object value cdef dict prefixed = {} cdef list future_kwargs = [] cdef NamedInjection kw_injection if len(kwargs) == 0: for index in range(inj_kwargs_len): kw_injection = inj_kwargs[index] name = __get_name(kw_injection) value = __get_value(kw_injection) kwargs[name] = value if __is_future_or_coroutine(value): future_kwargs.append((name, value)) else: kwargs, prefixed = __separate_prefixed_kwargs(kwargs) for index in range(inj_kwargs_len): kw_injection = inj_kwargs[index] name = __get_name(kw_injection) if name in kwargs: continue if name in prefixed: value = __get_value_kwargs(kw_injection, prefixed[name]) else: value = __get_value(kw_injection) kwargs[name] = value if __is_future_or_coroutine(value): future_kwargs.append((name, value)) if future_kwargs: return __combine_future_injections(kwargs, future_kwargs) return kwargs cdef inline object __combine_future_injections(object injections, list future_injections): future_result = asyncio.Future() injections_ready = asyncio.gather(*[value for _, value in future_injections]) injections_ready.add_done_callback( functools.partial( __async_prepare_args_kwargs_callback, future_result, injections, future_injections, ), ) asyncio.ensure_future(injections_ready) return future_result cdef inline void __async_prepare_args_kwargs_callback( object future_result, object args, object future_args_kwargs, object future, ): try: result = future.result() for value, (key, _) in zip(result, future_args_kwargs): args[key] = value except Exception as exception: future_result.set_exception(exception) else: future_result.set_result(args) @cython.boundscheck(False) @cython.wraparound(False) cdef inline object __provide_attributes(tuple attributes, int attributes_len): cdef NamedInjection attr_injection cdef dict attribute_injections = {} cdef list future_attributes = [] for index in range(attributes_len): attr_injection = attributes[index] name = __get_name(attr_injection) value = __get_value(attr_injection) attribute_injections[name] = value if __is_future_or_coroutine(value): future_attributes.append((name, value)) if future_attributes: return __combine_future_injections(attribute_injections, future_attributes) return attribute_injections cdef inline object __async_inject_attributes(future_instance, future_attributes): future_result = asyncio.Future() attributes_ready = asyncio.gather(future_instance, future_attributes) attributes_ready.add_done_callback( functools.partial( __async_inject_attributes_callback, future_result, ), ) asyncio.ensure_future(attributes_ready) return future_result cdef inline void __async_inject_attributes_callback(object future_result, object future): try: instance, attributes = future.result() for name, value in attributes.items(): setattr(instance, name, value) except Exception as exception: future_result.set_exception(exception) else: future_result.set_result(instance) cdef inline void __inject_attributes(object instance, dict attributes): for name, value in attributes.items(): setattr(instance, name, value) cdef inline object __call( object call, tuple context_args, tuple injection_args, int injection_args_len, dict context_kwargs, tuple injection_kwargs, int injection_kwargs_len, ): args = __provide_positional_args( context_args, injection_args, injection_args_len, ) kwargs = __provide_keyword_args( context_kwargs, injection_kwargs, injection_kwargs_len, ) is_future_args = __is_future_or_coroutine(args) is_future_kwargs = __is_future_or_coroutine(kwargs) if is_future_args or is_future_kwargs: future_args = args if is_future_args else __future_result(args) future_kwargs = kwargs if is_future_kwargs else __future_result(kwargs) future_result = asyncio.Future() args_kwargs_ready = asyncio.gather(future_args, future_kwargs) args_kwargs_ready.add_done_callback( functools.partial( __async_call_callback, future_result, call, ), ) asyncio.ensure_future(args_kwargs_ready) return future_result return call(*args, **kwargs) cdef inline void __async_call_callback(object future_result, object call, object future): try: args, kwargs = future.result() result = call(*args, **kwargs) except Exception as exception: future_result.set_exception(exception) else: if __is_future_or_coroutine(result): result = asyncio.ensure_future(result) result.add_done_callback(functools.partial(__async_result_callback, future_result)) return future_result.set_result(result) cdef inline object __async_result_callback(object future_result, object future): try: result = future.result() except Exception as exception: future_result.set_exception(exception) else: future_result.set_result(result) cdef inline object __callable_call(Callable self, tuple args, dict kwargs): return __call( self.__provides, args, self.__args, self.__args_len, kwargs, self.__kwargs, self.__kwargs_len, ) cdef inline object __factory_call(Factory self, tuple args, dict kwargs): cdef object instance instance = __callable_call(self.__instantiator, args, kwargs) if self.__attributes_len > 0: attributes = __provide_attributes(self.__attributes, self.__attributes_len) is_future_instance = __is_future_or_coroutine(instance) is_future_attributes = __is_future_or_coroutine(attributes) if is_future_instance or is_future_attributes: future_instance = instance if is_future_instance else __future_result(instance) future_attributes = attributes if is_future_attributes else __future_result(attributes) return __async_inject_attributes(future_instance, future_attributes) __inject_attributes(instance, attributes) return instance cdef inline bint __is_future_or_coroutine(object instance): if asyncio is None: return False return asyncio.isfuture(instance) or asyncio.iscoroutine(instance) cdef inline object __future_result(object instance): future_result = asyncio.Future() future_result.set_result(instance) return future_result