diff --git a/capnp/lib/capnp.pyx b/capnp/lib/capnp.pyx index ff3afb7..91f1019 100644 --- a/capnp/lib/capnp.pyx +++ b/capnp/lib/capnp.pyx @@ -2669,7 +2669,8 @@ def add_import_hook(additional_paths=[]): if _importer is not None: remove_import_hook() - this_dir = _os.path.join(_os.path.dirname(__file__), '..') + this_dir = _os.path.abspath(_os.path.join(_os.path.dirname(__file__), '..')) + print this_dir additional_paths.append(this_dir) _importer = _Importer(additional_paths) diff --git a/examples/calculator-client.py b/examples/calculator-client.py deleted file mode 100755 index 5488e9d..0000000 --- a/examples/calculator-client.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function -import argparse -import socket -import capnp - -import calculator_capnp - -class PowerFunction(calculator_capnp.Calculator.Function.Server): - '''An implementation of the Function interface wrapping pow(). Note that - we're implementing this on the client side and will pass a reference to - the server. The server will then be able to make calls back to the client.''' - - def call(self, params, **kwargs): - '''Note the **kwargs. This is very necessary to include, since protocols can add parameters over time. Also, by default, a _context variable is passed to all server methods. Read the docs for further explanation.''' - return pow(params[0], params[1]) - -def parse_args(): - parser = argparse.ArgumentParser(usage='Connects to the Calculator server at the given address and does some RPCs') - parser.add_argument("host", help="HOST:PORT") - - return parser.parse_args() - -def main(): - host, port = parse_args().host.split(':') - - sock = socket.create_connection((host, port)) - client = capnp.TwoPartyClient(sock) - - # Pass "calculator" to ez_restore (there's also a `restore` function that takes a struct or AnyPointer as an argument), and then cast the returned capability to it's proper type. This casting is due to capabilities not having a reference to their schema - calculator = client.ez_restore('calculator').cast_as(calculator_capnp.Calculator) - - '''Make a request that just evaluates the literal value 123. - - What's interesting here is that evaluate() returns a "Value", which is - another interface and therefore points back to an object living on the - server. We then have to call read() on that object to read it. - However, even though we are making two RPC's, this block executes in - *one* network round trip because of promise pipelining: we do not wait - for the first call to complete before we send the second call to the - server.''' - - print('Evaluating a literal... ', end="") - - # Set up the request. Note the form is 'evaluate' + '_request', where 'evaluate' is the name of the method we want to call - request = calculator.evaluate_request() - request.expression.literal = 123 - - # Send it, which returns a promise for the result (without blocking). - eval_promise = request.send() - - # Using the promise, create a pipelined request to call read() on the returned object. Note that here we are using the shortened method call syntax read(), which is mostly just sugar for read_request().send() - read_promise = eval_promise.value.read() - - # Now that we've sent all the requests, wait for the response. Until this - # point, we haven't waited at all! - response = read_promise.wait() - assert response.value == 123 - - print("PASS") - - '''Make a request to evaluate 123 + 45 - 67. - # // - The Calculator interface requires that we first call getOperator() to - get the addition and subtraction functions, then call evaluate() to use - them. But, once again, we can get both functions, call evaluate(), and - then read() the result -- four RPCs -- in the time of *one* network - round trip, because of promise pipelining.''' - - print("Using add and subtract... ", end='') - - # Get the "add" function from the server. - add = calculator.getOperator(op='add').func - # Get the "subtract" function from the server. - subtract = calculator.getOperator(op='subtract').func - - # Build the request to evaluate 123 + 45 - 67. - request = calculator.evaluate_request() - subtract_call = request.expression.init('call') - subtract_call.function = subtract - subtract_params = subtract_call.init('params', 2) - subtract_params[1].literal = 67.0 - - add_call = subtract_params[0].init('call') - add_call.function = add - add_params = add_call.init('params', 2) - add_params[0].literal = 123 - add_params[1].literal = 45 - - # Send the evaluate() request, read() the result, and wait for read() to finish. - eval_promise = request.send() - read_promise = eval_promise.value.read() - - response = read_promise.wait() - assert response.value == 101 - - print("PASS") - - - '''Make a request to evaluate 4 * 6, then use the result in two more - requests that add 3 and 5. - - Since evaluate() returns its result wrapped in a `Value`, we can pass - that `Value` back to the server in subsequent requests before the first - `evaluate()` has actually returned. Thus, this example again does only - one network round trip.''' - - print("Pipelining eval() calls... ", end="") - - # Get the "add" function from the server. - add = calculator.getOperator(op='add').func - # Get the "multiply" function from the server. - multiply = calculator.getOperator(op='multiply').func - - - # Build the request to evaluate 4 * 6 - request = calculator.evaluate_request() - - multiply_call = request.expression.init("call") - multiply_call.function = multiply - multiply_params = multiply_call.init("params", 2); - multiply_params[0].literal = 4 - multiply_params[1].literal = 6 - - multiply_result = request.send().value - - # Use the result in two calls that add 3 and add 5. - - add3Request = calculator.evaluate_request() - add3Call = add3Request.expression.init("call") - add3Call.function = add - add3Params = add3Call.init("params", 2) - add3Params[0].previousResult = multiply_result - add3Params[1].literal = 3 - add3Promise = add3Request.send().value.read() - - add5Request = calculator.evaluate_request() - add5Call = add5Request.expression.init("call") - add5Call.function = add - add5Params = add5Call.init("params", 2) - add5Params[0].previousResult = multiply_result - add5Params[1].literal = 5 - add5Promise = add5Request.send().value.read() - - # Now wait for the results. - assert add3Promise.wait().value == 27 - assert add5Promise.wait().value == 29 - - print("PASS") - - '''Our calculator interface supports defining functions. Here we use it - to define two functions and then make calls to them as follows: - - f(x, y) = x * 100 + y - g(x) = f(x, x + 1) * 2; - f(12, 34) - g(21) - - Once again, the whole thing takes only one network round trip.''' - - print("Defining functions... ", end="") - - # Get the "add" function from the server. - add = calculator.getOperator(op='add').func - # Get the "multiply" function from the server. - multiply = calculator.getOperator(op='multiply').func - - # Define f. - request = calculator.defFunction_request() - request.paramCount = 2 - - # Build the function body. - addCall = request.body.init("call") - addCall.function = add - addParams = addCall.init("params", 2) - addParams[1].parameter = 1 # y - - multiplyCall = addParams[0].init("call") - multiplyCall.function = multiply - multiplyParams = multiplyCall.init("params", 2) - multiplyParams[0].parameter = 0 # x - multiplyParams[1].literal = 100 - - f = request.send().func - - # Define g. - request = calculator.defFunction_request() - request.paramCount = 1 - - # Build the function body. - multiplyCall = request.body.init("call") - multiplyCall.function = multiply - multiplyParams = multiplyCall.init("params", 2) - multiplyParams[1].literal = 2 - - fCall = multiplyParams[0].init("call") - fCall.function = f - fParams = fCall.init("params", 2) - fParams[0].parameter = 0 - - addCall = fParams[1].init("call") - addCall.function = add - addParams = addCall.init("params", 2) - addParams[0].parameter = 0 - addParams[1].literal = 1 - - g = request.send().func - - # OK, we've defined all our functions. Now create our eval requests. - - # f(12, 34) - fEvalRequest = calculator.evaluate_request() - fCall = fEvalRequest.expression.init("call") - fCall.function = f - fParams = fCall.init("params", 2) - fParams[0].literal = 12 - fParams[1].literal = 34 - fEvalPromise = fEvalRequest.send().value.read() - - # g(21) - gEvalRequest = calculator.evaluate_request() - gCall = gEvalRequest.expression.init("call") - gCall.function = g - gCall.init('params', 1)[0].literal = 21 - gEvalPromise = gEvalRequest.send().value.read() - - # Wait for the results. - assert fEvalPromise.wait().value == 1234 - assert gEvalPromise.wait().value == 4244 - - print("PASS") - - '''Make a request that will call back to a function defined locally. - - Specifically, we will compute 2^(4 + 5). However, exponent is not - defined by the Calculator server. So, we'll implement the Function - interface locally and pass it to the server for it to use when - evaluating the expression. - - This example requires two network round trips to complete, because the - server calls back to the client once before finishing. In this - particular case, this could potentially be optimized by using a tail - call on the server side -- see CallContext::tailCall(). However, to - keep the example simpler, we haven't implemented this optimization in - the sample server.''' - - print("Using a callback... ", end="") - - # Get the "add" function from the server. - add = calculator.getOperator(op='add').func - - # Build the eval request for 2^(4+5). - request = calculator.evaluate_request() - - powCall = request.expression.init("call") - powCall.function = PowerFunction() - powParams = powCall.init("params", 2) - powParams[0].literal = 2 - - addCall = powParams[1].init("call") - addCall.function = add - addParams = addCall.init("params", 2) - addParams[0].literal = 4 - addParams[1].literal = 5 - - # Send the request and wait. - response = request.send().value.read().wait() - assert response.value == 512 - - print("PASS") - -if __name__ == '__main__': - main() diff --git a/examples/calculator-server.py b/examples/calculator-server.py deleted file mode 100755 index bcb7258..0000000 --- a/examples/calculator-server.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function -import argparse -import socket -import random -import capnp - -import calculator_capnp - -def readValue(value): - '''Helper function to asynchronously call read() on a Calculator::Value and - return a promise for the result. (In the future, the generated code might - include something like this automatically.)''' - - return value.read().then(lambda result: result.value) - -def evaluateImpl(expression, params=None): - '''Implementation of CalculatorImpl::evaluate(), also shared by - FunctionImpl::call(). In the latter case, `params` are the parameter - values passed to the function; in the former case, `params` is just an - empty list.''' - - which = expression.which() - if which == 'literal': - return capnp.Promise(expression.literal) - elif which == 'previousResult': - return readValue(expression.previousResult) - elif which == 'parameter': - assert expression.parameter < len(params) - return capnp.Promise(params[expression.parameter]) - elif which == 'call': - def then(vals): - ret = func.call(vals).then(lambda result: result.value) - return ret - call = expression.call - func = call.function - - # Evaluate each parameter. - paramPromises = [evaluateImpl(param, params) for param in call.params] - - joinedParams = capnp.join_promises(paramPromises) - # When the parameters are complete, call the function. - ret = joinedParams.then(then) - - return ret - - else: - raise ValueError("Unknown expression type: " + which) - -class ValueImpl(calculator_capnp.Calculator.Value.Server): - "Simple implementation of the Calculator.Value Cap'n Proto interface." - - def __init__(self, value): - self.value = value - - def read(self, **kwargs): - return self.value - -class FunctionImpl(calculator_capnp.Calculator.Function.Server): - '''Implementation of the Calculator.Function Cap'n Proto interface, where the - function is defined by a Calculator.Expression.''' - - def __init__(self, paramCount, body, obj): - self.paramCount = paramCount - self.body = body.as_builder() - self.obj = obj - - def call(self, params, _context, **kwargs): - assert len(params) == self.paramCount - return evaluateImpl(self.body, params).then(lambda value: setattr(_context.results, 'value', value)) # using setattr because '=' is not allowed inside of lambdas - - -class OperatorImpl(calculator_capnp.Calculator.Function.Server): - '''Implementation of the Calculator.Function Cap'n Proto interface, wrapping - basic binary arithmetic operators.''' - - def __init__(self, op): - self.op = op - - def call(self, params, **kwargs): - assert len(params) == 2 - - op = self.op - if op == 'add': - return params[0] + params[1] - elif op == 'subtract': - return params[0] - params[1] - elif op == 'multiply': - return params[0] * params[1] - elif op == 'divide': - return params[0] / params[1] - else: - raise ValueError('Unknown operator') - -class CalculatorImpl(calculator_capnp.Calculator.Server): - "Implementation of the Calculator Cap'n Proto interface." - - def evaluate(self, expression, _context, **kwargs): - return evaluateImpl(expression).then(lambda value: setattr(_context.results, 'value', ValueImpl(value))) - - def defFunction(self, paramCount, body, _context, **kwargs): - return FunctionImpl(paramCount, body, _context) - - def getOperator(self, op, **kwargs): - return OperatorImpl(op) - -def parse_args(): - parser = argparse.ArgumentParser(usage='''Runs the server bound to the given address/port -ADDRESS may be '*' to bind to all local addresses. -:PORT may be omitted to choose a port automatically. -''') - - parser.add_argument("address", help="ADDRESS[:PORT]") - - return parser.parse_args() - - -class CalcRestorer: - def __init__(self): - self.calc = CalculatorImpl() - - def restore(self, ref): - assert ref.as_text() == 'calculator' - return CalculatorImpl() - -def main(): - address = parse_args().address - - if ':' in address: - address, port = address.split(':') - port = int(port) - else: - port = random.randint(60000,61000) - - if address == '*': - address = '' - - print("Listening on port: {}".format(port)) - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind((address,port)) - s.listen(1) # service only 1 client at a time - - while True: - try: - (clientsocket, address) = s.accept() - restorer = CalcRestorer() - server = capnp.TwoPartyServer(clientsocket, restorer) - - server.run_forever() - print("client disconnected") - except KeyboardInterrupt: - break - -if __name__ == '__main__': - main()