An unofficial addendum to the XML-RPC specification defines three special functions in the "system" namespace, as a convenience to users who might not know which functions an XML-RPC server supports, or what those functions might do. These special functions are the web service equivalent of Python's ever-useful dir and help commands. Both SimpleXMLRPCServer and CGIXMLRPCRequestHandler support two of the three introspection functions, assuming you call the register_introspection_functions method on the server or handler object after instantiating it:
What It Does
Returns the names of all the functions the server makes available
Returns a string with documentation for the named function. Implemented in Python by returning the function's Python docstring.
Returns the signature and return type of the named function. Not automatically supported by the Python implementation because Python function definitions don't include type information.
Try It Out Using the XML-RPC Introspection API
Start up and connect to the BittyWiki XML-RPC server (or CGI) as before. In addition to the BittyWiki methods shown earlier, you can use the XML-RPC introspection methods:
>>> import xmlrpclib
>>> server = xmlrpclib.ServerProxy("http://localhost:8001/") >>> server.system.listMethods()
['bittywiki.delete', 'bittywiki.getPage', 'bittywiki.save', 'system.listMethods', 'system.methodHelp', 'system.methodSignature'] >>> server.system.methodHelp("bittywiki.save") 'Saves a page of the wiki.'
>>> server.system.methodSignature("bittywiki.save") 'signatures not supported'
XML-RPC introspection isn't meant as a substitute for a human-readable API document. For one thing, it's hard to get people excited about using your API if they must use XML-RPC method calls to even see what it is. However, the introspection API does make it a lot easier to experiment with an XML-RPC web service from an interactive Python shell.
A Python-centric reference to the XML-RPC introspection API is at www.python.org/doc/lib/ serverproxy-objects.html.
Many SOAP-based web services define their interface in a WSDL file. WSDL is basically a machine-parseable version of the human-readable API document shown earlier in this section.
Recall that XML-RPC defines a set of rules for transforming a few basic data structures into XML documents and back into data structures. WSDL allows such rules to be constructed on the fly. It's more or less a programming-language-agnostic schema for describing functions: their names, the data types of their arguments, and the data types of their return values. Although WSDL is associated with SOAP, it's possible to use SOAP without using WSDL (in fact, we did just that throughout this chapter's section on SOAP).
A WSDL file is an XML document (of course!), which defines the following aspects of your web service inside its definitions element:
□ Any custom data types defined by your web service. These go into complexType elements of a types list.
□ The formats of the messages sent and received by your web service; that is, the signatures and return values of the functions your web service defines. These are defined in a series of message elements, and may make reference to any custom data types you defined earlier.
□ The names of the functions your web service provides, along with the input and output messages expected by each. This is in the portType element, which contains an operation element for each of the web service's functions.
□ A binding of your web service's functions to a specific protocol—that is, HTTP. For simple SOAP applications, this section is an exercise in redundancy: You end up just listing all of your functions again. It exists because SOAP is protocol-independent; you need to explicitly state that you're exposing your methods over HTTP. This goes in the binding element.
□ Finally, the URL to your web service. This is defined in the service element.
Here's BittyWiki.wsdl, a WSDL file for the SOAP API exposed by BittyWiki:
<?xml version="1.0"?> <definitions name="BittyWiki"
<!--Descriptions of the functions exposed by the BittyWiki API. The definitions of the functions reference message elements which will be defined afterwards.--> <portType name="BittyWikiPortType"> <operation name="getPage">
<input message="sendPageName"/> <output message="getPageText"/> </operation>
<input message="sendPageNameAndText"/> <output message="getStatusMessage"/> </operation>
<input message="sendPageName"/> <output message="getStatusMessage"/> </operation>
The WSDL parser now knows which functions are exposed by BittyWiki, but nothing about the signatures or return types of those functions. Those come next:
<!--Descriptions of the method signatures used by the BittyWiki API. For instance, this first one is for a method where you send in a page name. This method signature is common to getPage() and delete().--> <message name="sendPageName">
<part name="pageName" type="xsd:string"/> </message>
<part name="pageName" type="xsd:string"/> <part name="pageText" type="xsd:string"/> </message>
<!--Descriptions of the possible return values obtained from the BittyWiki API. The first one is for a return value that contains a wiki page's markup: that is, the return value of getPage().--> <message name="getPageText">
<part name="pageText" type="xsd:string"/> </message>
<part name="message" type="xsd:string"/>
A rather redundant section follows, as the four SOAP functions are bound to SOAP-over-HTTP:
<!--A binding of the BittyWiki API functions (previously defined only in the abstract) to the specific "SOAP-over-HTTP" protocol.--> <binding type="BittyWikiPortType" name="BittyWikiSOAPBinding"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="getPage"> <input><soap:body use="literal" namespace="urn:BittyWiki" /></input> <output><soap:body use="literal" namespace="urn:BittyWiki" /></output> </operation>
<operation name="save"> <input><soap:body use="literal" namespace="urn:BittyWiki" /></input> <output><soap:body use="literal" namespace="urn:BittyWiki" /></output> </operation>
<operation name="delete"> <input><soap:body use="literal" namespace="urn:BittyWiki" /></input> <output><soap:body use="literal" namespace="urn:BittyWiki" /></output> </operation>
Finally, the code to let WSDL know where to find the BittyWiki web service:
<!--A link to the BittyWiki web service on the web. It uses the BittyWiki API defined in BittyWikiPortType, as realized by its SOAP-over-HTTP binding, BittyWikiSOAPBinding.--> <service name="BittyWiki"> <port name="BittyWikiPort" binding="BittyWikiSOAPBinding">
<soap:address location="http://localhost:8002/"/> </port> </service>
The BittyWiki API doesn't define any custom data types, so there's no types element in its WSDLfile. If you want to see a types element that has some complexTypes, look at the WSDLfile for the Google Web APIs.
WSDL is pretty complicated: That WSDL file is bigger than the Python script implementing the web service it describes. WSDL files are usually generated from the corresponding web service source code, so that humans don't have to specify them. It's not possible to do this from Python code because a big part of WSDL is defining the data types, and Python functions don't have predefined data types. Both the SOAPpy and ZSI libraries can parse WSDL (in fact, they share a WSDL library: wstools), but there's not much in the way of Python-specific resources for generating WSDL.
Try It Out Manipulating BittyWiki through a WSDL Proxy
The following looks more or less like the previous example of BittyWiki manipulation through direct SOAP calls:
>>> proxy = SOAPpy.WSDL.Proxy(open("BittyWiki.wsdl")) >>> proxy.getPage("SOAPViaWSDL")
<Fault SOAP-ENV:Server: Method urn:BittyWiki:getPage failed.: _main_.NoSuchPage
Traceback (most recent call last):
SOAPpy.Types.faultType: <Fault SOAP-ENV:Server: Method urn:BittyWiki:getPage failed.: _main_.NoSuchPage SOAPViaWSDL>
>>> proxy.save("SOAPViaWSDL", "This page created through SOAP via WSDL.") 'Page saved.'
'This page created through SOAP via WSDL.'
The main difference here is that going through WSDL will stop you from calling web service methods that don't exist:
>>> proxy.noSuchMethod() Traceback (most recent call last):
AttributeError: noSuchMethod >>>
>>> server = SOAPpy.SOAPProxy("http://localhost:8002/", "urn:BittyWiki") >>> server.noSuchMethod()
<Fault SOAP-ENV:Client: No method urn:BittyWiki:noSuchMethod found: exceptions.AttributeError BittyWikiAPI instance has no attribute 'noSuchMethod'> Traceback (most recent call last):
SOAPpy.Types.faultType: <Fault SOAP-ENV:Client: No method urn:BittyWiki:noSuchMethod found: exceptions.AttributeError BittyWikiAPI instance has no attribute 'noSuchMethod'>
Both attempts to call noSuchMethod raised an exception, but going through WSDL meant the problem was caught on the local machine instead of the server. This ability is a lot more interesting to a compiled language: WSDL makes it possible to apply the same compile-time checks to web service calls as to local function calls.
Was this article helpful?