""" RPCClient object is used to create RPC connection to services """ from __future__ import absolute_import from __future__ import division from __future__ import print_function __RCSID__ = "3c530efac (2020-12-05 00:59:33 +0100) Chris Burr " from DIRAC.Core.DISET.private.InnerRPCClient import InnerRPCClient class _MagicMethod(object): """ This object allows to bundle together a function calling an RPC and the remote function name. When this object is called (__call__), the call is performed. """ def __init__(self, doRPCFunc, remoteFuncName): """ Constructor :param doRPCFunc: the function actually performing the RPC call :param remoteFuncName: name of the remote function """ self.__doRPCFunc = doRPCFunc self.__remoteFuncName = remoteFuncName def __getattr__(self, remoteFuncName): """ I really do not understand when this would be called. I can only imagine it being called by dir, or things like that. In any case, it recursively return a MagicMethod object where the new remote function name is the old one to which we append the new called attribute. """ return _MagicMethod(self.__doRPCFunc, "%s.%s" % (self.__remoteFuncName, remoteFuncName)) def __call__(self, *args, **kwargs): """ Triggers the call. it uses the RPC calling function given by RPCClient, and gives as argument the remote function name and whatever arguments given. """ return self.__doRPCFunc(self.__remoteFuncName, args, **kwargs) def __str__(self): return "" % self.__remoteFuncName class RPCClient(object): """ This class contains the mechanism to convert normal calls to RPC calls. When instanciated, it creates a :class:`~DIRAC.Core.DISET.private.InnerRPCClient.InnerRPCClient` as an attribute. Any attribute which is accessed is then either redirected to InnerRPCClient if it has it, or creates a MagicMethod object otherwise. If the attribute is a function, MagicMethod will trigger the RPC call, using the InnerRPCClient. The typical workflow looks like this:: rpc = RPCClient('DataManagement/FileCatalog') # Here, func is the ping function, which we call remotely. # We go through RPCClient.__getattr__ which returns us a MagicMethod object func = rpc.ping # Here we call the method __call__ of the MagicMethod func() """ def __init__(self, *args, **kwargs): """ Constructor The arguments are just passed on to InnerRPCClient. In practice: * args: has to be the service name or URL * kwargs: all the arguments InnerRPCClient and BaseClient accept as configuration """ self.__innerRPCClient = InnerRPCClient(*args, **kwargs) def __doRPC(self, sFunctionName, args, **kwargs): """ Execute the RPC action. This is given as an attribute to MagicMethod :param sFunctionName: name of the remote function :param args: arguments to pass to the function """ return self.__innerRPCClient.executeRPC(sFunctionName, args, **kwargs) def __getattr__(self, attrName): """ Function for emulating the existence of functions. In literature this is usually called a "stub function". If the attribute exists in InnerRPCClient, return it, otherwise we create a _MagicMethod instance """ if attrName in dir(self.__innerRPCClient): return getattr(self.__innerRPCClient, attrName) return _MagicMethod(self.__doRPC, attrName) def executeRPCStub(rpcStub): """ Playback a stub """ # Generate a RPCClient with the same parameters rpcClient = RPCClient(rpcStub[0][0], **rpcStub[0][1]) # Get a functor to execute the RPC call rpcFunc = getattr(rpcClient, rpcStub[1]) # Reproduce the call return rpcFunc(*rpcStub[2])