WSDL

From PyWPS
Jump to: navigation, search

SOAP, WSDL and PyWPS

WPS 1.0.0 has some lacks concerning the support for SOAP (Simple Object Access Protocol) and WSDL (Web Services Description Language), the documentation provided by OGC isn't very detailed and the examples aren't very "straight-forward". WPS 2.0.0 is expected to be a big jump forward concerning SOAP/WSDL.

SOAP

SOAP (Simple Object Access Protocol) is a messaging framework, meaning, a structured way to pass, explain and process a message. When we say "a SOAP message" we are refering to some sort of XML content that is associated with a SOAP header than in turn is wrapped by a SOAP envelope. This SOAP structure is normally transported by HTTP, nevertheless nothing prevents the use of protocols like: FTP,SSH,SMTP etc.


SOAP structure.png


A WPS Getcapabilities request using SOAP 1.1 would look like this:

<?xml version="1.0" encoding="UTF-8"?>

 <!--
 Equivalent GET request is
 http://foo.bar/foo?Service=WPS&Version=1.0.0&Request=GetCapabilities&Language=en-CA
 -->
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
 <SOAP-ENV:Body>
     <wps:GetCapabilities xmlns:ows="http://www.opengis.net/ows/1.1"
      xmlns:wps="http://www.opengis.net/wps/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsGetCapabilities_request.xsd"
      language="en" service="WPS">
           <wps:AcceptVersions>
               <ows:Version>1.0.0</ows:Version>
           </wps:AcceptVersions>
     </wps:GetCapabilities>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This case is simple case since we just have a XML request wrapped by a SOAP envelope (<SOAP-ENV:Envelope>), this SOAP example doesn't provide much of SOAP's functionalities.

The majority of SOAP's functionalities are concentrated in the header, for example a SOAP request as follows:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
 <SOAP-ENV:Header>
   <h:BasicAuth xmlns:h="http://soap-authentication.org/basic/2001/10/"
   SOAP-ENV:mustUnderstand="1">
   <Name>bacon</Name>
   <Password>eggs</Password>
   </h:BasicAuth>
 </SOAP-ENV:Header>
<SOAP-ENV:Body>
<wps:GetCapabilities> ......</wps:GetCapabilities>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Would request that a basic authentication between client and the server. This is achieved by including the BasicAuth element and the the name/password, to be used. one important attribute is the mustUnderstand, meaning all message should be "understood" and no parts should be "ignore" (means: don't ignore any content that is in the SOAP envelope).

Currentely PyWPS will accept SOAP XML requests, but it will not process any header content basically PyWPS strips the SOAP envelope, processes the content as a normal WPS request and finally wraps the WPS response in the SOAP envelope.

WSDL

WSDL stands for Web Services Description Language. WSDL is a document written in XML that describes a Web service. It specifies the location of the service and the operations (or methods) the service exposes basically its an XML document that contains more or less the same information as GetCapabilities/DescribeProcess, but in a generalist way without the use of OGC's nomenclature and standards. The problem with WSDL is that we have to "explain" everything to system, meaning we have to explain all WPS structure.

In theory having a WPS instance providing WSDL information should allow for it to be used as a generalistic web service in any programming environment or orchestration language like BPEL

WSDL is more complicated that a GetCapabilities/DescribeProcess sequence. An XML WSDL document has the following structure:

WSDL 11vs20.png


Following the WSDL 1.1 standard we have the following XML sections:

<definitions>

 <types>
 definition of types........
 </types>

 <message>
 definition of a message....
 </message>

 <portType>
 definition of a port.......
 </portType>

 <binding>
 definition of a binding....
 </binding>

 <service>
 defintion of a service
 <service>

 </definitions>


  • Types:

A typical XML schema data description that will be used to describe the messages being passed by the web sevices. For example:

<xsd:element name="GetCapabilities">
<xsd:complexType>
<xsd:sequence>
 <xsd:element maxOccurs="1" minOccurs="0" name="updateSequence" type="xsd:string" />
 <xsd:element maxOccurs="1" minOccurs="0" name="acceptFormats" type="xsd:string" />
 <xsd:element maxOccurs="1" minOccurs="0" name="acceptVersions" type="xsd:string" />
 </xsd:sequence>
 </xsd:complexType>
 </xsd:element>

or

<xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_request.xsd"/>
 </xsd:schema>

In the last example we indicate the use of the wpsGetCapabilities_request.xsd schema, meaning we define the XML structure of a wpsGetCapabilties

  • Messages:

A message defines the data elements of an operation, meaning, from all the data types defined in <types>, message A will need type 1 and 2. Other people prefer to think the message as the arguments inside a function (use in traditional programming).

If we thing about GetCapabilites , we will have 2 messages, one to request it and one with the response (a web service has two messages: input and output)

<message name="GetCapabilitiesRequest">
 <part name="msg" element="wps:GetCapabilities"/>
 </message>
 <message name="GetCapabilitiesResponse">
 <part name="msg" element="wps:Capabilities"/>
 </message>

We could imagine name="msg" as the attribute name and element="wps:GetCapabilities" as attribute type being passed to a function GetCapabilitiesRequest(). Please note that a message can have more <part> sections.

So far we can have N types and M messages that support any combination of types, but still we dont have any information on what the web service will use.

  • portType:

Describes a web service, the operations that can be performed, and the messages that are involved, now we are going to have an actual definition on how GetCapabilities works:

 <portType name="PywpsServer_PortType">
 <operation name="GetCapabilities">
 <input message="tns:GetCapabilitiesRequest"/>
 <output message="tns:GetCapabilitiesResponse"/>
 <fault name="ExceptionResponse" message="tns:ExceptionResponse" />
 </operation>
 </portType>

The <portType> groups all the services provided by a server, in a complete WSDL file we would have inside this element operations like: GetCapabiltiies, DescribeProcess etc.

In the example above we define an operation called GetCapabilities, that has an input defined by a message called GetCapabilitiesRequest (defined in the message section) that has an output message defined in message GetCapabiltiiesResponse. In case of fault (OGC Exception) the service will reply with a different message called ExceptionResponse (that is not shown in the message example)

so far we have: data blocks (types), messages organizing the data blocks and portTypes that organizes the messages.... so what's missing ?! Defining how messages are transported (Binding)

  • Binding:

The <binding> element provides specific details on how a portType operation will actually be transmitted over the wire, either SOAP, HTTP GET, HTTP POST etc

 <binding name="PywpsServer_Binding" type="tns:PywpsServer_PortType">
 <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
 <operation name="GetCapabilities">
 <soap:operation soapAction="http://localhost/wps.py/GetCapabilities" style="document"/>
 <input name="GetCapabilitiesRequest">
 <soap:body use="literal" />
 </input>
 <output name="GetCapabilitiesResponse">
 <soap:body use="literal" />
 </output>
 <fault name="ExceptionResponse">
 <soap:fault name="ExceptionResponse" use="literal" />
 </fault>
 </operation>
 </binding>

The binding above indicates that a SOAP binding (using the standard SOAP style document (SOAP has RPC or Document styles)) will be used to run GetCapabilities and that input/output/exception messages are also expected to use SOAP. The use=literal more or less indicates that all the elements contents have been already defined, this attribute is not so important in our case but for more information please see (http://www.eherenow.com/soapfight.htm)

  • Service:

So, now that we know how to "transport" things we still need to indicate "to where" we have to send them.

 <service name="PywpsServer">
 <documentation>None</documentation>
 <port name="PywpsServer_Port" binding="tns:PywpsServer_Binding">
 <soap:address location="http://localhost/wps.py"/>
 </port>
 </service>

The XML service description is simple, we need to include the portType, binding and the URL (<soap:address location="http://localhost/wps.py"/>) to be used.

In the end a WSDL file describing a WPS would look like this:

<definitions name="PywpsServer" targetNamespace="http://localhost/wps.py_wsdl" xmlns:tns="http://localhost/wps.py_wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl">
 <types>
<!-- OGCs dataType definitions, complete schemas -->
 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_request.xsd"/>
 </xsd:schema>
 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd"/>
 </xsd:schema>

 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_request.xsd"/>
 </xsd:schema>
 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd"/>
 </xsd:schema>

 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd"/>
 </xsd:schema>
 <xsd:schema targetNamespace="http://www.opengis.net/wps/1.0.0">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd"/>
 </xsd:schema>

 <xsd:schema targetNamespace="http://www.opengis.net/ows/1.1">
 <xsd:include
 schemaLocation="http://schemas.opengis.net//ows/1.1.0/owsAll.xsd"/>
 </xsd:schema>

 <xsd:schema targetNamespace="http://www.opengis.net/ows/1.1">
 <xsd:include
 schemaLocation="http://schemas.opengis.net/ows/1.1.0/owsGetResourceByID.xsd"/>
 </xsd:schema>

 </types>

<!-- messages to be used by operations -->
 <message name="GetCapabilitiesRequest">
 <part name="msg" element="wps:GetCapabilities"/>
 </message>
 <message name="GetCapabilitiesResponse">
 <part name="msg" element="wps:Capabilities"/>
 </message>

 <message name="DescribeProcessRequest">
 <part name="msg" element="wps:DescribeProcess"/>
 </message>
 <message name="DescribeProcessResponse">
 <part name="msg" element="wps:ProcessDescriptions"/>
 </message>

 <message name="ExecuteRequest">
 <part name="msg" element="wps:Execute"/>
 </message>
 <message name="ExecuteResponse">
 <part name="msg" element="wps:ExecuteResponse"/>
 </message>

 <message name="ExceptionResponse">
 <part name="msg" element="wps:ExcepctionReport"/>
 </message>

<!-- OGC's WPS operations -->
 <portType name="PywpsServer_PortType">
 <operation name="GetCapabilities">
 <input message="tns:GetCapabilitiesRequest"/>
 <output message="tns:GetCapabilitiesResponse"/>
 <fault name="ExceptionResponse" message="tns:ExceptionResponse" />
 </operation>
 <operation name="DescribeProcess">
 <input message="tns:DescribeProcessRequest"/>
 <output message="tns:DescribeProcessResponse"/>
 <fault name="ExceptionResponse" message="tns:ExceptionResponse" />
 </operation>
 <operation name="Execute">
 <input message="tns:ExecuteRequest"/>
 <output message="tns:ExecuteResponse"/>
 <fault name="ExceptionResponse" message="tns:ExceptionResponse" />
 </operation>
 </portType>

<!-- Transport of Requests -->
 <binding name="PywpsServer_Binding" type="tns:PywpsServer_PortType">
 <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
 <operation name="GetCapabilities">
 <soap:operation soapAction="http://localhost/wps.py/GetCapabilities" style="document"/>
 <input name="GetCapabilitiesRequest">
 <soap:body use="literal" />
 </input>
 <output name="GetCapabilitiesResponse">
 <soap:body use="literal" />
 </output>
 <fault name="ExceptionResponse">
 <soap:fault name="ExceptionResponse" use="literal" />
 </fault>
 </operation>
 <operation name="DescribeProcess">
 <soap:operation soapAction="http://localhost/wps.py/DescribeProcess" style="document"/>
 <input name="DescribeProcessRequest">
 <soap:body use="literal" />
 </input>
 <output name="DescribeProcessResponse">
 <soap:body use="literal" />
 </output>
 <fault name="ExceptionResponse" >
 <soap:fault name="ExceptionResponse" use="literal" />
 </fault>
 </operation>
 <operation name="Execute">
 <soap:operation soapAction="http://localhost/wps.py/Execute" style="document"/>
 <input name="ExecuteRequest">
 <soap:body use="literal" />
 </input>
 <output name="ExecuteResponse">
 <soap:body use="literal" />
 </output>
 <fault name="ExceptionResponse" >
 <soap:fault name="ExceptionResponse" use="literal" />
 </fault>
 </operation>
 </binding>

<!-- Service web location -->
 <service name="PywpsServer">
 <documentation>None</documentation>
 <port name="PywpsServer_Port" binding="tns:PywpsServer_Binding">
 <soap:address location="http://localhost/wps.py"/>
 </port>
 </service>
</definitions>

Note: Actually the WSDL file above will not work.....The OGC schemas are not properly linked and when a WSDL parser starts to import all OGC's schemas it will rise an error, the best is to deactivate the <type> content using tags or to use some sort of "dummy" dataType. For example the WPS documentation shows the use of schema definition inside the <type> element and doesn't import any of the OGC schemas

The following example for a GetCapabilities dataType

<xsd:element name="GetCapabilities">
<xsd:complexType>
<xsd:sequence>
 <xsd:element maxOccurs="1" minOccurs="0" name="updateSequence" type="xsd:string" />
 <xsd:element maxOccurs="1" minOccurs="0" name="acceptFormats" type="xsd:string" />
 <xsd:element maxOccurs="1" minOccurs="0" name="acceptVersions" type="xsd:string" />
 </xsd:sequence>
 </xsd:complexType>
 </xsd:element>

Getting the WSDL file

The WSDL file should be served using the following request:

http://foo/wps.py?WSDL

When PyWPS recieves this request it will search the pywps.cfg file for the WSDL's location and will send it back. This request returns the general WSDL file that according to WPS 1.0.0 Annex E should contains a complete descriptions of all the services being offered by the server.

What is ExecuteProcess_GMLBuffer ?!

SOAP and WSDL are not prepared to deal with parameter identifier that is used to call a specific process during the Execute request, the work around is to create a new element with the desired operation and process name as follows:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
 <soap:Body>
 <ExecuteProcess_DummyProcess>
 <datainput1>10</datainput1>
 <datainput2>20</datainput2>
 </ExecuteProcess_DummyProcess>
 </soap:Body>
 </soap:Envelope>

Inside the ExecuteProcess_ element we will have each DataInput as an independent child element.


WSDL document should follow the same procedure and a process in the portType would look like:

<operation name="ExecuteProcess_GMLCountPoints">
 <soap:operation soapAction="http://wpsint.tigris.org/soap/SpatialAnalysis/ExecuteProcess_GMLCountPoints" style="document" />
<input name="ExecuteProcess_GMLCountPointsRequest">
 <soap:body use="literal" />
 </input>
<output name="ExecuteProcess_GMLCountPointsResponse">
 <soap:body use="literal" />
 </output>
 </operation>

Therefore each process will have its own dataTypes, messages, operations and bindings all identifier with ExecuteProcess_+<Process name>..

The WPS 1.0.0 is mercury but the same procedure should be used for DescribeProcess (DescribeProcess_+<Process name>)

Where are the WSDL file ?

WPS 1.0.0 in Annex E defines that each process should have his own Execute WSDL file that can be found as follows:

http://hostname/WPSName/identifier[/service.soap]?WSDL

or in the case of the ultimatequestionprocess:

http://foo/wps.py/ultimatequestionprocess?WSDL

Another possibility is for the process to be integrated in the general WSDL document that defines all the service.

Why is this so important !?

Because of something like this:


Taverna WPS.png


WSDL, SOAP services are extensively used by several communities or researchers mainly in bioinformatics, if we have serveral WPSs with proper WSDL files it would be possible to assemble complex execution work flows, were input/outputs are directioned to different WPSs according to the users needs.

SOAP client (using suds)

Suds is a lightweight SOAP python client for consuming Web Services: [1] , documentation can be found here: [2], difference between RemoteProcedureCall (RPC) and Document binding [3]


Here we will show a simple client test using suds to access the SOAP interface and call the services in a WPS instance.

>>>from suds.client import Client
>>>client = Client('http://earthserver.pml.ac.uk/wps/generic.wsdl')
>>>print client  
Suds ( https://fedorahosted.org/suds/ )  version: 0.4 GA  build: R699-20100913

Service ( PlymouthMarineLaboratory-RemoteSensingGroup.WpsRasterInstance-SoapBranchRev1312 ) tns="http://earthserver.pml.ac.uk/wps/generic.cgi_wsdl"
   Prefixes (2)
      ns0 = "http://www.opengis.net/ows/1.1"
      ns1 = "http://www.opengis.net/wps/1.0.0"
   Ports (1):
      (PlymouthMarineLaboratory-RemoteSensingGroup.WpsRasterInstance-SoapBranchRev1312_Port)
         Methods (37):
            DescribeProcess(ns0:CodeType[] Identifier, )
            Execute(ns0:CodeType Identifier, ns1:DataInputsType DataInputs, ns1:ResponseFormType ResponseForm, )
            ExecuteProcessAsync_buoyGraphic(xs:string date, )
            ExecuteProcessAsync_dummyprocess(xs:float input2, xs:float input1, )
            GetCapabilities(ns0:AcceptVersionsType AcceptVersions, )
:
:
            GetCapabilities(ns0:AcceptVersionsType AcceptVersions, )
       Types (78):
            ns0:AbstractReferenceBaseType
            ns0:AcceptFormatsType
            ns0:CodeType
            ns0:AcceptVersionsType
            ns0:GetCapabilitiesType

Some processes have simple inputs for example ExecuteProcessAsync_dummyprocess has 2 float type inputs: input1 and input2. Calling this service would be as simple as .ExecuteProcessAsync_dummyprocess(100,300)

Normally services require some sort of complex type input e.g: GetCapabilities requires an ns0:AcceptVersionsType object that need to be created first, normally ComplexData I/O are mapped to xs:anyType as defined in the WPS schema.

getCapabilities

GetCapabilities method can be called with a ns0:AcceptVersionsType argument that defines the service version or just with an empty argument list:

>>> client.service.GetCapabilities()
DEBUG:suds.client:sending to (http://earthserver.pml.ac.uk/wps/generic.cgi)
message:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.opengis.net/wps/1.0.0" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns1:Body>
      <ns0:GetCapabilities/>
   </ns1:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:headers = {'SOAPAction': u'"http://earthserver.pml.ac.uk/wps/generic.cgi/GetCapabilities"', 
'Content-Type': 'text/xml; charset=utf-8'}
DEBUG:suds.client:http succeeded:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<wps:Capabilities service="WPS" version="1.0.0" xml:lang="en-CA" xmlns:xlink="http://www.w3.org/1999/xlink" 
xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 
http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd" updateSequence="1">
	<ows:ServiceIdentification>
		<ows:Title>Plymouth Marine Laboratory - Remote Sensing Group. WPS Raster instance - SOAP branch rev 1312</ows:Title>
		<ows:Abstract>Generic WPS services</ows:Abstract>
		<ows:Keywords>

To request a getCapabilities with a specific version, it is necessary to use suds factory to generate the complex object, the AcceptVersionType object that has property "Version[]" this property is a list of strings with versions

>>> version=client.factory.create("ns0:AcceptVersionsType")
>>> version
(AcceptVersionsType){
   Version[] = <empty>
 }
>>> version.Version=["1.0.0"]
>>> client.service.GetCapabilities(version)
DEBUG:suds.client:sending to (http://earthserver.pml.ac.uk/wps/generic.cgi)
message:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.opengis.net/wps/1.0.0" 
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns2="http://www.opengis.net/ows/1.1" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns1:Body>
      <ns0:GetCapabilities>
         <ns0:AcceptVersions>
            <ns2:Version>1.0.0</ns2:Version>
         </ns0:AcceptVersions>
      </ns0:GetCapabilities>
   </ns1:Body>
</SOAP-ENV:Envelope>

Notice that now, the SOAP body contains several child elements below ns0:GetCapabilities with element ns2:Version containing the version information.

It's more convenient to assign result to variables for processing, in the following example a list of process names is obtained using the following code:

>>> getCapabilities=client.service.GetCapabilities()
>>> listOfProcesses=getCapabilities.ProcessOfferings.Process
>>> print listOfProcesses[0]
(ProcessBriefType){
   _processVersion = "0.1"
   Identifier = "temperatureConverter"
   Title = "Simple Temperature Converter, Centigrades to Kelvin"
   Abstract = "Simple Temperature Converter, Centigrades to Kelvin"
   Metadata[] = 
      (MetadataType){
         _href = "http://vocab.nerc.ac.uk/collection/P24/current/KELVIN"
         _title = "Temperature"
      },
 }

>>> [item.Identifier for item in listOfProcesses]
[temperatureConverter, oilspil, fetchMODISTimeStamp, fetchMODIS, fetchMRCS, r.colors, reprojectCoords, 
intersectBBOX, reprojectImage, mergeImages, buoyGraphic, gml2svg, geotiff2png, dummyprocess, gdalinfo, ncdump, ultimatequestionprocess]

DescribeProcess

DescribeProcess uses a "funny" object to define the process identifier, in the service list the operation is identified as follows:

DescribeProcess(ns0:CodeType[] Identifier, )

The codeType object is built using the factory and the process name is set to its value:

>>> codeType=client.factory.create("ns0:CodeType")
>>> codeType
(CodeType){
   value = None
   _codeSpace = ""
 }
>>>codeType.value="dummyprocess"
>>>client.service.DescribeProcess(codeType)

Unfortunally service.DescribeProcess will raise a MissigParameterValue (Identifier ) pywps exception, this is caused by an incorrect namespace in the SOAP request:

<SOAP-ENV:Envelope xmlns:ns0="http://www.opengis.net/wps/1.0.0" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns1:Body>
      <ns0:DescribeProcess>
         <ns0:Identifier>dummyprocess</ns0:Identifier>
      </ns0:DescribeProcess>
   </ns1:Body>
</SOAP-ENV:Envelope>

The Identifier is located in the WPS namespace and not in the OWS one. This problem could be related to the following suds' ticket: [4]

The solution is to change he SOAP request using a plugin strategy. The plugin code will determine if the request is a DescribeProcess and set the correct namespace:

from suds.plugin import MessagePlugin
from suds.client import Client
class DescribeProcessPlugin(MessagePlugin):
    def marshalled(self, context):
	try:
        	body = context.envelope.getChild('Body')
        	if "DescribeProcess" in body[0].name:
			body[0][0].setPrefix("ns2")
	except:
		pass
url="http://earthserver.pml.ac.uk/wps/generic.cgi?WSDL"       
client=Client(url,plugins=[DescribeProcessPlugin()])
client.service.DescribeProcess()

Execute

Execute processes are defined by ExecuteProcessAsyn_ or ExecuteProcess_ in front of the process name. To tun the dummyProcess it is enough:


>>>result=client.service.ExecuteProcessAsync_dummyprocess(100,200)

DEBUG:suds.client:sending to (http://earthserver.pml.ac.uk/wps/generic.cgi)
message:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.opengis.net/wps/1.0.0" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns1:Body>
      <ns0:ExecuteProcess_dummyprocess>
         <input2>100</input2>
         <input1>200</input1>
      </ns0:ExecuteProcess_dummyprocess>
   </ns1:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:headers = {'SOAPAction': u'"http://earthserver.pml.ac.uk/wps/generic.cgi/ExecuteProcess_dummyprocess"',
 'Content-Type': 'text/xml; charset=utf-8'}
DEBUG:suds.client:http succeeded:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ExecuteProcess_dummyprocessResponse>
<output2Result>199.0</output2Result>
<output1Result>201.0</output1Result>
</ExecuteProcess_dummyprocessResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
(reply){
   output2Result = 199.0
   output1Result = 201.0
 }

>>> print result.output2Result
199.0
>>> print result.output1Result
201.00

Other processes may have anyType or boolean input, for their complexData Input. The anyType content is identical to any content located in the WPS ComplexData Input, meaning a base64 structure or a URL pointing to the data location. The following examples will use a gdalinfo service that has the following arguments: ExecuteProcess_gdalinfo(xs:boolean nogcp, xs:boolean nomd, xs:boolean mm, xs:boolean checksum, xs:boolean hist, xs:anyType input, xs:boolean stats, )

Using an url + Boolean:


>>> client.service.ExecuteProcess_gdalinfo(input="http://rsg.pml.ac.uk/wps/testdata/elev_srtm_30m.tif",stats=True)


<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.opengis.net/wps/1.0.0" 
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns1:Body>
      <ns0:ExecuteProcess_gdalinfo>
         <input>http://rsg.pml.ac.uk/wps/testdata/elev_srtm_30m.tif</input>
         <stats>true</stats>
      </ns0:ExecuteProcess_gdalinfo>
   </ns1:Body>
</SOAP-ENV:Envelope>
:
Driver: GTiff/GeoTIFF
Files: ./pywpsInputDXXjHH
Size is 500, 450
:
    STATISTICS_MAXIMUM=189.81750488281
    STATISTICS_MEAN=113.49829166407
    STATISTICS_MINIMUM=-32.654445648193
    STATISTICS_STDDEV=23.718306793643

Using a local image + Boolean (local image has to be open and coded to base64)

>>> import base64
>>> imageBase64=base64.b64encode(open("/users/rsg/jmdj/elev_srtm_30m.tif","r").read())
>>> result=client.service.ExecuteProcess_gdalinfo(input=imageBase64,stats=True)

Attributes

If necessary some attributes can be se using the following code

from suds.plugin import MessagePlugin
class LanguagePlugin(MessagePlugin):
    def marshalled(self, context):
        body = context.envelope.getChild('Body')
        getCap = body[0]
        getCap.set('service', 'WPS')
        getCap.set('language', 'EN-CA')


The usage of attributes in SOAP content is unrecomented

Logging

Suds logging is based on the logging module, the following is the most extreme verbose:

import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
logging.getLogger('suds.wsdl').setLevel(logging.DEBUG)

For simple SOAP message checking it is enough:

import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)

--Jmdj 17:15, 4 July 2012 (BST)