SOAP in Python - ZSI
SOAP (it no longer actually STANDS for anything) is an XML-based RPC protocol commonly used for webservices, and generally transferred over HTTP, although various other transports, including HTTP, are possible. Most publicly-available SOAP webservices are described by a WSDL (pronounced wiz-del or, Wikipedia tells me, woozedel, surely up there with 'SquiReL' in the silly acronym pronunciation stakes) file, an XML file specifying input and output parameters, methods and custom types.
Now, obviously if writing a webservice, you don't want to have to generate SOAP messages yourself (though, once you
see the libraries, you'll be tempted, oh, you'll be tempted), so you'll want to use a library. There are two major libraries, SOAPpy and ZSI (
both here). Only ZSI is still under active development, and the two will apparently be merged, eventually.
I've just spent a few days trying to figure out ZSI; the documentation is really most lacking (in particular, few of the examples actually WORK on current versions), and I'd never played with SOAP before. So, here's what you do if you want a WSDL-described Python web service.
First, you'll need ZSI. ZSI requires the Python wrapper for
Expat, an XML parser library. Irritatingly, it will not warn that this is missing on startup, nor indeed to the console while running. Instead, the warning will be included in SOAP messages returned. Yay! If you're using Linux, your distribution probably has a ZSI package which will take care of all this for you.
Assuming you want WSDL, you'll also want a WSDL file describing your service. This is beyond the scope of this post, but there are various tools for making them (
here's one I wrote) and they aren't horribly difficult to write yourself. Now, run 'wsdl2py -f myfile.wsdl' (produces bla_services.py, describing the service, and bla_services_types.py, describing any custom types) and 'wsdl2dispatch -f myfile.wsdl' (produces bla_services_server.py, containing skeletons for your webservice methods, plus some magic to hold it altogether. This is the part that held me up; it's actually very useful, but it's not mentioned in the documentation (which doesn't really touch on writing wsdl-described services at all) or examples, and indeed is only mentioned once on the mailing list) on your file.
Now, you want to edit 'bla_services_server.py', or whatever. You'll find it contains a stub for each of your methods like so (the parts in bold are examples inserted by myself:
def soap_myMethod(self, ps):
# input vals in request object
args = ps.Parse( myMethodWrapper )
name = args._name
age = args._name
# assign return values to response object
response = myMethodWrapper()
message = "Hello %s" % name
age_list = [i for i in range(1,age+1)]
response._message = message
response._age_list = age_list
# Return the response
return response
Note that all members of the argument and response structures are prefixed with an underscore.
To invoke all this, then, you'll want a fairly simple file:
from bla_services_server import *
from ZSI.ServiceContainer import AsServer
AsServer(port=8080,services=(Service(),))
This imports those methods you wrote earlier, along with the ZSI machinery for creating a dispatch server. This code creates a small standalone POST-only server on port 8080. If you're actually exposing a service to the real world, you should probably use cgi, fastcgi or (preferably) mod_python instead; instructions for doing this are in the ZSI docs.
Now, how to test it? One way would be to have ZSI generate a python client from the wsdl. The downside to that is that both sides will be using code generated by wsdl2py, and as such will share the same (not necessarily quite correct) interpretation of the WSDL file. I noticed, in particular, that the client will be very tolerant of messages produced from a broken WSDL file. There's an easy perl client, SOAP::Lite, but it's
extremelytolerant of broken output. There's a Windows only one
here, based on .NET 1.1, which is quite fussy, but doesn't seem to be too good at RPC-type (as opposed to Document-type) SOAP; it can't use the Google web service, for instance. Apparently, AXIS, a Java library, is quite good;I haven't gotten around to testing it yet. If you enjoy C, MacOS has C (and Objective C) SOAP libraries.
Anyway, hope this is of some use to somebody. It's the products of a few days of false starts and experimentation in what is really a quite good, but very poorly documented, library.
As an interesting aside, ZSI is unusual in that generated XML includes not only linebreaks, but actual correct indentation! As the XML standard doesn't require such things, it has become common for XML that people will never have to look at, such as SOAP messages, RSS feeds and generated XHTML, to exclude it for space reasons, but the authors of ZSI apparently cared not for such things. The whole deal, of course, is fairly space-inefficient; a query and response of one integer each would be wrapped in a kilobyte or so of XML, for instance, and the Google web API wsdl, consisting of three methods, two very simple, results in an amazing
30kb of python. And there's more! Amazon web service generates 320kb, MapPoint 400kb and something called SRM 420kb. All this, for RPC!