ppolv’s blog

December 1, 2007

a pythonic walk on jabber’s pubsub

Filed under: jabber, python, x60br, xmpp — Tags: , , , , , — ppolv @ 9:10 am

In the previous post, i wrote about the tool i’m using to work with XMPP PubSub.
Now instead of the GUI, we will use the base APIs to create a node hierarchy directly from a python script. The hierarchy will have collection and leaf nodes, mirroring an initial “model” a -recursive- list structure. An example model can looks like the following:

hierarchy = [
             (COLLECTION,"collection1",[
                      (COLLECTION,"collection1.1",[]),
                      (LEAF,"node1.1"),
                      (LEAF,"node1.2")
                   ]),
             (LEAF,"node1"),
             (LEAF,"node2"),
             (COLLECTION,"collection2",[
                      (LEAF,"node2.1")
                 ]),
             (COLLECTION,"collection3",[
                      (LEAF,"node3.1"),
                      (LEAF,"node3.2"),
                      (LEAF,"node3.3")
                  ]),
             ]

The pubsub api of x60br is coded following the twisted asynchronous way: most of the methods perform a IQ call to the server and then return a Deferred, whitout blocking the caller. Then the calling code should register their own callbacks on that deferred to be executed when the response/error becomes available.
The API is still far for complete or stable… but enough to play with it. You can check the epydoc’s generated API Documentation.

In this example, to create the node hierarchy, the main function iterate over the list of tuples, creating the appropriated nodes, and returns a Deferred that will be fired when all responses where received from the server.

def add_nodes(ps,nodes,parent_collection = None):
    #ps is the pubsub service
    if nodes == []: #nothing to do
        return defer.succeed(None)

    #array of pending responses from the server
    #used to fire a callback when all request have been fulfilled
    pending = [] 

    for node in nodes:
        print "Creating node ", node[1]
        if node[0] is LEAF:
            d = ps.create_leaf_node(node[1], parent_collection)
            d.addErrback(log_err,node[1])
            pending.append(d)
        else:
            d = defer.Deferred()
            d_collection = ps.create_collection_node(node[1], parent_collection)
            def create_childs(parent_node,parent_cb,childs):
                d_childs = add_nodes(ps,childs,parent_node)
                #when all childs have been created, fire the completion
                #of the parent's defer
                d_childs.chainDeferred(parent_cb)

            #after the collection node is created, create all children
            d_collection.addCallback(create_childs,d,node[2])
            d_collection.addErrback(d.errback)
            d.addErrback(log_err,node[1])
            pending.append(d)
    return defer.DeferredList(pending,fireOnOneErrback=1, consumeErrors=1)

The full source code for the example is in http://svn.berlios.de/svnroot/repos/x60br/trunk/samples/hierarchy_creation.py

The code is simple, although if you aren’t used to Twisted, it may seems more complex than what really is.
The deferred d_collection is used for collection nodes, after receiving the confirmation that a collection node was created, we make a recursive call to create all the children of that collection.

This simple example creates nodes using the default configuration assigned by the server , but you can also specify configuration fields at creation time, or change the configuration after the creation of the node.

Advertisements

Blog at WordPress.com.