Ejemplo n.º 1
0
    def _handle_batch_response(self, msgs):
        def _extract_msg_content(msg):
            if 'result' in msg:
                return msg['result']
            else:
                return RpcErrors.error_to_exception(msg['error'])

        without_id_msgs = list(filter(lambda x: x.get('id') is None, msgs))
        with_id_msgs = list(filter(lambda x: x.get('id') is not None, msgs))
        resp_map = dict(map(lambda x: (x['id'], x), with_id_msgs))
        msg_ids = set(resp_map.keys())
        resolved = False
        for idtuple, promise in self._batch_responses.items():
            if msg_ids.issubset(set(idtuple)):
                batch_response = []
                for req_id in idtuple:
                    if req_id in resp_map:
                        batch_response.append(
                            _extract_msg_content(resp_map[req_id]))
                    elif without_id_msgs:
                        batch_response.append(
                            _extract_msg_content(without_id_msgs.pop(0)))
                    else:
                        batch_response.append(
                            BsonRpcError(
                                'Peer did not respond to this request!'))
                promise.set(batch_response)
                resolved = True
                break
        if not resolved:
            self._log_error(
                u'Unrecognized/expired batch response from peer: ' +
                six.text_type(msgs))
Ejemplo n.º 2
0
 def unregister(self, msg_id):
     if isinstance(msg_id, tuple):
         promise = self._batch_responses.get(msg_id)
         if msg_id in self._batch_responses:
             del self._batch_responses[msg_id]
     else:
         promise = self._responses.get(msg_id)
         if msg_id in self._responses:
             del self._responses[msg_id]
     if promise and not promise.is_set():
         promise.set(BsonRpcError('Timeout'))
Ejemplo n.º 3
0
    def put(self, item):
        '''
        Put item to queue -> codec -> socket.

        :param item: Message object.
        :type item: dict, list or None
        '''
        if self._closed:
            raise BsonRpcError('Attempt to put items to closed queue.')
        msg_bytes = self.codec.into_frame(self.codec.dumps(item))
        with self._lock:
            self.socket.sendall(msg_bytes)
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
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