<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def call(self, method_name: str, *args, rpc_timeout: float = None, **kwargs):
 """ Send JSON RPC request to a backend socket and receive reply Note that this uses the default event loop to run in a blocking manner. If you would rather run in an async fashion or provide your own event loop then use .async_call instead :param method_name: Method name :param args: Args that will be passed to the remote function :param float rpc_timeout: Timeout in seconds for Server response, set to None to disable the timeout :param kwargs: Keyword args that will be passed to the remote function """

        request = utils.rpc_request(method_name, *args, **kwargs)
        _log.debug("Sending request: %s", request)

        self._socket.send_multipart([to_msgpack(request)])

        # if an rpc_timeout override is not specified, use the one set in the Client attributes
        if rpc_timeout is None:
            rpc_timeout = self.rpc_timeout

        start_time = time.time()
        while True:
            # Need to keep track of timeout manually in case this loop runs more than once. We subtract off already
            # elapsed time from the timeout. The call to max is to make sure we don't send a negative value
            # which would throw an error.
            timeout = max((start_time + rpc_timeout - time.time()) * 1000, 0) if rpc_timeout is not None else None
            if self._socket.poll(timeout) == 0:
                raise TimeoutError(f"Timeout on client {self.endpoint}, method name {method_name}, class info: {self}")

            raw_reply, = self._socket.recv_multipart()
            reply = from_msgpack(raw_reply)
            _log.debug("Received reply: %s", reply)

            # there's a possibility that the socket will have some leftover replies from a previous
            # request on it if that .call() was cancelled or timed out. Therefore, we need to discard replies that
            # don't match the request just like in the call_async case.
            if reply.id == request.id:
                break
            else:
                _log.debug('Discarding reply: %s', reply)

        for warning in reply.warnings:
            warn(f"{warning.kind}: {warning.body}")

        if isinstance(reply, RPCError):
            raise utils.RPCError(reply.error)
        else:
            return reply.result