Ejemplo n.º 1
0
    def invoke_request(self, method_name, *args, **kwargs):
        '''
        Invoke RPC Request.

        :param method_name: Name of the request method.
        :type method_name: str
        :param args: Arguments
        :param kwargs: Keyword Arguments.
        :returns: Response value(s) from peer.
        :raises: BsonRpcError

        A timeout for the request can be set by giving a special keyword
        argument ``timeout`` (float value of seconds) which can be prefixed
        by any number of underscores - if necessary - to differentiate it from
        the actual keyword arguments going to the peer node method call.

        e.g.
        ``invoke_request('testing', [], {'_timeout': 22, '__timeout: 10.0})``
        would call a request method ``testing(_timeout=22)`` on the RPC peer
        and wait for the response for 10 seconds.

        **NOTE:**
          Use either arguments or keyword arguments. Both can't
          be used in a single call.
          (Naturally the timeout argument does not count to the rule.)
        '''
        rec = re.compile(r'^_*timeout$')
        to_keys = sorted(filter(lambda x: rec.match(x), kwargs.keys()))
        if to_keys:
            timeout = kwargs[to_keys[0]]
            del kwargs[to_keys[0]]
        else:
            timeout = None

        def _send_request(msg_id):
            try:
                promise = self.dispatcher.register(msg_id)
                self.socket_queue.put(
                    self.definitions.request(msg_id, method_name, args,
                                             kwargs))
                return promise
            except Exception as e:
                self.dispatcher.unregister(msg_id)
                raise e

        msg_id = six.next(self.id_generator)
        promise = _send_request(msg_id)
        try:
            result = promise.wait(timeout)
        except RuntimeError:
            self.dispatcher.unregister(msg_id)
            raise ResponseTimeout(u'Waiting response expired.')
        if isinstance(result, Exception):
            print("Failing method name: '{}'".format(method_name))
            raise result
        return result
Ejemplo n.º 2
0
    def batch_call(self, batch_calls, timeout=None):
        '''
        :param batch_calls: Batch of requests/notifications to be executed on
                            the peer node. Use ``BatchBuilder()`` and pass the
                            object here as a parameter.

                            Example:

                            .. code-block:: python

                              bb = BatchBuilder(['swapit', 'times'], ['logit'])
                              bb.swapit('hello')    # request
                              bb.times(3, 5)        # request
                              bb.logit('world')     # notification
                              results = jsonrpc.batch_call(bb, timeout=15.0)
                              # results: ['olleh', 15]

                            Note that ``BatchBuilder`` is used and behaves like
                            the peer-proxy returned by ``.get_peer_proxy()``.

                            Instead of BatchBuilder you may give a simple list
                            argument which must be in the following format:
                            [("r"/"n", "<method-name>", args, kwargs), ...]
        :type batch_calls: bsonrpc.BatchBuilder | list of 4-tuples
        :param timeout: Timeout in seconds for waiting results. Default: None
        :type timeout: float | None
        :returns: * list of results to requests, in order of original requests.
                    Each result may be:

                    * a single return value or
                    * a tuple of return values or
                    * an Exception object

                  * ``None`` if ``batch_calls`` contained only notifications.
        :raises: ResponseTimeout in case batch_calls contains requests,
                 for which response batch did not arrive within timeout.
        '''
        def _compose_batch(batch_calls):
            request_ids = []
            batch = []
            try:
                for call_type, method_name, args, kwargs in batch_calls:
                    if call_type.lower().startswith('n'):
                        batch.append(
                            self.definitions.notification(
                                method_name, args, kwargs))
                    else:
                        msg_id = six.next(self.id_generator)
                        batch.append(
                            self.definitions.request(msg_id, method_name, args,
                                                     kwargs))
                        request_ids.append(msg_id)
            except Exception as e:
                raise BsonRpcError(u'Malformed batch call: ' +
                                   six.text_type(e))
            return request_ids, batch

        if isinstance(batch_calls, BatchBuilder):
            batch_calls = batch_calls._batch_calls
        if not batch_calls:
            raise BsonRpcError(u'Refusing to send an empty batch.')
        format_info = (u'Argument "batch_calls"(list) is expected to contain '
                       u'4-tuples of (str, str, list, dict) -types.')
        for item in batch_calls:
            assert len(item) == 4, format_info
            assert isinstance(item[0], six.string_types), format_info
            assert isinstance(item[1], six.string_types), format_info
            assert isinstance(item[2], (list, tuple)), format_info
            assert isinstance(item[3], dict), format_info
        request_ids, batch = _compose_batch(batch_calls)
        # Notifications only:
        if not request_ids:
            self.socket_queue.put(batch)
            return None
        # At least one request in the batch:
        try:
            with ResultScope(self.dispatcher, tuple(request_ids)) as promise:
                self.socket_queue.put(batch)
                results = promise.wait(timeout)
        except RuntimeError:
            raise ResponseTimeout(u'Timeout for waiting batch result.')
        if isinstance(results, Exception):
            raise results
        return results