Developer Forums | About Us | Site Map
Search  
HOME > TUTORIALS > SERVER SIDE CODING > PYTHON TUTORIALS > PYTHON SOAP LIBRARIES, PART 2


Sponsors





Useful Lists

Web Host
site hosted by netplex

Online Manuals

Python SOAP libraries, Part 2
By Mike Olson and Uche Ogbuji - 2004-09-21 Page:  1 2 3

With and without WSDL

Continuing their look at Python SOAP implementations, Mike Olson and Uche Ogbuji put the Zolera SOAP Infrastructure (ZSI) through its paces as a SOAP client and server library. They find that ZSI is possibly the best tool for SOAP use under Python right now.

This month we continue our look at SOAP implementations for Python. In fact, we will take a close look at one we completely neglected to mention last time: the Zolera SOAP Infrastructure (ZSI). ZSI is the brain-child of Rich Salz, an active contributor to the Python/XML package, and veteran of several important standards efforts in Internet infrastructure and security. ZSI has been added as part of the Python Web Services project on SourceForge, which also hosts SOAPy, another Python/SOAP package we covered in the previous installment. We expect this confusing state of affairs will be resolved soon, perhaps with a merger of the SOAP packages.

ZSI is a purist's SOAP library. In particular, it is less concerned about handling any low-level protocol details: It deals with a stream of data that must be a properly-formatted SOAP message, and dispatches events to Python event handlers based on the parsed data. The upside of this strict focus is that ZSI is probably the most complete and compliant SOAP library for Python. The downside is that the ZSI user may have to do more work to actually put his or her Web services into play.

Setting up ZSI

To install ZSI 1.1, the latest version as of our writing, you need Python 2.0 or later. The instructions say you need PyXML 0.6 or later, but actually, you need version 0.6 or later, but earlier than 0.7. There were several significant API changes that appeared in PyXML 0.7 that break ZSI 1.1. This is a general problem issue while dealing with packages that depend on PyXML: if you get strange errors when using a package that states a dependency on PyXML 0.6, you might have understandably grabbed the latest available version of PyXML, which, unfortunately, will not work. The next release of ZSI will probably work with PyXML 0.7 and later, as the recent CVS activity seems to indicate. Grab PyXML 0.6.6 from SourceForge and unpack it, then build it:


$ python setup.py install

Download the ZSI package, unpack it, and build it the same way:



$ python setup.py install

This is the consistency that Python's distribution utilities (AKA distutils) brings to installing add-on Python packages.

Now that it's all installed, check out the doc directory, which has excellent documentation in HTML, PDF, postscript, and TeX format, using the same LaTeX tools as used in the Python documentation project.

Invoking client privilege

First of all, we looked at using ZSI as a SOAP client. In the last installment, we wrote a SOAP.py client that accesses a toy Web service that returns curses by Captain Haddock (ever popular to Tin Tin comic fans). We will write a client for the same server using ZSI. Unfortunately, we ran into a bit of a kink doing this. The most straightforward approach is the code in Listing 1, but it turns out that this requires the server to handle message parameters by their order as well as their name, but the Borland Delphi implementation with which the Captain Haddock server is implemented isn't so flexible, so it gets confused.

Listing 1: Attempt at a ZSI program to access the Captain Haddock SOAP service

		
  #http://xmethods.net/detail.html?id=175

import sys

#Import the ZSI client
from ZSI.client import Binding

u = '/scripts/Haddock.exe/soap/IHaddock'
n = 'urn:HaddockIntf-IHaddock'
b = Binding(url=u, ns=n, host='www.tankebolaget.se', port=80,
            tracefile=sys.stdout)

try:
  lang = sys.argv[1]
except IndexError:
  lang = 'us'

result = b.Curse(lang)

print 'What captain Haddock had to say: "%s"'%result


Because this simple approach would work with many SOAP 1.1 servers, let's have a look, even though it doesn't give us the expected result. First of all we import the ZSI client module, and the Binding class, which implements the machinery for setting up and invoking SOAP requests. Next we set up the components of the address of the SOAP end point. Note that we set up the host, the URL path, and the port separately. You cannot use a single URL, as you can with SOAP.py. These components are used to create a binding to the remote server.

Finally, we call the Curse method on the binding object to send the remote request, and we get back the result in the method return. But as we mentioned, this simple approach doesn't work with the Captain Haddock server. We must use a more complex structure to communicate the LangCode parameter to the server. The code in Listing 2 does the trick.

Listing 2: Working ZSI program to access the Captain Haddock SOAP service

		
  import sys

#Import the ZSI client
from ZSI import TC
from ZSI.client import Binding

u = '/scripts/Haddock.exe/soap/IHaddock'
n = 'urn:HaddockIntf-IHaddock'
b = Binding(url=u, host='www.tankebolaget.se', port=80, ns=n)

try:
  lang = sys.argv[1]
except IndexError:
  lang = 'us'

class CurseRequest:
    def __init__(self, langCode):
        self.LangCode = langCode

CurseRequest.typecode=TC.Struct(CurseRequest,
                                [TC.String('LangCode')],
                                'Curse',
                                inline=1)

try:
    result_list = b.RPC(u, 'Curse', CurseRequest(lang), TC.Any(aslist=1))
    #Extract the first returned parameter
    result = result_list[0]
    print 'What captain Haddock had to say: "%s"'%result
except:
    raise
    print 'reply=', b.reply_code
    print 'reply_msg=', b.reply_msg
    print 'headers=', b.reply_headers
    print 'data=', b.data

The TypeCode module allows a precise and extensible mapping between Python data types and SOAP data types. It has a set of built-in classes for String, Integer, etc. It also defines a Struct class for defining arbitrary data aggregations, and the special class Any which can be used to represent any built-in or derived type and thus is the basis of dynamic typing. These latter two classes are the key to navigating around the many interoperability problems in SOAP encoding that stem from different marshalling and unmarshalling of typed data. Even though we had to go through some contortions to get ZSI to align with the Delphi server we're addressing, at least ZSI gives you all the machinery to do so. With the other Python SOAP implementations we have had to hack at the client library code to sort out interoperability problems.

We then create a class, CurseRequest, which will be marshalled into the form required by the Captain Haddock server. We define an instance attribute for the required LangCode parameter. Then we assign the class a ZSI type code that marshals the LangCode value into a string. One note is the inline=1 specification, which disables marshalling using multi-reference values. By default, ZSI marshals structures using multi-reference, which means that you can express a value in one part of the SOAP message and refer to that same value by reference in another part of the SOAP message, in order to avoid duplication of the values. SOAP encoding multi-reference values are much less commonly used than just simple values, so it's odd that the default in ZSI's structure types is to be marshalled as multi-reference.

This detail and others seem to reinforce a comment Rich Salz, author of ZSI, made to us: that ZSI is actually designed primarily for complex Web services. He promises to make it a bit easier soon to use ZSI against the toys that seem to be the majority of published services.

The following illustrates the use of the example client code



$ python curse-zsi.py 
What captain Haddock had to say: "Polygraphs!"

It is quite easy to enable debugging and tracing in ZSI. Just pass a tracefile=sys.stdout parameter to the Binding initializer. After making this modification, the sample ZSI session looks like:



$ python curse-zsi-trace.py 
_________________________________ Thu Jan 31 10:25:09 2002 REQUEST:
<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"

 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" >
<SOAP-ENV:Body>

<Curse id="822b6dc">
<LangCode id="81089d0" xsi:type="xsd:string">us</LangCode>
</Curse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

_________________________________ Thu Jan 31 10:25:10 2002 RESPONSE:
Server: Microsoft-IIS/5.0
Date: Thu, 31 Jan 2002 17:18:37 GMT
Content-Type: text/xml
Content-Length: 524
Content:

<?xml version="1.0" encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">

<SOAP-ENV:Body>
<NS1:CurseResponse xmlns:NS1="urn:HaddockIntf-IHaddock" 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<NS1:return xsi:type="xsd:string">Sea-lice!</NS1:return>
</NS1:CurseResponse>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
What captain Haddock had to say: "Sea-lice!"



View Python SOAP libraries, Part 2 Discussion

Page:  1 2 3 Next Page: Are you being served?

First published by IBM developerWorks


Copyright 2004-2017 GrindingGears.com. All rights reserved.
Article copyright and all rights retained by the author.