As of this writing, server-side implementations of SOAP services are virtually nonexistent. The few that do exist stray from the emerging standards and are likely to continue to morph as they discover what the users really want.
The most stable web service sample implementations come from Microsoft and IBM. In this section, we create a Python client that utilizes the calculator service that ships with Microsoft’s free SOAP Toolkit 2.0, available from http://msdn.microsoft.com. As such, the service must run on either Windows NT Server 4.0 or Windows 2000 Server. The clients may run anywhere COM runs. The toolkit sets up very easily on these platforms, and is ready to go after the install script is finished.
The Python client created in this section uses COM to connect with the MSSOAP type library objects, and interacts with the service. The clients can run on virtually all flavors of Windows, provided they have access to the services and WSDL files residing on the server. Note that the clients can easily (and probably most conveniently) run on the same machine as the servers.
SOAP and web services are new, and as such require the installation of software for developers who wish to experiment the technology. The rest of this chapter relies on COM; therefore, if you are not familiar with the workings of COM and Python, this section helps to get things set up.
The following steps are required in order to run the Python client example in this section.
The Microsoft SOAP Toolkit 2.0 must be installed on a server.
The WSDL and service implementations that ship with the SOAP Toolkit must be visible via HTTP on your network, per the installation examples that ship with the toolkit. This is true even if you are running the client and server on the same machine.
Python clients must have win32all.exe (Python COM Support available from http://aspn.activestate.com and developed by Mark Hammond) installed, and the utility script makepy.py must be applied against the SOAP Type Library. Additionally, if running the clients on a different machine, the SOAP Toolkit, or at least the COM object .dll files, must be installed.
Microsoft has made available client DLLs and robust client and server example implementations in their SOAP Toolkit 2.0. This is a free download, fully supported as well as available from http://msdn.microsoft.com/downloads/default.asp.
When installing the toolkit, you automatically receive an updated MSXML 3.0 package, which fully supports XSLT. Appendix E covers working with Python and the MSXML parser.
When you install the samples, you need to follow the
instructions for creating a virtual directory in IIS (Internet
Information Server, the default HTTP implementation on Windows
servers) that can point to the samples. The instructions call for
putting an entry for MSSOAPSampleServer
in your hosts file (c:winntsystem32hosts), but this step
is optional, and is only required if you intend to run the Microsoft
sample clients. For the purpose of this chapter, you are writing a
Python client from scratch; therefore, your
existing hostname is fine. You need to be able to see the samples
directory via HTTP, as the instructions indicate.
If you plan on running the Python client from the same machine that hosts the samples, you won’t need to install anything else (except perhaps Python COM support) to proceed.
If you have not used COM from Python (and we haven’t yet in this book), you need to download and install Python COM support. Start by retrieving the appropriate installer from the Web available at http://aspn.activestate.com/ASPN/Downloads/ActivePython/Extensions/Win32all.
There are links and instructions to download win32all.exe for your version of Python; read the information on this web page carefully to be sure you get the right version. This installer provides full support for COM from your Python programs, and allows you to implement COM servers from other languages to use.
Even if you already have Python COM support (or just installed it), you need to tweak Python’s access to the SOAP Type Library. Unfortunately, the authors of the SOAP Toolkit objects rely on Visual Basic’s and Windows Scripting’s ability to set object properties like this:
Object.Property("PropertyName") = NewPropertyValue
That’s not even legal syntax in Python! Visual C++ uses a slightly different syntax, allowing an overloaded operator to provide syntax that matches Python’s dictionary assignment syntax:
Object.Property["PropertyName"] = NewPropertyValue
Great! This syntax works just fine in Python if you are
assigning a value to an element of a member dictionary.
Unfortunately, the COM API does not automatically convert this COM
construct to Python member dictionaries. For this specific object,
Property
is a method, and there
is no way to assign a value in Python to an object method.
Typically, components implement access methods, or at the minimum,
implement SetProperty
and
GetProperty
type constructs.
Thankfully, the makepy.py
script that ships with win32all.exe wraps another Python
interface on top of the COM objects and uses a lower-level API to
access them correctly, allowing you to overcome this aspect.
To run makepy.py, launch it from within the win32comclient directory of your Python installation (typically, C:Python20win32comclient):
C:Python20win32comclient>python makepy.py
A dialog GUI pops up that displays all of the different type
libraries registered on your system. Find Microsoft SOAP Type Library (1.0) and
click Ok. The script suddenly produces a flurry of activity
(evidenced by frequent text output) and writes a .py file with a monstrously long name
inside the folder C:Python20win32comgen_py. You probably
will not need to see that file again, as you can use a standard call
to win32com.client.Dispatch
to
invoke the object and it will seek out the updated Python-friendly
interface that makepy.py
created. Figure 9-1
shows the dialog in action with Microsoft
SOAP Type Library (1.0) highlighted.
Once you’ve created the Python interface for the object by selecting Ok, you may go about instantiating the object normally, but now you can call constructs such as:
Object.SetProperty("PropertyName", "NewValue")
The makepy.py
accomplishes more than just correcting property assignments for
Python syntax, as it can also be used to smooth out parameter types
and properties for all sorts of COM objects. See the makepy
documentation for more
information.
Before we get into developing the client, it’s important to understand the server setup. SOAP and web services are inherently cross-platform. While you are running services implemented on Windows, clients can conceivably be written on any platform, provided they can either create the correct kind of SOAP packet to invoke the services, or interpret the published WSDL to wrap the services with a local stub object.
The Python client implemented here uses COM access to a SOAP connector and serializer; however any SOAP implementation should be able to connect to these services and utilize them.
The server setup that ships with the toolkit is merely a collection of WSDL files describing services, as well as service end-points that implement them. The end-points are in a variety of languages and techniques, ranging from Active Server Pages to ISAPI plug-ins. The interface to these services is purely SOAP.
The SOAP Toolkit ships with a Calculator service. This service offers four different operations that it performs on two supplied parameters, much like a basic calculator. This example is similar in functionality to the VBScript sample client that ships with the Toolkit. The most significant difference between the two is that your client is implemented entirely in Python from scratch, not in VBScript.
The calculator operations are add
, subtract
, multiply
, and divide
. If your SOAP Toolkit is freshly
installed, it’s a good idea to verify that the calculator service is
running properly by testing it with one of the sample clients that
ships with the toolkit—it certainly aids in the debugging phase of
things (if you need to debug!) In other words, knowing that your web
server and SOAP implementations are working helps to isolate any
problems or errors that may occur when running your Python
client.
To properly build the SOAP packet, define a portion of the SOAP Action URI, allowing for the appending of different method names. You also want to reuse the namespace URI between method invocations. These two reused items, along with the service end-point, are defined up front as global variables:
import win32com.client # SOAP Action URI BaseSoapActionUri = "http://tempuri.org/action/Calc." # Namespace WrapperElementNamespace = "http://tempuri.org/message/" # Service End Point EndPointUrl = "http://centauri/MSSoapSamples/Calc/Service/SrSz/AspVbs/Calc.asp"
You can take calculator functionality and embed it within a method, allowing the method to take a string representing the operation you wish to perform, along with the parameters. This method can then repeatedly be called to generate answers:
def Calculate(Op, Val1, Val2): """Return a result based on the operator 'Op' and two input values.""" # Instantiate HttpConnector connector = win32com.client.Dispatch("MSSOAP.HttpConnector")
Immediately as the function begins, the HTTP SOAP connector is created as shown in the preceding code. The connector is then handed some critical information regarding the location of the service:
# Set properties (will fail if makepy.py wasn't run # on SOAP type library) connector.SetProperty("EndPointURL", EndPointUrl) connector.SetProperty("SoapAction", BaseSoapActionUri + Op) # Start SOAP message connector.BeginMessage( )
What’s critical in this code snippet is the SetProperty
call to change the value of
SoapAction
. If you note, the
BaseSoapActionUri
is concatenated
with the desired operation Op
. If
using the add
method, create a SoapAction
string similar to:
http://tempuri.org/action/Calc.Add |
Now the task turns to actually creating the SOAP envelope.
# Create a serialization object serializer = win32com.client.Dispatch("MSSOAP.SoapSerializer") # Attach it to the connector created earlier serializer.Init(connector.InputStream)
The serializer
is bound to
the connector
’s input stream, so
that the SOAP packet actually finds its way to the service
end-point. Creating the rest of the packet follows the same pattern
shown earlier, with methods representing the starting and ending of
elements:
# Create SOAP Envelope serializer.startEnvelope( ) serializer.startBody( ) serializer.startElement(Op, WrapperElementNamespace, '', "m") serializer.startElement("A") serializer.writeString(Val1) serializer.endElement( ) serializer.startElement("B") serializer.writeString(Val2) serializer.endElement( ) serializer.endElement( ) serializer.endBody( ) serializer.endEnvelope( ) # Finish SOAP message connector.EndMessage( )
The reader
is brought in to
read and interpret the result of the call to the service. As
discussed earlier, in Section
9.2, the reader
is
attached to the connector
’s
output stream, and digests the information as it is returned:
# Create SOAP reader object reader = win32com.client.Dispatch("MSSOAP.SoapReader") reader.Load(connector.OutputStream) # check for errors if reader.Fault: print "Error: ", reader.faultstring.Text # Return calculation value return reader.RPCResult.Text
If there has been an error, it is reflected in the reader.Fault
property. When a SOAP call
fails, the SOAP server sends a fault entry back to the client. SOAP
uses many of the same semantics as HTTP regarding propagating error
conditions back to the caller. In fact, when using SOAP over HTTP,
SOAP is bound to some of the same exact error conditions—SOAP must
send back an HTTP 500 Internal Server Error, even if the web server
behaves as expected but the code handling the SOAP request fails.
(This is required to ensure that knowledge of the implementation
details of the server is not needed by the client; it is not a
failing of the SOAP or HTTP protocols.)
Example 9-1 shows the complete listing of PyCalcSerial.py.
""" Python MSSOAP Serializer Example """ # import support for COM import win32com.client # SOAP Action URI BaseSoapActionUri = "http://tempuri.org/action/Calc." # Namespace WrapperElementNamespace = "http://tempuri.org/message/" # Service End Point EndPointUrl = "http://centauri/MSSoapSamples/Calc/Service/SrSz/AspVbs/Calc.asp" # Calculate(operation, value1, value2) # Takes an operator (as a word like "Add") along # with two values and returns the result def Calculate(Op, Val1, Val2): # Instantiate HttpConnector connector = win32com.client.Dispatch("MSSOAP.HttpConnector") # Set properties (will fail if makepy.py wasn't run # on SOAP type library) connector.SetProperty("EndPointURL", EndPointUrl) connector.SetProperty("SoapAction", BaseSoapActionUri + Op) # Start SOAP message connector.BeginMessage( ) # Create a serialization object serializer = win32com.client.Dispatch("MSSOAP.SoapSerializer") # Attach it to the connector created earlier serializer.Init(connector.InputStream) # Create SOAP Envelope serializer.startEnvelope( ) serializer.startBody( ) serializer.startElement(Op, WrapperElementNamespace, '', "m") serializer.startElement("A") serializer.writeString(Val1) serializer.endElement( ) serializer.startElement("B") serializer.writeString(Val2) serializer.endElement( ) serializer.endElement( ) serializer.endBody( ) serializer.endEnvelope( ) # Finish SOAP message connector.EndMessage( ) # Create SOAP reader object reader = win32com.client.Dispatch("MSSOAP.SoapReader") reader.Load(connector.OutputStream) # check for errors if reader.Fault: print "Error: ", reader.faultstring.Text # Return calculation value return reader.RPCResult.Text # Main line-- do some calculations print "Using Service:", EndPointUrl print "Calculate 3 * 4: ", print Calculate("Multiply", 3, 4) print "Calculate 4 - 3: ", print Calculate("Subtract", 4, 3) print "Calculate 345 + 1004: ", print Calculate("Add", 345, 1004) print "Calculate 115 / 5: ", print Calculate("Divide", 115, 5)
To run the example, just launch it from your command line. You should then see output similar to the following:
C:my-dir> python PyCalcSerial.py Using Service: http://centauri/MSSoapSamples/Calc/Service/SrSz/AspVbs/Calc.asp Calculate 3 * 4: 12 Calculate 4 - 3: 1 Calculate 345 + 1004: 1349 Calculate 115 / 5: 23
SOAP is the heart of web services, at least as they are being described by most of the big players. WSDL, if implemented correctly, is seldom even seen by developers as it can be automatically generated from object source files. When support for WSDL matures, most languages (most likely including Python) will have WSDL generators that generate WSDL directly from class code. Of course, these tools can also be provided by SOAP server implementations, or by Python object servers such as Zope.