PubSub Ahoy!

Posted on by Chris Warburton

I've just found out about x60br through an obscure link trek from a blog post in my feed reader. x60br is an XMPP PubSub library for Python, similar to the one I'm writing. This is teh awesome, since it means I can check what I'm doing against someone else, and possibly look into using their implementation in some areas.

The main difference between the two (apart from the API, since mine needs to actually work before I can give it a proper API :P ) is that they handle the asynchronous nature of XMPP differently.

In order to understand this you'll need to know a little about synchronous vs. asynchronous programming, which I've touched on before. The bits of programs which do the most work are functions. The purpose of a function is to define how to do something so that you can do it later on, for instance saying "this is how to get the items from a PubSub node" then later, when you want to get the items at a node, you can just say "get the items here" rather than having to recite every step again and again.

In the example I just gave it could be done in two ways, synchronously or asynchronously. If it were done synchronously your program would say something like:

def get_items(server, node):
    items = []
    ask_for_items(server, node)
    reply = None
    while reply is None:
        process_replies()
    for item in reply:
        items.append(item)
    return items

my_blog_posts = get_items("pubsub.jabber.org", "seriously-this-is-not-worth-reading")

The first part DEFINES how to get the items at a node (nothing is run, it is simply a definition of what getting items means). When the last line is reached the program will request the items from the server "http://pubsub.jabber.org", then will wait for replies until it gets one and finally put every item it finds into my_blog_posts. Then the next line is run. This is a REALLY bad way of doing things in any kind of interactive, GUI-based program, since the whole program will appear to freeze whilst the reply is being waited for.

The way around this is to make the function asynchronous. This is more complicated than the above way of doing things, but it means that your program can get on with doing other stuff (like updating progress bars, for example) whilst the routers, ISPs and servers go about sending messages to and fro so you can get the items. The way I have implemented this in my library works a little like the following:

def get_items(server, node, completed_function):
    stanza_id = ask_for_items(server, node)
    assign_handler(stanza_id, completed_function)

def assign_blog_posts(items):
    my_blog_posts = items

get_items("pubsub.jabber.org", "seriously-this-is-not-worth-reading", assign_blog_posts)

This is in a simplified form (since namespaces aren't dealt with, some functions aren't defined and the reply handling mechanism isn't shown) but it gets the point across. What happens is that the request for items is still sent, but instead of waiting around for a reply before doing anything else, the program just gets on with things. When a reply IS received, the function assign_blog_posts is run, which does whatever needs to be done with the items. This is called a handler function (since it handles the replies).

The way x60br does things is in a slightly different way. It uses the following technique:

def get_items(server, node):
    items = ask_for_items(server, node)
    return items

my_blog_posts = get_items("pubsub.jabber.org", "seriously-this-is-not-worth-reading")

This looks the same as the synchronous way, and it is, except that it uses some clever behind-the-scenes stuff in functions like ask_for_items which makes my_blog_post get assigned straight away, letting the program carry on, except instead of assigning the items to my_blog_posts it essentially assigns a dummy. When the replies are received any instances of this dummy are changed to the actual items. This is done using the famous Twisted framework for Python.

These are two ways of approaching the same problem (one triggers a response, one hands around an IOU), so I'll have a go at using x60br (although Twisted really seems more web servery, rather than desktop applicationy). I'll use x60br's API when modelling my own library's though, at least as a guide.