def test_disconnect(): #logger.level = logging.DEBUG # Clients receive notification when server disconnects gracefully server_proc = ProcessSpawner() client_proc = ProcessSpawner() cli = client_proc.client._import('pyacq.core.rpc').RPCClient(server_proc.client.address) cli.close_server() assert cli.disconnected() is True assert server_proc.client.disconnected() is True try: print(server_proc.client.ping()) assert False, "Expected RuntimeError" except RuntimeError: pass # Clients receive closure messages even if the server exits without closing server_proc = ProcessSpawner() server_proc.client['self']._closed = 'sabotage!' time.sleep(0.1) assert server_proc.client.disconnected() is True # Clients gracefully handle sudden death of server (with timeout) server_proc = ProcessSpawner() server_proc.kill() try: server_proc.client.ping(timeout=1) assert False, "Expected TimeoutError" except TimeoutError: pass # server doesn't hang up if clients are not available to receive disconnect # message server_proc = ProcessSpawner() for i in range(4): # create a bunch of dead clients cp = ProcessSpawner() cli = cp.client._import('pyacq.core.rpc').RPCClient(server_proc.client.address) cp.kill() start = time.time() server_proc.client.close_server() assert time.time() - start < 1.0 assert server_proc.client.disconnected() == True
def test_spawner(): proc = ProcessSpawner() cli = proc.client # check spawned RPC server has a different PID ros = cli._import('os') assert os.getpid() != ros.getpid() # test closing nicely proc.stop() # start process with QtRPCServer proc = ProcessSpawner(qt=True) cli = proc.client rqt = cli._import('pyqtgraph.Qt') assert rqt.QtGui.QApplication.instance() is not None # test closing Qt process proc.stop()
import numpy as np import datetime import pytest from pyacq.core.rpc.serializer import JsonSerializer, MsgpackSerializer, HAVE_MSGPACK from pyacq.core.rpc import ObjectProxy, ProcessSpawner proc = ProcessSpawner() test_data = { 'int': 1, 'float': 1., 'str': 'abc', 'bytes': b'abc', 'ndarray': np.arange(8).reshape(2, 4).astype('float64'), 'datetime': datetime.datetime(2015, 1, 1, 12, 00, 00), 'date': datetime.date(2015, 1, 1), #'tuple': (1,2), # both msgpack and json return tuples as lists. # see: https://github.com/msgpack/msgpack-python/issues/98 'list': [1, 2], 'proxy': proc.client['self'], } @pytest.mark.skipif(not HAVE_MSGPACK, reason='msgpack not available') def test_msgpack(): check_serializer(MsgpackSerializer()) def test_json(): check_serializer(JsonSerializer())
def test_disconnect(): #~ logger.level = logging.DEBUG # Clients receive notification when server disconnects gracefully server_proc = ProcessSpawner() client_proc = ProcessSpawner() cli = client_proc.client._import('pyacq.core.rpc').RPCClient(server_proc.client.address) cli.close_server() assert cli.disconnected() is True assert server_proc.client.disconnected() is True try: print(server_proc.client.ping()) assert False, "Expected RuntimeError" except RuntimeError: pass # add by Sam: force the end of process server_proc.kill() # Clients receive closure messages even if the server exits without closing server_proc = ProcessSpawner() server_proc.client['self']._closed = 'sabotage!' time.sleep(0.1) assert server_proc.client.disconnected() is True # add by Sam: force the end of process server_proc.kill() # Clients gracefully handle sudden death of server (with timeout) server_proc = ProcessSpawner() server_proc.kill() try: server_proc.client.ping(timeout=1) assert False, "Expected TimeoutError" except TimeoutError: pass # server doesn't hang up if clients are not available to receive disconnect # message server_proc = ProcessSpawner() for i in range(4): # create a bunch of dead clients cp = ProcessSpawner() cli = cp.client._import('pyacq.core.rpc').RPCClient(server_proc.client.address) cp.kill() start = time.time() server_proc.client.close_server() assert time.time() - start < 1.0 assert server_proc.client.disconnected() == True # add by Sam: force the end of process server_proc.kill()
def test_rpc(): previous_level = logger.level #logger.level = logging.DEBUG class TestClass(object): count = 0 def __init__(self, name): self.name = name TestClass.count += 1 def __del__(self): TestClass.count -= 1 def add(self, x, y): return x + y def array(self): return np.arange(20).astype('int64') def sleep(self, t): time.sleep(t) def get_list(self): return [0, 'x', 7] def test(self, obj): return self.name, obj.name, obj.add(5, 7), obj.array(), obj.get_list() def types(self): return {'int': 7, 'float': 0.5, 'str': 'xxx', 'bytes': bytes('xxx', 'utf8'), 'ndarray': np.arange(10), 'dict': {}, 'list': [], 'ObjectProxy': self} def type(self, x): return type(x).__name__ server1 = RPCServer() server1['test_class'] = TestClass server1['my_object'] = TestClass('obj1') serve_thread = threading.Thread(target=server1.run_forever, daemon=True) serve_thread.start() client = RPCClient.get_client(server1.address) # test clients are cached assert client == RPCClient.get_client(server1.address) try: # can't manually create client for the same address RPCClient(server1.address) assert False, "Should have raised KeyError." except KeyError: pass # get proxy to TestClass instance obj = client['my_object'] assert isinstance(obj, ObjectProxy) # check equality with duplicate proxy obj2 = client['my_object'] assert obj == obj2 assert obj._obj_id == obj2._obj_id assert obj._ref_id != obj2._ref_id # check hashability assert obj in {obj2: None} assert obj in set([obj2]) logger.info("-- Test call with sync return --") add = obj.add assert isinstance(add, ObjectProxy) assert add(7, 5) == 12 # test return types for k, v in obj.types().items(): assert type(v).__name__ == k if k != 'ObjectProxy': assert obj.type(v) == k # NOTE: msgpack converts list to tuple. # See: https://github.com/msgpack/msgpack-python/issues/98 assert obj.get_list() == [0, 'x', 7] logger.info("-- Test async return --") fut = obj.sleep(0.1, _sync='async') assert not fut.done() assert fut.result() is None logger.info("-- Test no return --") assert obj.add(1, 2, _sync='off') is None logger.info("-- Test return by proxy --") list_prox = obj.get_list(_return_type='proxy') assert isinstance(list_prox, ObjectProxy) assert list_prox._type_str == "<class 'list'>" assert len(list_prox) == 3 assert list_prox[2] == 7 logger.info("-- Test proxy access to server --") srv = client['self'] assert srv.address == server1.address logger.info("-- Test remote exception raising --") try: obj.add(7, 'x') except RemoteCallException as err: if err.type_str != 'TypeError': raise else: raise AssertionError('should have raised TypeError') try: client.asdffhgk raise AssertionError('should have raised AttributeError') except AttributeError: pass logger.info("-- Test deferred getattr --") arr = obj.array(_return_type='proxy') dt1 = arr.dtype.name._get_value() assert isinstance(dt1, str) arr._set_proxy_options(defer_getattr=True) dt2 = arr.dtype.name assert isinstance(dt2, ObjectProxy) assert dt2._obj_id == arr._obj_id assert dt2._attributes == ('dtype', 'name') dt3 = dt2._undefer() assert dt3 == dt2 logger.info("-- Test remote object creation / deletion --") class_proxy = client['test_class'] obj2 = class_proxy('obj2') assert class_proxy.count == 2 assert obj2.add(3, 4) == 7 obj2._delete() handler.flush_records() # log records might have refs to the object assert class_proxy.count._get_value() == 1 try: obj2.array() assert False, "Should have raised RemoteCallException" except RemoteCallException: pass logger.info("-- Test proxy auto-delete --") obj2 = class_proxy('obj2') obj2._set_proxy_options(auto_delete=True) assert class_proxy.count == 2 del obj2 handler.flush_records() # log records might have refs to the object assert class_proxy.count._get_value() == 1 logger.info("-- Test timeouts --") try: obj.sleep(0.2, _timeout=0.01) except TimeoutError: pass else: raise AssertionError('should have raised TimeoutError') obj.sleep(0.2, _timeout=0.5) logger.info("-- Test result order --") a = obj.add(1, 2, _sync='async') b = obj.add(3, 4, _sync='async') assert b.result() == 7 assert a.result() == 3 logger.info("-- Test transfer --") arr = np.ones(10, dtype='float32') arr_prox = client.transfer(arr) assert arr_prox.dtype.name == 'float32' print(arr_prox, arr_prox.shape) assert arr_prox.shape._get_value() == [10] logger.info("-- Test import --") import os.path as osp rosp = client._import('os.path') assert osp.abspath(osp.dirname(__file__)) == rosp.abspath(rosp.dirname(__file__)) logger.info("-- Test proxy sharing between servers --") obj._set_proxy_options(defer_getattr=True) r1 = obj.test(obj) server2 = RPCServer() server2['test_class'] = TestClass serve_thread2 = threading.Thread(target=server2.run_forever, daemon=True) serve_thread2.start() client2 = RPCClient(server2.address) client2.default_proxy_options['defer_getattr'] = True obj3 = client2['test_class']('obj3') # send proxy from server1 to server2 r2 = obj3.test(obj) # check that we have a new client between the two servers assert (serve_thread2.ident, server1.address) in RPCClient.clients_by_thread # check all communication worked correctly assert r1[0] == 'obj1' assert r2[0] == 'obj3' assert r1[1] == r2[1] == 'obj1' assert r1[2] == r2[2] == 12 assert np.all(r1[3] == r2[3]) assert r1[4] == r2[4] logger.info("-- Test publishing objects --") arr = np.arange(5, 10) client['arr'] = arr # publish to server1 s2rpc = client2._import('pyacq.core.rpc') s2cli = s2rpc.RPCClient.get_client(client.address) # server2's client for server1 assert np.all(s2cli['arr'] == arr) # retrieve via server2 logger.info("-- Test JSON client --") # Start a JSON client in a remote process cli_proc = ProcessSpawner() cli = cli_proc.client._import('pyacq.core.rpc').RPCClient(server2.address, serializer='json') # Check everything is ok.. assert cli.serializer.type._get_value() == 'json' assert cli['test_class']('json-tester').add(3, 4) == 7 cli_proc.kill() logger.info("-- Setup reentrant communication test.. --") class PingPong(object): def set_other(self, o): self.other = o def pingpong(self, depth=0): if depth > 6: return "reentrant!" return self.other.pingpong(depth+1) server1['pp1'] = PingPong() server2['pp2'] = PingPong() pp1 = client['pp1'] pp2 = client2['pp2'] pp1.set_other(pp2) pp2.set_other(pp1) logger.info("-- Test reentrant communication --") assert pp1.pingpong() == 'reentrant!' logger.info("-- Shut down servers --") client2.close_server() serve_thread2.join() client.close_server() client.close() serve_thread.join() logger.level = previous_level
try: raise Exception("local exception") except: sys.excepthook(*sys.exc_info()) # start a new thread in this process logger.info(">>>> Spawn a new thread:") th = ThreadSpawner(name="thread1") # cause an exception in the thread logger.info(">>>> Cause an exception in the remote thread:") try: th.client._import('xxxxx') except: sys.excepthook(*sys.exc_info()) logger.info(">>>> Spawn a new process:") proc = ProcessSpawner(name='proc1') logger.info(">>>> Cause an exception in the remote process:") proc.client._import('xxxxx') #rlogging = proc.client._import('logging') #rlogger = rlogging.getLogger() #rlogger.warn('test exception') #time.sleep(0.3) #qapp.processEvents() #rec = lv.log_records[-5] #print("msg:\n%s" % rec.getMessage()) #print("\nexc_info: \n%s" % rec.stack)