Exemplo n.º 1
0
 def run(self):
     client = RPCClient(self.addr)
     qt = client._import('pyqtgraph.Qt')
     # widget creation happens in main GUI thread; we are working with
     # proxies from here.
     self.l = qt.QtGui.QLabel('remote-controlled label')
     self.l.show()
     time.sleep(0.3)
     self.l.hide()
     with self.lock:
         self.done = True
Exemplo n.º 2
0
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