I couldn't talk about SOAP client with Python without mentionning ZSI. This wonderfull library is probably much more powerfull than the others, but also more complicated to use. I will show how to consume a Java WSDP and a .NET web service with ZSI in this page. Like usually, make sure you have the last ZSI package installed on your system, see ZSI at sourceforge.
In this first exemple I will just consume two methods: readLS (taking and returning a string) and readTemp (taking an integer and returning a float). We will see the complexity of what should have been a simple call returning simple type. The Java WSDP web service needs named parameters, so we have to register a class for each parameter and also for each return type. Let's see how to handle simple call with ZSI.
Example 4-7. ZSI SOAP client for my Java WSDP web service.
# rcx web service client with ZSI
import sys
from ZSI import TC
from ZSI.client import Binding
serverPath = '/rcx-ws/rcx'
myns = 'http://phonedirlux.homeip.net/types'
mybind = Binding(url=serverPath, nsdict={'ns1':myns}, host='www.pascalbotte.be', port=80,
tracefile=sys.stdout)
# we define a class mapping
# the parameter for the function readLS
class ReadLSRequest:
def __init__(self, String_1):
self.String_1 = String_1
ReadLSRequest.typecode = TC.Struct(ReadLSRequest,
[TC.String('String_1')],
'ns1:readLS',
inline=1)
# same for the parameter of
# the readTemp function
class ReadTempRequest:
def __init__(self, int_1):
self.int_1 = int_1
ReadTempRequest.typecode = TC.Struct(ReadTempRequest,
[TC.Integer('int_1')],
'ns1:readTemp',
inline=1)
# we have to define also a class
# for the return type of the functions
class ReadLSResponse:
def __init__(self, result):
self.result = result
def __str__(self):
return self.result
ReadLSResponse.typecode = TC.Struct(ReadLSResponse,
[TC.String('result')], 'readLSResponse')
class ReadTempResponse:
def __init__(self, result):
self.result = result
def __str__(self):
return self.result
ReadTempResponse.typecode = TC.Struct(ReadTempResponse,
[TC.Decimal('result')], 'readTempResponse')
try:
# call readLS
mybind.Send(serverPath, 'ns1:readLS', ReadLSRequest('your message or e-mail'))
result = mybind.Receive(ReadLSResponse.typecode)
# call readTemp
mybind.Send(serverPath, 'ns1:readTemp', ReadTempRequest(1))
temp = mybind.Receive(ReadTempResponse.typecode)
# print the results
print 'Light sensor simulation: %s' % result
print 'temperature: %.1f' % temp.result
except:
raise
print 'reply=', mybind.reply_code
print 'reply_msg=', mybind.reply_msg
print 'headers=', mybind.reply_headers
print 'data=', mybind.data
The remote procedure status() return my rcxResponse object. This object contains some internal values of the RCX like the internal clock, memory, etc... The best way to build the necessary classes, by hand, to unmarshal the complex response returned by the web service, is to start from a SOAP excerpt of the message received.
...
<ns1:statusResponse>
<response>
<battery>6.2</battery>
<currentTime>123</currentTime>
<memory>12335</memory>
<memoryTot>15600</memoryTot>
<status>Rcx on-line</status>
</response>
</ns1:statusResponse>
...We will need a structure of two classes, one to tell ZSI how to deserialize <statusResponse>, and one for the <response>. The last one containing the values requested. Find below the code needed to consume the status() remote procedure.
For my remote procedure collInt() returning an array of integer, we will use the tool wsdl2py that comes with the ZSI package. Go to your_Python_install_directory/Scripts and you will find the script wsdl2py. You can invoke the tool:
your_prompt:\> python wsdl2py -u url_path_to_wsdl
The command will generate two files, one contains the stub class to help invoke the web service, and we won't use it. The other is the class type needed to serialize/deserialize the SOAP message. Note here my Java WSDP web service needs namespace prefix, and we have to make a little modification in the class generated to specify the use of this prefix (ns1). The file we have to modify is: MyRcxService_services_types.py, and more specifically we will have to modify the class collInt_Def and collInt_Dec. Find below these classes with the modification in bold:
class collInt_Def(ZSI.TCcompound.Struct):
schema = 'http://phonedirlux.homeip.net/types'
type = 'collInt'
def __init__(self, name=None, ns=None, **kw):
# internal vars
TClist = []
oname = name
if name:
aname = '_%s' % name
if ns:
oname += ' xmlns:ns1="%s"' % ns
else:
oname += ' xmlns="%s"' % self.__class__.schema
else:
aname = None
ZSI.TCcompound.Struct.__init__(self, self.__class__, TClist,
pname=name, inorder=0,
aname=aname, oname=oname,
**kw)
class collInt_Dec(collInt_Def):
literal = "ns1:collInt"
schema = "http://phonedirlux.homeip.net/types"
def __init__(self, name=None, ns=None, **kw):
name = name or self.__class__.literal
ns = ns or self.__class__.schema
ns1.collInt_Def.__init__(self, name=name, ns=ns, **kw)
self.typecode = ns1.collInt_Def(name=name, ns=ns, **kw)Now we can consume the remote procedure collInt() returning an array of integer.
Similarily, we are going to use the same technique for the remote procedure collPos of my Java WSDP web service. This remote procedure return an array of object PosCol containing two integer variable (XPos and YPos). Modify the class collPos_Dec and collPos_Def by adding the namespace prefix the same way than for the collInt remote procedure. Here is the code allowing to call this function:
from MyRcxService_services import RcxReadLS_collPosWrapper,\ RcxReadLS_collPosResponseWrapper from ZSI.client import Binding import sys serverPath = '/rcx-ws/rcx' mybind = Binding(url=serverPath, host='www.pascalbotte.be', port=80) msg = RcxReadLS_collPosWrapper() msgresp = RcxReadLS_collPosResponseWrapper() mybind.Send(serverPath, 'ns1:collPos', msg) cpresp = mybind.Receive(msgresp) collObj = cpresp._result for obj in collObj: print 'Object XPos: %d, YPos: %d' % (obj._XPos, obj._YPos)
We continue here our interoperability demo cases by showing how to consume the .NET Infobel web service with ZSI and wsdl2py.
Go to the Scripts directory under your Python install and use the following command line to generate the class needed to serialize/deserialize the SOAP messages exchanged between your Python client and the .NET web service.
your_prompt:\> python wsdl2py -u http://hal.kapitol.com/infobelservices/service1.asmx?WSDL
Copy the two files: InfobelSearchEngines_services.py and InfobelSearchEngines_services_types.py in your working directory. Then create your client:
We see here how it is relatively easy to access a .NET web service from the ZSI, provided you use the wsdl2py script!.
![]() | ![]() | ![]() |
| Python XML HTTP Post to send a SOAP message to a JWSDP or to a .NET web service. | ![]() My home page Index RSS | Make a SOAP client with C/C++ and gSOAP. |