From 3f7c3ae023e92007ed38bac1cea7d9afd0ca2df5 Mon Sep 17 00:00:00 2001 From: Jason Paryani Date: Mon, 9 Dec 2013 17:13:43 -0800 Subject: [PATCH] Change server instantion to be inherited --- capnp/lib/capnp.pyx | 22 ++- test/test_capability.py | 42 +++--- test/test_capability_context.py | 6 +- test/test_capability_old.py | 252 ++++++++++++++++++++++++++++++++ test/test_rpc.py | 18 +-- test/test_serialization.py | 22 +++ 6 files changed, 318 insertions(+), 44 deletions(-) create mode 100644 test/test_capability_old.py diff --git a/capnp/lib/capnp.pyx b/capnp/lib/capnp.pyx index 562bbff..a95b51b 100644 --- a/capnp/lib/capnp.pyx +++ b/capnp/lib/capnp.pyx @@ -91,8 +91,9 @@ cdef public C_Capability.Client * call_py_restorer(PyObject * _restorer, C_Dynam ret = restorer.restore(reader) cdef _DynamicCapabilityServer server = ret + cdef _InterfaceSchema schema = ret.schema - return new C_Capability.Client(helpers.server_to_client(server.schema.thisptr, server.server)) + return new C_Capability.Client(helpers.server_to_client(schema.thisptr, server)) cdef extern from "" namespace " ::kj": String strStructReader" ::kj::str"(C_DynamicStruct.Reader) @@ -597,8 +598,9 @@ cdef C_DynamicValue.Reader _extract_dynamic_struct_reader(_DynamicStructReader v cdef C_DynamicValue.Reader _extract_dynamic_client(_DynamicCapabilityClient value): return C_DynamicValue.Reader(value.thisptr) -cdef C_DynamicValue.Reader _extract_dynamic_server(_DynamicCapabilityServer value): - return helpers.new_server(value.schema.thisptr, value.server) +cdef C_DynamicValue.Reader _extract_dynamic_server(object value): + cdef _InterfaceSchema schema = value.schema + return helpers.new_server(schema.thisptr, value) cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent): cdef C_DynamicValue.Reader temp @@ -632,7 +634,7 @@ cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent): thisptr.set(field, _extract_dynamic_struct_reader(value)) elif value_type is _DynamicCapabilityClient: thisptr.set(field, _extract_dynamic_client(value)) - elif value_type is _DynamicCapabilityServer: + elif value_type is _DynamicCapabilityServer or isinstance(value, _DynamicCapabilityServer): thisptr.set(field, _extract_dynamic_server(value)) else: raise ValueError("Non primitive type") @@ -1355,6 +1357,9 @@ cdef class _DynamicCapabilityServer: self.schema = s self.server = server + def __getattr__(self, field): + return getattr(self.server, field) + cdef class _DynamicCapabilityClient: cdef C_DynamicCapability.Client thisptr cdef public object _server, _parent @@ -1846,13 +1851,16 @@ class _StructModule(object): return builder.set_root(obj) class _InterfaceModule(object): - def __init__(self, schema): + def __init__(self, schema, name): + def server_init(server_self): + pass self.schema = schema + self.Server = type(name, (_DynamicCapabilityServer,), {'__init__': server_init, 'schema':schema}) def _new_client(self, server): return _DynamicCapabilityClient()._init_vals(self.schema, server) - def new_server(self, server): + def _new_server(self, server): return _DynamicCapabilityServer(self.schema, server) cdef class SchemaParser: @@ -1949,7 +1957,7 @@ cdef class SchemaParser: elif proto.isConst: module.__dict__[node.name] = schema.as_const_value() elif proto.isInterface: - local_module = _InterfaceModule(schema.as_interface()) + local_module = _InterfaceModule(schema.as_interface(), node.name) module.__dict__[node.name] = local_module diff --git a/test/test_capability.py b/test/test_capability.py index f87e301..01b2fec 100644 --- a/test/test_capability.py +++ b/test/test_capability.py @@ -2,13 +2,9 @@ import pytest import capnp import os -this_dir = os.path.dirname(__file__) +import test_capability_capnp as capability -@pytest.fixture -def capability(): - return capnp.load(os.path.join(this_dir, 'test_capability.capnp')) - -class Server: +class Server(capability.TestInterface.Server): def __init__(self, val=1): self.val = val @@ -21,16 +17,16 @@ class Server: def buz(self, i, **kwargs): return i.host + '_test' -class PipelineServer: +class PipelineServer(capability.TestPipeline.Server): def getCap(self, n, inCap, _context, **kwargs): def _then(response): _results = _context.results _results.s = response.x + '_foo' - _results.outBox.cap = capability().TestInterface.new_server(Server(100)) + _results.outBox.cap = Server(100) return inCap.foo(i=n).then(_then) -def test_client(capability): +def test_client(): client = capability.TestInterface._new_client(Server()) req = client._request('foo') @@ -62,7 +58,7 @@ def test_client(capability): with pytest.raises(ValueError): req.baz = 1 -def test_simple_client(capability): +def test_simple_client(): client = capability.TestInterface._new_client(Server()) remote = client._send('foo', i=5) @@ -116,7 +112,7 @@ def test_simple_client(capability): with pytest.raises(ValueError): remote = client.foo(baz=5) -def test_pipeline(capability): +def test_pipeline(): client = capability.TestPipeline._new_client(PipelineServer()) foo_client = capability.TestInterface._new_client(Server()) @@ -131,7 +127,7 @@ def test_pipeline(capability): response = remote.wait() assert response.s == '26_foo' -class BadServer: +class BadServer(capability.TestInterface.Server): def __init__(self, val=1): self.val = val @@ -141,25 +137,25 @@ class BadServer: extra = 1 return str(i * 5 + extra + self.val), 10 # returning too many args -def test_exception_client(capability): +def test_exception_client(): client = capability.TestInterface._new_client(BadServer()) remote = client._send('foo', i=5) with pytest.raises(capnp.KjException): remote.wait() -class BadPipelineServer: +class BadPipelineServer(capability.TestPipeline.Server): def getCap(self, n, inCap, _context, **kwargs): def _then(response): _results = _context.results _results.s = response.x + '_foo' - _results.outBox.cap = capability().TestInterface.new_server(Server(100)) + _results.outBox.cap = Server(100) def _error(error): raise Exception('test was a success') return inCap.foo(i=n).then(_then, _error) -def test_exception_chain(capability): +def test_exception_chain(): client = capability.TestPipeline._new_client(BadPipelineServer()) foo_client = capability.TestInterface._new_client(BadServer()) @@ -170,7 +166,7 @@ def test_exception_chain(capability): except Exception as e: assert 'test was a success' in str(e) -def test_pipeline_exception(capability): +def test_pipeline_exception(): client = capability.TestPipeline._new_client(BadPipelineServer()) foo_client = capability.TestInterface._new_client(BadServer()) @@ -185,7 +181,7 @@ def test_pipeline_exception(capability): with pytest.raises(Exception): remote.wait() -def test_casting(capability): +def test_casting(): client = capability.TestExtends._new_client(Server()) client2 = client.upcast(capability.TestInterface) client3 = client2.cast_as(capability.TestInterface) @@ -193,7 +189,7 @@ def test_casting(capability): with pytest.raises(Exception): client.upcast(capability.TestPipeline) -class TailCallOrder: +class TailCallOrder(capability.TestCallOrder.Server): def __init__(self): self.count = -1 @@ -201,7 +197,7 @@ class TailCallOrder: self.count += 1 return self.count -class TailCaller: +class TailCaller(capability.TestTailCaller.Server): def __init__(self): self.count = 0 @@ -211,7 +207,7 @@ class TailCaller: tail = callee.foo_request(i=i, t='from TailCaller') return _context.tail_call(tail) -class TailCallee: +class TailCallee(capability.TestTailCallee.Server): def __init__(self): self.count = 0 @@ -221,9 +217,9 @@ class TailCallee: results = _context.results results.i = i results.t = t - results.c = capability().TestCallOrder.new_server(TailCallOrder()) + results.c = TailCallOrder() -def test_tail_call(capability): +def test_tail_call(): callee_server = TailCallee() caller_server = TailCaller() diff --git a/test/test_capability_context.py b/test/test_capability_context.py index 7c0c962..b901713 100644 --- a/test/test_capability_context.py +++ b/test/test_capability_context.py @@ -25,7 +25,7 @@ class PipelineServer: def getCap_context(self, context): def _then(response): context.results.s = response.x + '_foo' - context.results.outBox.cap = capability().TestInterface.new_server(Server(100)) + context.results.outBox.cap = capability().TestInterface._new_server(Server(100)) return context.params.inCap.foo(i=context.params.n).then(_then) @@ -149,7 +149,7 @@ class BadPipelineServer: def getCap_context(self, context): def _then(response): context.results.s = response.x + '_foo' - context.results.outBox.cap = capability().TestInterface.new_server(Server(100)) + context.results.outBox.cap = capability().TestInterface._new_server(Server(100)) def _error(error): raise Exception('test was a success') @@ -217,7 +217,7 @@ class TailCallee: results = context.results results.i = context.params.i results.t = context.params.t - results.c = capability().TestCallOrder.new_server(TailCallOrder()) + results.c = capability().TestCallOrder._new_server(TailCallOrder()) def test_tail_call(capability): callee_server = TailCallee() diff --git a/test/test_capability_old.py b/test/test_capability_old.py new file mode 100644 index 0000000..a56aa65 --- /dev/null +++ b/test/test_capability_old.py @@ -0,0 +1,252 @@ +import pytest +import capnp +import os + +this_dir = os.path.dirname(__file__) + +@pytest.fixture +def capability(): + return capnp.load(os.path.join(this_dir, 'test_capability.capnp')) + +class Server: + def __init__(self, val=1): + self.val = val + + def foo(self, i, j, **kwargs): + extra = 0 + if j: + extra = 1 + return str(i * 5 + extra + self.val) + + def buz(self, i, **kwargs): + return i.host + '_test' + +class PipelineServer: + def getCap(self, n, inCap, _context, **kwargs): + def _then(response): + _results = _context.results + _results.s = response.x + '_foo' + _results.outBox.cap = capability().TestInterface._new_server(Server(100)) + + return inCap.foo(i=n).then(_then) + +def test_client(capability): + client = capability.TestInterface._new_client(Server()) + + req = client._request('foo') + req.i = 5 + + remote = req.send() + response = remote.wait() + + assert response.x == '26' + + req = client.foo_request() + req.i = 5 + + remote = req.send() + response = remote.wait() + + assert response.x == '26' + + with pytest.raises(ValueError): + client.foo2_request() + + req = client.foo_request() + + with pytest.raises(ValueError): + req.i = 'foo' + + req = client.foo_request() + + with pytest.raises(ValueError): + req.baz = 1 + +def test_simple_client(capability): + client = capability.TestInterface._new_client(Server()) + + remote = client._send('foo', i=5) + response = remote.wait() + + assert response.x == '26' + + + remote = client.foo(i=5) + response = remote.wait() + + assert response.x == '26' + + remote = client.foo(i=5, j=True) + response = remote.wait() + + assert response.x == '27' + + remote = client.foo(5) + response = remote.wait() + + assert response.x == '26' + + remote = client.foo(5, True) + response = remote.wait() + + assert response.x == '27' + + remote = client.foo(5, j=True) + response = remote.wait() + + assert response.x == '27' + + remote = client.buz(capability.TestSturdyRefHostId.new_message(host='localhost')) + response = remote.wait() + + assert response.x == 'localhost_test' + + with pytest.raises(ValueError): + remote = client.foo(5, 10) + + with pytest.raises(ValueError): + remote = client.foo(5, True, 100) + + with pytest.raises(ValueError): + remote = client.foo(i='foo') + + with pytest.raises(ValueError): + remote = client.foo2(i=5) + + with pytest.raises(ValueError): + remote = client.foo(baz=5) + +def test_pipeline(capability): + client = capability.TestPipeline._new_client(PipelineServer()) + foo_client = capability.TestInterface._new_client(Server()) + + remote = client.getCap(n=5, inCap=foo_client) + + outCap = remote.outBox.cap + pipelinePromise = outCap.foo(i=10) + + response = pipelinePromise.wait() + assert response.x == '150' + + response = remote.wait() + assert response.s == '26_foo' + +class BadServer: + def __init__(self, val=1): + self.val = val + + def foo(self, i, j, **kwargs): + extra = 0 + if j: + extra = 1 + return str(i * 5 + extra + self.val), 10 # returning too many args + +def test_exception_client(capability): + client = capability.TestInterface._new_client(BadServer()) + + remote = client._send('foo', i=5) + with pytest.raises(capnp.KjException): + remote.wait() + +class BadPipelineServer: + def getCap(self, n, inCap, _context, **kwargs): + def _then(response): + _results = _context.results + _results.s = response.x + '_foo' + _results.outBox.cap = capability().TestInterface._new_server(Server(100)) + def _error(error): + raise Exception('test was a success') + + return inCap.foo(i=n).then(_then, _error) + +def test_exception_chain(capability): + client = capability.TestPipeline._new_client(BadPipelineServer()) + foo_client = capability.TestInterface._new_client(BadServer()) + + remote = client.getCap(n=5, inCap=foo_client) + + try: + remote.wait() + except Exception as e: + assert 'test was a success' in str(e) + +def test_pipeline_exception(capability): + client = capability.TestPipeline._new_client(BadPipelineServer()) + foo_client = capability.TestInterface._new_client(BadServer()) + + remote = client.getCap(n=5, inCap=foo_client) + + outCap = remote.outBox.cap + pipelinePromise = outCap.foo(i=10) + + with pytest.raises(Exception): + loop.wait(pipelinePromise) + + with pytest.raises(Exception): + remote.wait() + +def test_casting(capability): + client = capability.TestExtends._new_client(Server()) + client2 = client.upcast(capability.TestInterface) + client3 = client2.cast_as(capability.TestInterface) + + with pytest.raises(Exception): + client.upcast(capability.TestPipeline) + +class TailCallOrder: + def __init__(self): + self.count = -1 + + def getCallSequence(self, expected, **kwargs): + self.count += 1 + return self.count + +class TailCaller: + def __init__(self): + self.count = 0 + + def foo(self, i, callee, _context, **kwargs): + self.count += 1 + + tail = callee.foo_request(i=i, t='from TailCaller') + return _context.tail_call(tail) + +class TailCallee: + def __init__(self): + self.count = 0 + + def foo(self, i, t, _context, **kwargs): + self.count += 1 + + results = _context.results + results.i = i + results.t = t + results.c = capability().TestCallOrder._new_server(TailCallOrder()) + +def test_tail_call(capability): + callee_server = TailCallee() + caller_server = TailCaller() + + callee = capability.TestTailCallee._new_client(callee_server) + caller = capability.TestTailCaller._new_client(caller_server) + + promise = caller.foo(i=456, callee=callee) + dependent_call1 = promise.c.getCallSequence() + + response = promise.wait() + + assert response.i == 456 + assert response.i == 456 + + dependent_call2 = response.c.getCallSequence() + dependent_call3 = response.c.getCallSequence() + + result = dependent_call1.wait() + assert result.n == 0 + result = dependent_call2.wait() + assert result.n == 1 + result = dependent_call3.wait() + assert result.n == 2 + + assert callee_server.count == 1 + assert caller_server.count == 1 \ No newline at end of file diff --git a/test/test_rpc.py b/test/test_rpc.py index 19a096a..fc58843 100644 --- a/test/test_rpc.py +++ b/test/test_rpc.py @@ -3,32 +3,28 @@ import capnp import os import socket -this_dir = os.path.dirname(__file__) +import test_capability_capnp -@pytest.fixture -def capability(): - return capnp.load(os.path.join(this_dir, 'test_capability.capnp')) - -class Server: +class Server(test_capability_capnp.TestInterface.Server): def __init__(self, val=1): self.val = val def foo(self, i, j, **kwargs): return str(i * 5 + self.val) -def test_simple_rpc(capability): +def test_simple_rpc(): def _restore(ref_id): - return capability.TestInterface.new_server(Server(100)) + return Server(100) read, write = socket.socketpair(socket.AF_UNIX) - restorer = capnp.Restorer(capability.TestSturdyRefObjectId, _restore) + restorer = capnp.Restorer(test_capability_capnp.TestSturdyRefObjectId, _restore) server = capnp.RpcServer(write, restorer) client = capnp.RpcClient(read) - ref = capability.TestSturdyRefObjectId.new_message() + ref = test_capability_capnp.TestSturdyRefObjectId.new_message() cap = client.restore(ref) - cap = cap.cast_as(capability.TestInterface) + cap = cap.cast_as(test_capability_capnp.TestInterface) remote = cap.foo(i=5) response = remote.wait() diff --git a/test/test_serialization.py b/test/test_serialization.py index 8d89fa8..9bc109a 100644 --- a/test/test_serialization.py +++ b/test/test_serialization.py @@ -77,3 +77,25 @@ def test_roundtrip_dict(all_types): msg = all_types.TestAllTypes.from_dict(d) test_regression.check_all_types(msg) + +def test_file_and_bytes(all_types): + f = open('example', 'w') + msg = all_types.TestAllTypes.new_message() + test_regression.init_all_types(msg) + msg.write(f) + f.close() + + f = open('example', 'r') + + assert f.read() == msg.to_bytes() + +def test_file_and_bytes_packed(all_types): + f = open('example', 'w') + msg = all_types.TestAllTypes.new_message() + test_regression.init_all_types(msg) + msg.write_packed(f) + f.close() + + f = open('example', 'r') + + assert f.read() == msg.to_bytes_packed()