Support for web services in Python is emerging, but is not complete. At the time of this writing, there is lively debate in the XML-SIG (Python XML Special Interest Group; see http://www.python.org/sigs/xml-sig/) concerning SOAP client and server implementations and their ability to interoperate.
SOAP and WSDL, and therefore web services, are being driven largely by companies such as IBM and Microsoft. Microsoft has robust client and server support for web services in their SOAP Toolkit, while IBM is making headway contributing to the Apache SOAP project. Both camps feature support for two common styles of web service access: RPC-like proxy access, and SOAP Serialization access.
You can implement Python web service clients easily today by using one of Python’s bridge mechanisms into subsystems such as COM and Java. Python’s COM support is excellent and is enabled by installing PythonCOM (part of the win32all.exe package from the ActiveState web site). Detailed instructions are provided in Section 9.4.1 later in this chapter.
Through the accepted APIs of web services today, you’ll likely be able to quickly adapt your Python code to use native Python web service support when it matures, although it may be more desirable to write your logic in Python but utilize APIs from Python that are implemented in faster C++. Python makes a great glue language due to its robust object model, sophisticated text and file manipulation, and component access. Utilizing components that are resource intensive (such as Parsers or SOAP clients) may work better if the components are written in fast native code and are driven by your Python code.
With one cross-platform exception, the Python client examples in this chapter rely primarily on using COM to bridge Microsoft’s mature SOAP Serializer and SOAP Connector. Therefore, the Python examples in this section primarily run on Windows platforms, due to their utilization of COM. However, the SOAPy API is covered as well. This is a native Python RPC-like SOAP client implementation. SOAPy should run anywhere Python runs.
As of this writing, SOAPy provides support for RPC-like interaction with WSDL-published web services. SOAPy is currently available on SourceForge (http://soapy.sourceforge.net). SOAPy is inherently cross-platform because it is a native Python implementation.
Working with SOAPy is very simple, as it’s designed to
transparently present a remote web service as if it were a local
Python object. If you download one of the source distributions of
SOAPy, you get a few examples that allow SOAPy to strut its stuff. For
instance, the get_temperature
example that ships with SOAPy allows you to enter a zip code and query
a remote weather service for the current temperature. So, to check the
temperature in Woodinville, Washington:
C:c9>python get_temperature.py 98072 Temperature for 98072 = 53.0 degrees F
While this application is impressive, it immediately inspires curiosity as to how SOAPy works. SOAPy performs this trick in three lines of code:
import soap server = soap.get_proxy('http://www.xmethods.net/sd/TemperatureService.wsdl') temperature = server.getTemp(zipcode=zip)
The secret is that SOAPy interprets the WSDL file, and creates a local stub object for you to work with that seemingly has all of the methods of the remote service.
Ideally, when working with SOAPy, the only method you call is
get_proxy
. Afterwards, you should
be able to use the methods described in the WSDL file located at the
remote service.
When working with MSSOAP, you have the option of using an RPC client or a Serialization client.
The RPC client works essentially the same way that SOAPy does;
however, there are some subtle differences. For example, to initialize
a connection with remote service description, use the mssoapinit
method as opposed to SOAPy’s
get_proxy
, but the net effect is
the same as shown here:
import win32com.client sc = win32com.client.Dispatch("MSSOAP.SoapClient") sc.mssoapinit("http://WebServiceDomain/service.wsdl") response = sc.methodName(param, param) print(response)
Again, the net effect of web service RPC implementations is to allow you to work with a remote object as if it were local. The Serialization method works slightly differently, but gives you finer control over how an actual SOAP request is structured, and allows you to work with a service without necessarily relying upon a service description WSDL file.
Using serialization is more involved than using RPC, but it has its own advantages as well as drawbacks. Serialization gives you fine control over exactly how a SOAP XML request appears. Implementing serialization also allows you to interact with a web service without having to understand WSDL, something that may be of considerable value as SOAP implementations (both client and server) mature.
The main trade-off between RPC and Serialization is the WSDL file. The WSDL file provides an RPC implementation with the information it needs about the service end-point, such as the URIs and namespaces involved, or the parameters and their types. Without RPC and WSDL, you’d need to supply these extra details manually.
Creating a SOAP packet with MSSOAP requires a few
objects, but start with the connector
and the serializer
. You must give the connector
information that is normally
held in a WSDL file. For example, you need to supply the end-point,
the SOAP Action URI, and the namespace:
import win32com.client SoapActionUri = "http://tempuri.org/action/Calc.Add" ElementNamespace = "http://tempuri.org/message/" EndPointUrl = "http://centauri/MSSoapSamples/Calc/Service/SrSz/AspVbs/Calc.asp" connector = win32com.client.Dispatch("MSSOAP.HttpConnector") connector.SetProperty("EndPointURL", EndPointUrl) connector.SetProperty("SoapAction", SoapActionUri) connector.BeginMessage( )
The connector
is now
prepared to connect to the service. All that is left to do is to
prepare the SOAP envelope and execute the call. The SOAP envelope is
also prepared manually.
serializer = win32com.client.Dispatch("MSSOAP.SoapSerializer") serializer.Init(connector.InputStream)
Once the serializer
is
created, it is attached to the connector
for writing to the
service.
You use the serializer
’s methods to actually construct
the SOAP packet, including the method you are targeting, as well as
to supply the parameters. The following lines prepare a SOAP packet
for delivery to a calculator service expecting that parameters A and
B are integer parameters to a method named Add
:
# Create SOAP Envelope serializer.startEnvelope( ) serializer.startBody( ) serializer.startElement("Add", ElementNamespace, '', "m") serializer.startElement("A") serializer.writeString("4") serializer.endElement( ) serializer.startElement("B") serializer.writeString("5") serializer.endElement( ) serializer.endElement( ) serializer.endBody( ) serializer.endEnvelope( ) # Finish SOAP message connector.EndMessage( )
As shown in the previous code, the connector is then
instructed that the complete SOAP message has been prepared with a
call to EndMessage
.
After you’ve completed constructing your serializer
and connector
, a final step is to instantiate
a reader
object to check for
errors with the service and to retrieve the result of the
call.
reader = win32com.client.Dispatch("MSSOAP.SoapReader") reader.Load(connector.OutputStream)
Here, the reader
is
associated with the connector
’s
output stream in order to retrieve the result of the call to the
service. The Fault
attribute of
the reader
indicates success or
failure.
if reader.Fault: print("Error: ", reader.faultstring.Text) print reader.RPCResult.Text
The response from the service is contained in the reader.RPCResult
object. In this
particular case, the response from the calculator service is “9,”
and the sum of the supplied parameters is 4 and 5.