Thursday, August 14th, 2014

A JSON version of the standard Python xmlrpclib

I have always loved how easy Python made it to create simple RPC services using XML-RPC. I like how dead simple the API for both an XML-RPC server and client are. Here's an example XML-RPC Server in Python:

from SimpleXMLRPCServer import SimpleXMLRPCServer

# Create server
server = SimpleXMLRPCServer(("localhost", 8000))

# Register a function
@server.register_function
def hello(name='World'):
    return 'Hello %s!' % name

# Run the server's main loop
server.serve_forever()

Accessing this service via Python couldn't be simpler:

import xmlrpclib

s = xmlrpclib.ServerProxy('http://localhost:8000')

print s.hello()  # Outputs "Hello World!"
print s.hello('XML-RPC') # Outputs "Hello XML-RPC!"

I have been using XML-RPC a lot as a result, as I build mostly everything in Python. However, the overhead of XML parsing can be daunting for larger applications that are expected to receive many requests per minute. One technology that Python seems to be lacking in it's standard library is a simple service called JSON-RPC. Luckily I recently wrote a client library that currently only works on pure socket connections, here is the source code:

#
# JSON-RPC CLIENT LIBRARY inspired by the standard library xmlrpclib
#
# an JSON-RPC client interface for Python.
#
import urllib, json, socket

class _Method(object):
    # some magic to bind an JSON-RPC method to an RPC server.
    # supports "nested" methods (e.g. examples.getStateName)
    def __init__(self, send, name):
        self.__send = send
        self.__name = name
    def __getattr__(self, name):
        return _Method(self.__send, "%s.%s" % (self.__name, name))
    def __call__(self, **kwargs):
        return self.__send(self.__name, kwargs)

class ServerProxy(object):
    def __init__(self, uri):
        protocol, uri = urllib.splittype(uri)
        if protocol not in ('socket',):
            raise IOError, 'unsupported JSON-RPC protocol'
        self.__host, self.__handler = urllib.splithost(uri)
        self.__id = 0
    def __request(self, method, params):
        s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
        s.connect(urllib.splitnport(self.__host, 80))
        self.__id +=1
        data = {"jsonrpc": "2.0", "method": method, "id":self.__id}
        if params != {}:
            data.update({'params':params})
        s.send(json.dumps(data))
        resp = json.loads(s.recv(512))
        s.close()
        return resp['result']
    def __repr__(self):
        return '<ServerProxy for %s%s>' % (self.__host, self.__handler)
    __str__ = __repr__
    def __getattr__(self, name):
        return _Method(self.__request, name)

This library works in the exact same manner as the xmlrpclib shown above, expect it connects to JSON-RPC servers, such as the XBMC Media Center software Here is how you would interface with XBMC:

#
# A super simple XBMC JSON-RPC Interface for Python
# Control your XBMC in a Pythonic way!
#
import jsonrpclib, time

class XBMC(jsonrpclib.ServerProxy):
    def __init__(self, host, port=9090):
        super(XBMC, self).__init__('socket://%s:%s/' % (host, int(port)))
    def notification(self, title, message):
        return self.GUI.ShowNotification(title=title, message=message)
    def global_search(self, query):
        self.home()
        self.Input.Up()
        self.Input.Select()
        time.sleep(2) # Wait for keyboard on slower boxes.
        return self.Input.SendText(text=query)
    def weather(self):
        return self.GUI.ActivateWindow(window='weather')
    def home(self):
        return self.GUI.ActivateWindow(window='home')
    def favourites(self):
        return self.GUI.ActivateWindow(window='favourites')
    favorites = favourites

All of this code, plus many more, are now available on my newest bitbucket repository aptly called Python Experiments.

Comment #1: Posted 2 months ago by Thomas

Hi,

Just for information, a jsonrcplib library already exists since a long time :)
=> https://github.com/joshmarshall/jsonrpclib
It also supports the serialization of custom types.

I've forked it to support both Python 2.7 and 3+
=> https://github.com/tcalmant/jsonrpclib

Although, the idea of an XBMC pythonic controller is great :D

Cheers,
Thomas

About Me

My Photo
Names Kevin, hugely into UNIX technologies, not just Linux. I've dabbled with the demons, played with the Sun, and now with the Penguins.




Kevin Veroneau Consulting Services
Do you require the services of a Django contractor? Do you need both a website and hosting services? Perhaps I can help.

If you like what you read, please consider donating to help with hosting costs, and to fund future books to review.

Python Powered | © 2012-2014 Kevin Veroneau