def test_socket_queue_garbage(codec, threading_model): s1, s2 = _socketpair(threading_model) sq = SocketQueue(s2, codec, threading_model) s1.sendall(b'\xfa\xff\xff\xff\xde\xadabracadabra bobby jenkins\x05\x04') assert isinstance(sq.get(), FramingError) # All but DecodingError's are considered irrecoverable -> socket is closed. sq.join(timeout=1.0) assert sq.is_closed
def __init__(self, socket, codec, services=None, **options): assert (hasattr(services, '_request_handlers') and hasattr(services, '_notification_handlers')) for key, value in options.items(): setattr(self, key, value) self.definitions = Definitions(self.protocol, self.protocol_version, self.no_arguments_presentation) self.services = services self.socket_queue = SocketQueue(socket, codec, self.threading_model) self.dispatcher = Dispatcher(self)
class RpcBase(DefaultOptionsMixin): def __init__(self, socket, codec, services=None, **options): assert (hasattr(services, '_request_handlers') and hasattr(services, '_notification_handlers')) for key, value in options.items(): setattr(self, key, value) self.definitions = Definitions(self.protocol, self.protocol_version, self.no_arguments_presentation) self.services = services self.socket_queue = SocketQueue(socket, codec, self.threading_model) self.dispatcher = Dispatcher(self) @property def is_closed(self): ''' :property: bool -- Closed by peer node or with ``close()`` ''' return self.socket_queue.is_closed 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 invoke_notification(self, method_name, *args, **kwargs): ''' Send an RPC Notification. :param method_name: Name of the notification method. :type method_name: str :param args: Arguments :param kwargs: Keyword Arguments. **NOTE:** Use either arguments or keyword arguments. Both can't be used simultaneously in a single call. ''' self.socket_queue.put( self.definitions.notification(method_name, args, kwargs)) def get_peer_proxy(self, requests=None, notifications=None, timeout=None): ''' Get a RPC peer proxy object. Method calls to this object are delegated and executed on the connected RPC peer. :param requests: A list of method names which can be called and will be delegated to peer node as requests. Default: None -> All arbitrary attributes are handled as requests to the peer. :type requests: list of str | None :param notifications: A list of method names which can be called and will be delegated to peer node as notifications. Default: None -> If ``requests`` is not ``None`` all other attributes are handled as notifications. :type notifications: list of str | None :param timeout: Timeout in seconds, maximum time to wait responses to each Request. :type timeout: float | None :returns: A proxy object. Attribute method calls delegated over RPC. ``get_peer_proxy()`` (without arguments) will return a proxy where all attribute method calls are turned into Requests, except calls via ``.n`` which are turned into Notifications. Example: :: proxy = rpc.get_peer_proxy() proxy.n.log_this('hello') # -> Notification result = proxy.swap_this('Alise') # -> Request But if arguments are given then the interpretation is explicit and ``.n``-delegator is not used: :: proxy = rpc.get_peer_proxy(['swap_this'], ['log_this']) proxy.log_this('hello') # -> Notification result = proxy.swap_this('esilA') # -> Request ''' return PeerProxy(self, requests, notifications, timeout) def close(self): ''' Close the connection and stop the internal dispatcher. ''' # Closing the socket queue causes the dispatcher to close also. self.socket_queue.close() def join(self, timeout=None): ''' Wait for the internal dispatcher to shut down. :param timeout: Timeout in seconds, max time to wait. :type timeout: float | None ''' self.dispatcher.join(timeout=timeout)
class RpcBase(DefaultOptionsMixin): def __init__(self, socket, codec, services=None, **options): assert (hasattr(services, '_request_handlers') and hasattr(services, '_notification_handlers')) for key, value in options.items(): setattr(self, key, value) self.definitions = Definitions(self.protocol, self.protocol_version, self.no_arguments_presentation) self.services = services self.socket_queue = SocketQueue(socket, codec, self.threading_model) self.dispatcher = Dispatcher(self) @property def is_closed(self): ''' :property: bool -- Closed by peer node or with ``close()`` ''' return self.socket_queue.is_closed 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 msg_id = six.next(self.id_generator) try: with ResultScope(self.dispatcher, msg_id) as promise: self.socket_queue.put( self.definitions.request(msg_id, method_name, args, kwargs)) result = promise.wait(timeout) except RuntimeError: raise ResponseTimeout(u'Waiting response expired.') if isinstance(result, Exception): raise result return result def invoke_notification(self, method_name, *args, **kwargs): ''' Send an RPC Notification. :param method_name: Name of the notification method. :type method_name: str :param args: Arguments :param kwargs: Keyword Arguments. **NOTE:** Use either arguments or keyword arguments. Both can't be used simultaneously in a single call. ''' self.socket_queue.put( self.definitions.notification(method_name, args, kwargs)) def get_peer_proxy(self, requests=None, notifications=None, timeout=None): ''' Get a RPC peer proxy object. Method calls to this object are delegated and executed on the connected RPC peer. :param requests: A list of method names which can be called and will be delegated to peer node as requests. Default: None -> All arbitrary attributes are handled as requests to the peer. :type requests: list of str | None :param notifications: A list of method names which can be called and will be delegated to peer node as notifications. Default: None -> If ``requests`` is not ``None`` all other attributes are handled as notifications. :type notifications: list of str | None :param timeout: Timeout in seconds, maximum time to wait responses to each Request. :type timeout: float | None :returns: A proxy object. Attribute method calls delegated over RPC. ``get_peer_proxy()`` (without arguments) will return a proxy where all attribute method calls are turned into Requests, except calls via ``.n`` which are turned into Notifications. Example: :: proxy = rpc.get_peer_proxy() proxy.n.log_this('hello') # -> Notification result = proxy.swap_this('Alise') # -> Request But if arguments are given then the interpretation is explicit and ``.n``-delegator is not used: :: proxy = rpc.get_peer_proxy(['swap_this'], ['log_this']) proxy.log_this('hello') # -> Notification result = proxy.swap_this('esilA') # -> Request ''' return PeerProxy(self, requests, notifications, timeout) def close(self): ''' Close the connection and stop the internal dispatcher. ''' # Closing the socket queue causes the dispatcher to close also. self.socket_queue.close() def join(self, timeout=None): ''' Wait for the internal dispatcher to shut down. :param timeout: Timeout in seconds, max time to wait. :type timeout: float | None ''' self.dispatcher.join(timeout=timeout)
def test_socket_queue_basics(codec, threading_model): s1, s2 = _socketpair(threading_model) sq1 = SocketQueue(s1, codec, threading_model) sq2 = SocketQueue(s2, codec, threading_model) sq1.put(msg2) sq1.put(msg1) sq2.put(msg2) assert sq2.get() == msg2 assert sq2.get() == msg1 assert sq1.get() == msg2 sq1.close() assert sq2.get() is None assert sq1.get() is None assert sq1.is_closed assert sq2.is_closed sq1.join() sq2.join()