headerphoto

Python and Jabber: Head to Head Library Review

Posted by Tim Freund Tue, 12 Aug 2008 03:42:00 GMT

Python is known as a batteries included language, but sometimes it can feel like a home improvement project gone bad. You know, the kind of project where there seems to be too many parts for the work that remains? I had that feeling as I tried to figure out which Jabber/XMPP library would suit my application best. Like any programmer worth their salt, my first tool for research is a visit to Google. Two searches, python jabber and python xmpp, seemed to turn up three viable projects.

There’s also a reference to Twisted’s support for XMPP in the first page of search results, but we’ll save Twisted for another day. I haven’t grasped Twisted enough to feel comfortable writing about it, and the application that I am working on doesn’t use Twisted in any way. Keep an eye on these guys for more information regarding Twisted’s XMPP support.

All three of the options seem like reasonable choices, so we will review each of the packages for the following criteria:

  • Installation
  • Ease of Use
  • Support Options

Installation

The trial begins with an empty virtualenv environment. We have a blank slate into which we can install the projects. We will first attempt easy_install $PROJECT_NAME, and then easy_install $DOWNLOAD_URL. Any library that won’t easily install with easy_install is going to be a source of grief form any team or project that depends on it.

jabber.py

(xmpp)tim@prime:~/src/xmpp$ easy_install jabber.py
...
error: Could not find suitable distribution for Requirement.parse('jabber.py')
(xmpp)tim@prime:~/src/xmpp$ easy_install "http://downloads.sourceforge.net/jabberpy/jabberpy-0.5-0.tar.gz?modtime=1075826815&big_mirror=0" 
...
Finished processing dependencies for jabber.py==0.3-1

It appears that jabber.py isn’t in the PyPI index at all, and when we attempt to download and install jabber.py version 0.5-0, we are told that we have just installed version 0.3-1. I downloaded jabberpy-0.5-0.tar.gz and confirmed that setup.py reports the distribution as version 0.3-1. One other slightly confusing issue: the README file reports jabber.py as a GPL project, but the home page and setup.py files report it as LGPL. The xmpppy project also refers to jabber.py as LGPL. Best 3 out of 4 licensing?

The good news: jabber.py installation is painless on Windows, OS X, and Linux, as long as a download URL is provided.

PyXMPP

(xmpp)tim@prime:~/src/xmpp$ easy_install PyXMPP
...
Reading http://pypi.python.org/simple/
Reading http://pypi.python.org/simple/pyxmpp/
Reading http://pyxmpp.jabberstudio.org/
No local packages or download links found for PyXMPP
error: Could not find suitable distribution for Requirement.parse('PyXMPP')
(xmpp)tim@prime:~/src/xmpp$ easy_install http://pyxmpp.jajcus.net/downloads/pyxmpp-1.0.0.tar.gz
...
Finished processing dependencies for pyxmpp==1.0.0

PyXMPP is in the PyPI index, but the download URL is out of date and the download URL is not automatically discovered from the page at http://pyxmpp.jajcus.net. The package installs without issues once the download URL is passed to the easy_install program, assuming you are not using Windows. Windows users will likely see something like “Python was build with Visual Studio version 7.1, and extensions need to be build with the same version of the compiler, but it isn’t installed.” And with that, we’ve exceeded the scope of this tutorial.

Before we go further, are you running OS X? I know you OS X users were laughing at the Windows guys in the last paragraph because their inferior system left them hanging. Sure, PyXMPP installed just fine on your Mac Book Pro, but open an iTerm session right now and try to run a PyXMPP application. You will probably see ImportError: No module named libxml2, and if you don’t, you’ve run into this problem before and fixed it yourself. Let this be a lesson, karma always finds a way. Google may hold the answer to this problem, however, I would guess that Cuil does not.

So now we have, what, maybe a handful of Linux users still chomping at the bit to try PyXMPP? Let’s get going. One more package to install, and we’re off to the races.

(xmpp)tim@prime:~/src/xmpp$ easy_install dnspython
...
Finished processing dependencies for dnspython

xmpppy

Hey, you Windows and OS X users, you can come back now. This next one is a piece of cake for everybody:

(xmpp)tim@prime:~/src/xmpp$ easy_install xmpppy
...
Installed /home/tim/pyenvs/xmpp/lib/python2.5/site-packages/xmpppy-0.4.1-py2.5.egg
Processing dependencies for xmpppy
Finished processing dependencies for xmpppy

The latest version of the xmpppy project installs automatically with easy_install.

Ease of Use

We are about to write some code, and this is where any head to head competition can turn subjective. For the sake of this comparison, I have written a small base class that does the following:

  1. Create a chat bot
  2. Send an arbitrary message to an arbitrary user on a supplied list every 10 seconds
  3. Accept messages from users: print the message to the console, and thank the user.

Although the messages sent in this demonstration are fairly silly, the concept can easily be applied to real world applications. For instance, bug trackers could send notifications to developers, and developers could request status changes with the help of a chat bot.

We will create an implementation for each of the libraries under review. Before we begin, you may want to review the base class below. You can also check out the whole package from subversion.

jabber.py

Let’s dive in with jabber.py because it comes first alphabetically. That’s the kind of rigorous science we’re doing here, we alphabetize.

The JabberPyBot subclass proceeds in a pretty straightforward way that matches my experiences with writing Jabber client software in Java. We create a Client, connect, authenticate, and prepare to send and receive messages in the __init__ method.

I was able to send messages in very little time, but receiving messages was a little tricky. We registered a message handler when we created our Client object, and I thought that registering the handler would cause a glitter-laden jabber fairy to fly in on gossamer wings and sprinkle a little “we’ll handle these incoming messages automagically” dust over the code.

I ran the code a second time. Surely those dropped incoming messages were a fluke, but running unchanged code a second time produced exactly the same results. I was shocked. Shocked! Truthfully, I was just delaying the inevitable RTM moment. After reading through the example a second time, I saw the process method. That’s the ticket, when called it processes queued messages and fires off the appropriate handlers. We wire the process method into a receiver method that will run in its own thread, and we’re in business.

PyXMPP

The PyXMPP client was the last of the three clients written. So much for rigorous, alphabetized, science, eh? This code was a nice change of pace since the jabber.py and xmpppy clients were so similar, as you will soon find out.

The code for the PyXMPP implementation is just as short as our other two implementations, but something feels a little funny about it. Notice how we need to keep digging into our connection (JabberClient) object and grabbing its stream object to get stuff done? I don’t know about you, but that feels a little dirty. Matter of fact, there’s even a name for the “rule” that we are breaking. Take note, and during your next code review at the office when you see something similar you can say “I don’t know about that, Barry, the AbstractDatabaseManagerManagerFactory doesn’t adhere to the Law of Demeter very well, and I think it will be a maintenance nightmare down the road. Are you going to support this stuff when the API changes in the future?” But who are we kidding? Barry is such an old crank that it’s easier to fix his stuff after it’s been deployed than argue with him about his crazy design right now at 10:55. It is so close to lunch, and we can’t be late for that. Man, I’m hungry. A gyro sounds really good.

What were we talking about? Oh, right, our PyxmppBot and the code that talks to more than just its immediate friends. I feel like we’re giving the library a quick brush off just because it doesn’t adhere to the world view that we’ve adopted in the other two client examples. Let’s take another look at the PyXMPP example. We have formed the opinion that our JabberBot “has-a” connection/client object and interacts with it, but the PyXMPP programmers seem to think that a bot “is-a” client/connection object. The have been doing this stuff longer than we have, so let’s give it one more try.

That feels a little bit cleaner. If Demeter is happy, I’m happy.

xmpppy

The xmpppy client was shockingly similar to the jabber.py client, but the similarities were explained away upon reading the xmpppy home page where it mentions that some of the code and API decisions came directly from jabber.py. That just goes to show, when in doubt, read the docs.

One thing I didn’t quite understand about the xmpppy API was the choice of method names. Why are some methods given capitalized names, while others are lowercase? This is probably a silly complaint on my part, but I just know I will spend a lot of time second guessing the capitalization of method names if I use this library in my applications.

Reading the xtalk.py source provided most of the pointers that I needed to get our client up and running, with only one minor issue that stood in the way. The example calls a method named SendInitPresence which was since changed to sendInitPresence. Again, this made me wonder about the naming conventions in use.

Support and Growth Options

The example we worked through today was pretty basic. Where do these packages leave us as our skill and requirements grow?

jabber.py

Jabber.py is dead, dead, dead, and they are kind enough to tell us that right on the front page. If that’s the case, why did we look into it? Well, it works, and it isn’t licensed under the GPL. The working bit matters to everyone, and the GPL bit matters to some. If you have fairly simple needs and a list of requirements that includes “NO GPL!”, then this may just be the project for you.

PyXMPP

PyXMPP under active development, and it is a capable performer that seems to have a lot of power available for developers. For instance, as your bots grow, you can transfer your efforts into server side Jabber components, and the PyXMPP API will support your efforts. Of course, the fact that a large majority of developers will need to jump through a few hoops to install the library will be a source of frustration.

xmpppy

The xmpppy project’s last release was in December of 2007, and the mailing list remains active. It installs easily on Windows, OS X, and Linux, and it is a fairly easy to grasp library that could be quickly integrated into existing applications. The fact that it is GPL licensed could cause heartache for some projects or corporations, so proceed with caution if you are working in such a situation.

Conclusion

Sometimes the best way to pick a library is to sit down and write a little bit with each of the contenders. A little bit of code, and a little bit of reading goes a very long way toward a sound design decision. Sometimes these experiments lead us to question our initial assumptions. In this case, I am reconsidering Twisted to handle my XMPP needs since it is mature, under active development, and installs quickly on all of my potential target platforms.

What did I do right? What did I get wrong? What did I miss? Your comments are appreciated!

Submit to programming.reddit.com

Posted in , ,

Bookmark and Share

Comments are disabled