def setUp(self): self.channel = SocketRpcChannel(host=host, port=success_port) self.controller = self.channel.newController() self.service = test_pb2.TestService_Stub(self.channel) self.request = test_pb2.Request() self.request.str_data = 'I like cheese' self.callback = lambda request, response: response self.thread = service.RpcThread( test_pb2.TestService_Stub.TestMethod, self.service, self.controller, self.request, self.callback) ServerThread.start_server( success_port, fail_port, Exception('YOU FAIL!'))
def __init__(self, service_stub_class, port, host): ''' Contruct a new ProtoBufRpcRequest and return it. Accepted Arguments: service_stub_class -- (Service_Stub) The client side RPC stub class produced by protoc from the .proto file port -- (Integer) The port on which the service is running on the RPC server. host -- (String) The hostname or IP address of the server running the RPC service. ''' self.service_stub_class = service_stub_class self.port = port self.host = host # Setup the RPC channel self.channel = SocketRpcChannel(host=self.host, port=self.port) self.service = self.service_stub_class(self.channel) # go through service_stub methods and add a wrapper function to # this object that will call the method for method in service_stub_class.GetDescriptor().methods: # Add service methods to the this object rpc = lambda request, timeout=None, callback=None, service=self, \ method=method.name: \ service.call(service_stub_class.__dict__[method], request, timeout, callback) rpc.__doc__ = method.name + ' method of the ' + \ service_stub_class.DESCRIPTOR.name + ' from the ' + \ service_stub_class.__module__ + ' module generated by the ' + \ 'protoc compiler. This method can be called ' + \ 'synchronously by setting timeout -> ms or ' + \ 'asynchrounously by setting callback -> ' + \ 'function(request,response)\n\nSynchronous Example:\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t#Wait 1000ms for a response\n' + \ '\t\tresponse = ' + method.name + \ '(request, timeout=1000)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' + \ 'Asynchronous Example:\n' + \ '\tdef callback(request,response):\n' + \ '\t\t#Do some stuff\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t\t' + method.name + '(request, callback=callback)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' self.__dict__[method.name] = rpc
class TestRpcThread(unittest.TestCase): ''' Unit tests for the protobuf.service.RpcThread class.''' def setUp(self): self.channel = SocketRpcChannel(host=host, port=success_port) self.controller = self.channel.newController() self.service = test_pb2.TestService_Stub(self.channel) self.request = test_pb2.Request() self.request.str_data = 'I like cheese' self.callback = lambda request, response: response self.thread = service.RpcThread( test_pb2.TestService_Stub.TestMethod, self.service, self.controller, self.request, self.callback) ServerThread.start_server( success_port, fail_port, Exception('YOU FAIL!')) def tearDown(self): pass def test__init__(self): '''Test RpcThread constructor.''' self.assertEqual(self.thread.method, test_pb2.TestService_Stub.TestMethod, "Attribute 'method' incorrectly initialized") self.assertEqual(self.thread.service, self.service, "Attribute 'service' incorrectly initialized") self.assertEqual(self.thread.controller, self.controller, "Attribute 'controller' incorrectly initialized") self.assertEqual(self.thread.request, self.request, "Attribute 'request' incorrectly initialized") self.assertEqual(self.thread.callback, self.callback, "Attribute 'callback' incorrectly initialized") self.assertEqual(self.thread.isDaemon(), True, "Thread not set as Daemon") def test_run(self): '''Test the run method''' try: self.thread.run() except Exception, e: self.assert_(False, "TestRpcThread.run() threw and exception", e)
class RpcService(object): ''' Class abstracting the Protocol Buffer RPC calls for a supplied service stub. ''' def __init__(self, service_stub_class, port, host): ''' Contruct a new ProtoBufRpcRequest and return it. Accepted Arguments: service_stub_class -- (Service_Stub) The client side RPC stub class produced by protoc from the .proto file port -- (Integer) The port on which the service is running on the RPC server. host -- (String) The hostname or IP address of the server running the RPC service. ''' self.service_stub_class = service_stub_class self.port = port self.host = host # Setup the RPC channel self.channel = SocketRpcChannel(host=self.host, port=self.port) self.service = self.service_stub_class(self.channel) # go through service_stub methods and add a wrapper function to # this object that will call the method for method in service_stub_class.GetDescriptor().methods: # Add service methods to the this object rpc = lambda request, timeout=None, callback=None, service=self, \ method=method.name: \ service.call(service_stub_class.__dict__[method], request, timeout, callback) rpc.__doc__ = method.name + ' method of the ' + \ service_stub_class.DESCRIPTOR.name + ' from the ' + \ service_stub_class.__module__ + ' module generated by the ' + \ 'protoc compiler. This method can be called ' + \ 'synchronously by setting timeout -> ms or ' + \ 'asynchrounously by setting callback -> ' + \ 'function(request,response)\n\nSynchronous Example:\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t#Wait 1000ms for a response\n' + \ '\t\tresponse = ' + method.name + \ '(request, timeout=1000)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' + \ 'Asynchronous Example:\n' + \ '\tdef callback(request,response):\n' + \ '\t\t#Do some stuff\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t\t' + method.name + '(request, callback=callback)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' self.__dict__[method.name] = rpc def call(self, rpc, request, timeout=None, callback=None): ''' Save the object that has been created and return the response. Will timeout after timeout ms if response has not been received. The timeout arg is only used for asynch requests. If a callback has been supplied the timeout arg is not used. The response value will be returned for a synch request but nothing will be returned for an asynch request. Accepted Arguments: timeout -- (Integer) ms to wait for a response before returning ''' # Define local callback function to handle RPC response # and initialize result dict result = {'done': False, 'response': None} def synch_callback(request, response): result['response'] = response result['done'] = True result['error_msg'] = '' result['success'] = True # If no callback has been passed in then this is meant to be # synchronous if callback == None: rpc_callback = synch_callback else: if ((not callable(callback) and (callback.__class__.__dict__.get('run') == None or callback.run.func_code.co_argcount < 2)) or (callable(callback) and callback.func_code.co_argcount < 2)): raise Exception("callback must be a callable with signature " + "callback(request, response, ...) or an " + "object with a callable run function with " + "the same signature") rpc_callback = callback # Create a controller for this call controller = self.channel.newController() # Spawn a new thread to wait for the callback so this can return # immediately if an asynch callback has been requested rpc_thread = RpcThread(rpc, self.service, controller, request, rpc_callback) rpc_thread.start() # If a callback has been passed in return if rpc_callback == callback: return else: if timeout == None: timeout = 100 end = time() + (timeout / 1000) # Wait for timeout or thread to exit indicating call has returned rpc_thread.join(timeout) if time() >= end and not result['done']: raise RpcError('request timed out') return result['response']
class RpcService(object): ''' Class abstracting the Protocol Buffer RPC calls for a supplied service stub. ''' def __init__(self, service_stub_class, port, host): ''' Contruct a new ProtoBufRpcRequest and return it. Accepted Arguments: service_stub_class -- (Service_Stub) The client side RPC stub class produced by protoc from the .proto file port -- (Integer) The port on which the service is running on the RPC server. host -- (String) The hostname or IP address of the server running the RPC service. ''' self.service_stub_class = service_stub_class self.port = port self.host = host # Setup the RPC channel self.channel = SocketRpcChannel(host=self.host, port=self.port) self.service = self.service_stub_class(self.channel) # go through service_stub methods and add a wrapper function to # this object that will call the method for method in service_stub_class.GetDescriptor().methods: # Add service methods to the this object rpc = lambda request, timeout=None, callback=None, service=self, \ method=method.name: \ service.call(service_stub_class.__dict__[method], request, timeout, callback) rpc.__doc__ = method.name + ' method of the ' + \ service_stub_class.DESCRIPTOR.name + ' from the ' + \ service_stub_class.__module__ + ' module generated by the ' + \ 'protoc compiler. This method can be called ' + \ 'synchronously by setting timeout -> ms or ' + \ 'asynchrounously by setting callback -> ' + \ 'function(request,response)\n\nSynchronous Example:\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t#Wait 1000ms for a response\n' + \ '\t\tresponse = ' + method.name + \ '(request, timeout=1000)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' + \ 'Asynchronous Example:\n' + \ '\tdef callback(request,response):\n' + \ '\t\t#Do some stuff\n' + \ '\trequest = ' + method.input_type.name + '()\n' + \ '\ttry:\n' + \ '\t\t' + method.name + '(request, callback=callback)\n' + \ '\texcept: RpcException\n' + \ '\t\t#Handle exception\n\n' self.__dict__[method.name] = rpc def call(self, rpc, request, timeout=None, callback=None): ''' Save the object that has been created and return the response. Will timeout after timeout ms if response has not been received. The timeout arg is only used for asynch requests. If a callback has been supplied the timeout arg is not used. The response value will be returned for a synch request but nothing will be returned for an asynch request. Accepted Arguments: timeout -- (Integer) ms to wait for a response before returning ''' # Create a controller for this call controller = self.channel.newController() if callback is None and timeout is None: # Sync call without timeout return rpc(self.service, controller, request, callback) # Define local callback function to handle RPC response # and initialize result dict result = {'done': False, 'response': None} def synch_callback(request, response): result['response'] = response result['done'] = True result['error_msg'] = '' result['success'] = True # If no callback has been passed in then this is meant to be # synchronous if callback == None: rpc_callback = synch_callback else: if ((not callable(callback) and (callback.__class__.__dict__.get('run') == None or callback.run.func_code.co_argcount < 2)) or (callable(callback) and callback.func_code.co_argcount < 2)): raise Exception("callback must be a callable with signature " + "callback(request, response, ...) or an " + "object with a callable run function with " + "the same signature") rpc_callback = callback # Spawn a new thread to wait for the callback so this can return # immediately if an asynch callback has been requested rpc_thread = RpcThread(rpc, self.service, controller, request, rpc_callback) rpc_thread.start() # If a callback has been passed in, return controller if rpc_callback == callback: return controller # Run with timeout end = time() + (timeout / 1000) # Wait for timeout or thread to exit indicating call has returned rpc_thread.join(timeout) if time() >= end and not result['done']: raise RpcError('request timed out') if controller.failed(): raise RpcError(controller.error()) return result['response']