Package: wsjsonrpc
wsJsonRpcWsJsonRpcis aTwisted Pythonprotocol to supportJSON-RPC 2.0overwebsocket. The underlying websocket implementation is provided byAutobahn.FeaturesBoth the client and server endpoints are fully bidirectional. Once the client connects to the server and the server accepts the connection, either endpoint can initiate requests or notifications on the remote side.Tested with:Pypy 2.7, 3.6Python 2.7, 3.6, 3.8InstallpipinstallwsjsonrpcUsageA simple server application should expose at least one method to clients. Here we register amath.summethod in the API.#!/usr/bin/env pythonimportsysfromtwistedimportloggerfromtwisted.internetimportreactorfromwsjsonrpcimportfactorylogobserver=logger.textFileLogObserver(sys.stdout)logger.globalLogPublisher.addObserver(logobserver)def_sum(protocol,x,y):returnx+yif__name__=="__main__":factory=factory.JsonRpcWebSocketServerFactory(u"ws://127.0.0.1:8095/wsjsonrpc")factory.registerMethod("math.sum",_sum)reactor.listenTCP(8095,factory)reactor.run()This client calls that remotemath.summethod, and then exits.#!/usr/bin/env pythonimportsysfromtwistedimportloggerfromtwisted.internetimportdeferfromtwisted.internetimporttaskfromwsjsonrpcimportfactorylogobserver=logger.textFileLogObserver(sys.stdout)logger.globalLogPublisher.addObserver(logobserver)log=logger.Logger()@defer.inlineCallbacksdefmain(reactor):result=Noneprotocol=yieldfactory.get_client(hostname="localhost",port=8095,path=u"wsjsonrpc")"""Call the 'math.sum' method on our peer and log the result."""result=yieldprotocol.request("math.sum",[1,2])log.debug("sum result:{}".format(result))yieldresulttask.react(main)Batch requestsBoth the client and server will accept and process batch requests. Callrequestornotifyas often as you need to in the batch context. When you exit the context, the batch will be sent to the peer.#!/usr/bin/env pythonimportsysfromtwistedimportloggerfromtwisted.internetimportdeferfromtwisted.internetimporttaskfromwsjsonrpcimportfactorylogobserver=logger.textFileLogObserver(sys.stdout)logger.globalLogPublisher.addObserver(logobserver)log=logger.Logger()@defer.inlineCallbacksdefmain(reactor):protocol=yieldfactory.get_client(hostname="localhost",port=8095,path=u"wsjsonrpc")df=Nonewithprotocol.batchContext()asbatch:batch.request("math.sum",[1,2])batch.request("math.sum",[2,3])batch.request("math.sum",[3,4])batch.request("math.sum",[4,5])df=batch.deferredList(consumeErrors=1)result=yielddflog.debug("sum result:{}".format(result))yieldresulttask.react(main)A batch can be submitted without the context manager. The following batch includes two calls tonotify. Note that thenotifymethod does not return a deferred, as notifications do not generate any response from the server.Thedefer.gatherResultscall here does not wait for thenotifycalls to complete - as soon as they are dispatched to the peer they are complete.batch=protocol.batch()df0=batch.request("math.sum",[1,2])df1=batch.request("math.sum",[2,3])batch.notify("math.sum",[3,4])batch.notify("math.sum",[4,5])df=defer.gatherResults(batch.deferreds,consumeErrors=1)batch.sendBatch()yielddfImplementing an APIYour API methods must takeprotocolas their first argument, and all other arguments should be either positional only or keyword only, for example:defsum_positional(protocol,x,y):returnx+ydefsum_keyword(protocol,x=0,y=0):returnx+yThe JSON-RPC spec requires that each request can have oneparamsvalue which MUST be either adict, alistorNone. If WsJsonRpc receivesparamsas a list, it expands it into positional arguments:# client:protocol.request("math.sum",[1,2])# server:sum_positional(*params)If it receivesparamsas a dict, it expands it into keyword arguments:# client:protocol.request("math.sum",{"x":1,"y":2})# server:sum_keyword(**params)Error handling in your APIIf an error occurs in your API, you might want to catch and log that exception, and then raise an exception from thewsjsonrpc.exception.JsonRpcExceptionfamily. The WsJsonRpc protocol will extract the correct error code and message from that exception to return a valid error object to the remote peer.It's likely that this will be aJsonRpcInternalError, but it could also beJsonRpcMethodNotFoundor a custom subclass ofJsonRpcCustomExceptionthat you have created. See the JSON-RPC documentation of theError objectfor guidance on that.If your API raises any other exception, WsJsonRpc will repackage that into aJsonRpcInternalErrorwhich is probably what you want anyway.AuthenticationNote that on the server side, the first argument that is passed into any API method will be the protocol instance itself. This allows you to write methods that authenticate the client (or peer), and then store the client's identity information as an attribute on your custom protocol implementation.Subsequent API calls will then be able to access that identity information.Alternatively, if your WsJsonRpc endpoint is one route in a larger web application, you could use the protocol to access the WebSocket request's authentication cookie.