"""Decorators and wrappers designed for wrapping :class:`BaseExecutor` functions. """importfunctoolsimportinspectfromfunctoolsimportwrapsfromtypingimportCallable,Union,List,Optional,Dict,Sequence,TYPE_CHECKINGfromjina.serve.executors.metasimportget_default_metasfromjina.helperimportconvert_tuple_to_list,iscoroutinefunctionifTYPE_CHECKING:fromjinaimportDocumentArray
[docs]defwrap_func(cls,func_lst,wrapper):"""Wrapping a class method only once, inherited but not overridden method will not be wrapped again :param cls: class :param func_lst: function list to wrap :param wrapper: the wrapper """forf_nameinfunc_lst:ifhasattr(cls,f_name)andall(getattr(cls,f_name)!=getattr(i,f_name,None)foriincls.mro()[1:]):setattr(cls,f_name,wrapper(getattr(cls,f_name)))
[docs]defstore_init_kwargs(func:Callable)->Callable:"""Mark the args and kwargs of :func:`__init__` later to be stored via :func:`save_config` in YAML :param func: the function to decorate :return: the wrapped function """@wraps(func)defarg_wrapper(self,*args,**kwargs):iffunc.__name__!='__init__':raiseTypeError('this decorator should only be used on __init__ method of an executor')taboo={'self','args','kwargs','metas','requests','runtime_args'}_defaults=get_default_metas()taboo.update(_defaults.keys())all_pars=inspect.signature(func).parameterstmp={k:v.defaultfork,vinall_pars.items()ifknotintaboo}tmp_list=[kforkinall_pars.keys()ifknotintaboo]# set args by aligning tmp_list with arg valuesfork,vinzip(tmp_list,args):tmp[k]=v# set kwargsfork,vinkwargs.items():ifkintmp:tmp[k]=vifhasattr(self,'_init_kwargs_dict'):self._init_kwargs_dict.update(tmp)else:self._init_kwargs_dict=tmpconvert_tuple_to_list(self._init_kwargs_dict)f=func(self,*args,**kwargs)returnfreturnarg_wrapper
[docs]defrequests(func:Callable[['DocumentArray',Dict,'DocumentArray',List['DocumentArray'],List['DocumentArray'],],Optional[Union['DocumentArray',Dict]],]=None,*,on:Optional[Union[str,Sequence[str]]]=None,):""" `@requests` defines when a function will be invoked. It has a keyword `on=` to define the endpoint. A class method decorated with plan `@requests` (without `on=`) is the default handler for all endpoints. That means, it is the fallback handler for endpoints that are not found. :param func: the method to decorate :param on: the endpoint string, by convention starts with `/` :return: decorated function """fromjinaimport__default_endpoint__,__args_executor_func__classFunctionMapper:def__init__(self,fn):arg_spec=inspect.getfullargspec(fn)ifnotarg_spec.varkwandnot__args_executor_func__.issubset(arg_spec.args):raiseTypeError(f'{fn} accepts only {arg_spec.args} which is fewer than expected, 'f'please add `**kwargs` to the function signature.')ifiscoroutinefunction(fn):@functools.wraps(fn)asyncdefarg_wrapper(*args,**kwargs):returnawaitfn(*args,**kwargs)self.fn=arg_wrapperelse:@functools.wraps(fn)defarg_wrapper(*args,**kwargs):returnfn(*args,**kwargs)self.fn=arg_wrapperdef__set_name__(self,owner,name):self.fn.class_name=owner.__name__ifnothasattr(owner,'requests'):owner.requests={}ifisinstance(on,(list,tuple)):foroinon:owner.requests[o]=self.fnelse:owner.requests[onor__default_endpoint__]=self.fnsetattr(owner,name,self.fn)iffunc:returnFunctionMapper(func)else:returnFunctionMapper