JSON-RPC over TCP
Dependencies
- Twisted
- simplejson
- setuptools
Notes
This version was built from the adytum.twisted.web2.jsonrpc code by essentially replacing the HTTP-related code with twisted.protocols.basic.NetstringReceiver code.
One of the benefits of using Netstrings for this are string-length checks. There is a default maximum length that can be sent to the server. If you are sending large JSON strings to the server, you may need to override the default (1024 bytes).
The tests for adytum.twisted.web2.jsonrpc were also included/adapted and pass successfully.
Import
This is a stand-alone package that uses the adytum namespace, so if you have any other adytum code installed, you'll need to use setuptools' pkg_resource module:
>>> from pkg_resources import require
>>> require('Twisted-JSONRPC')
>>> from adytum.twisted import jsonrpcIf not, then you can simply use the standard import:
>>> from adytum.twisted import jsonrpc
Note that these imports are at the adytum.twisted level (unlike the others two, which are at adytum.twisted.web and adytum.twisted.web2).
Usage
Server
The server setup is a little different than with the HTTP versions. In particular, the manner of setting up subhandlers has changed. But first, let's look at a server example:
from twisted.application import service, internet
from adytum.twisted import jsonrpc
class Example(jsonrpc.JSONRPC):
"""An example object to be published."""
def jsonrpc_echo(self, x):
"""Return all passed args."""
return x
class Testing(jsonrpc.JSONRPC):
def jsonrpc_getList(self):
"""Return a list."""
return [1,2,3,4,'a','b','c','d']
class Math(jsonrpc.JSONRPC):
def jsonrpc_add(self, a, b):
"""Return sum of arguments."""
return a + b
# Setup the subhandlers for our RPC API
factory = jsonrpc.RPCFactory(Example)
factory.putSubHandler('math', Math)
factory.putSubHandler('testing', Testing)
# Let's add introspection, just for fun
factory.addIntrospection()
application = service.Application("Example JSON-RPC Server")
jsonrpcServer = internet.TCPServer(7080, factory)
jsonrpcServer.setServiceParent(application)With the HTTP versions, you create instances of your JSONRPC subclasses when adding them as subhandlers:
top = Example()
top.addSubHandler('math', Math())
top.addSubHandler('testing', Testing())But for the TCP version, you can see that we add the JSONRPC subclasses to the factory instead:
factory = jsonrpc.RPCFactory(Example)
factory.putSubHandler('math', Math)
factory.putSubHandler('testing', Testing)As for adding the introspection, there are two ways to do it with the TCP version. One way is how we did it in the example server above. The other way is similar to the HTTP RPC servers:
addIntrospection(factory)
But note that you are passing the factory and not the parent instance.
Save the server code from above as server_subhandled.tac and run with the following command:
$ twistd -noy server_subhandled.tac
Client
Here is example client code for the server above:
from twisted.internet import reactor
from twisted.internet import defer
from adytum.twisted.jsonrpc import Proxy
def printValue(value):
print "Result: %s" % str(value)
def printError(error):
print 'error', error
def shutDown(data):
print "Shutting down reactor..."
reactor.stop()
print "Making remote calls..."
proxy = Proxy('127.0.0.1', 7080)
dl = []
d = proxy.callRemote('system.listMethods')
d.addCallbacks(printValue, printError)
dl.append(d)
d = proxy.callRemote('echo', 'bite me')
d.addCallbacks(printValue, printError)
dl.append(d)
d = proxy.callRemote('testing.getList')
d.addCallbacks(printValue, printError)
dl.append(d)
d = proxy.callRemote('math.add', 3, 5)
d.addCallbacks(printValue, printError)
dl.append(d)
dl = defer.DeferredList(dl)
dl.addCallback(shutDown)
reactor.run()Save this file as client_subhandled.py and run it from the command line. You should see output something like this:
Making remote calls...
Result: [u'echo', u'math.add', u'system.listMethods', u'system.methodHelp', u'system.methodSignature', u'testing.getList']
Result: [1, 2, 3, 4, u'a', u'b', u'c', u'd']
Result: 8
Result: bite me
Shutting down reactor...