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
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