API access

From PyWPS
Jump to: navigation, search

API access

PyWPS can be imported as a standard python module and run as a python script. The project unittest are a good source of examples on how to interact with PyWPS module.

New instance and quick start

Assuming that Pywps is installed in the default python packages folder or that Python's path contain the PyWPS location, the module imported in the standard way:

Python 2.6.4 (r264:75706, Jun  4 2010, 18:20:31) 
[GCC 4.4.4 20100503 (Red Hat 4.4.4-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pywps

To run a request and response sequence is necessay to perform 3 steps:

  1. Define if we are dealing with a GET or POST request (key-values or XML string content).
    • Create a pywps.Pywps instance
  2. Parse the input request
  3. Execute request and wait for results

GET versus POST information is necessary so that pywps knows what request parser it has to use. PyWPS package provides 2 constants (pywps.METHOD_GET and pywps.METHOD_POST) that will be used during class instantiation.

The pywps instaniation is done as follows:

>>>wps=pywps.Pywps(method=pywps.METHOD_GET, configFiles=None)

The class's init accepts 2 arguments:

  • Method
  • Path to pywps configuration file

It's usefull that class instation uses a configuration file where process location,available locations wpsoutputs, etc are defined.

Next stage is to pass the WPS request to the object, in this example a getCapabilities:

>>>wps.parseRequest("request=getCapabilities&service=WPS")
{'acceptversions': ['1.0.0'], 'version': '1.0.0', 'request': 'getcapabilities', 'language': 'en-CA', 'service': 'wps'}

The parseRequest() method will parse the request and set the wps inputs dictionary.

>>>wps.inputs
{'acceptversions': ['1.0.0'], 'version': '1.0.0', 'request': 'getcapabilities', 'language': 'en-CA', 'service': 'wps'}

Having parsed the resquest the remaining step is to execute the request:

>>wps.performRequest()
<?xml version="1.0" encoding="utf-8"?>\n<wps:Capabilities service="WPS" version="1.0.0" xml:lang="en-CA"..........

The same example using POST:

>>> import pywps, StringIO
>>> wps=pywps.Pywps(method=pywps.METHOD_POST, configFiles=None)
>>> req="""<?xml version="1.0" encoding="UTF-8"?>
... <ows:GetCapabilities xmlns:ows="http://www.opengis.net/ows/1.1"
... xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:xlink="http://www.w3.org/1999/xlink"
... xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
... xsi:schemaLocation="http://www.opengis.net/ows/1.1 ..\wpsGetCapabilities_request.xsd"
... language="en" service="WPS">
...  <ows:AcceptVersions>
...  <ows:Version>1.0.0</ows:Version>
...  </ows:AcceptVersions>
... </ows:GetCapabilities>"""
>>> reqIO=StringIO.StringIO(req)
>>> t.parseRequest(reqIO)
>>wps.performRequest()
<?xml version="1.0" encoding="utf-8"?>\n<wps:Capabilities service="WPS" version="1.0.0" xml:lang="en-CA"..........

From absolute scratch

PyWPS checks the system variables and certain default folder locations (e.g: /etc) for the pywps.cfg file, normally the configuration file contains the process folder path. If a system doesn't have a configuration file it is likely that PyWPS will pick the default file that comes with the its installation (e.g: /usr/lib/python2.6/pywps-trunk-py2.6.egg.old/pywps/default.cfg). This configuration file has no processes.

Running PyWPS from absolute scratch means that processes have to be dynamically created.

The "dummyprocess" adds and subtracts numbers can be created by creating a WPSPRocess instance:

>>> dummyProcess=pywps.Process.WPSProcess(identifier = "dummyprocess",title="Dummy Process",version = "1.0")

Other class arguments can be used without major problems.

After creating the process instance (dummyProcess) is is necessary to set it's Inputs/Outputs, the procedure is identical to a "normally" created process:

>>> dummyProcess.addLiteralInput(identifier='input1',title='input number',default=100)
>>> dummyProcess.addLiteralInput(identifier='input2',title='input number',default=200)
>>> dummyProcess.addLiteralOutput(identifier="output1",title="Output1 add 1 result")
>>> dummyProcess.addLiteralOutput(identifier="output2",title="Output2 subtract 1 result")

Methods like addLiteralInput()/addLiteralOutput append I/O to the process but also dump the newly created object into the prompt

To check that everything is in order:

>>> dummyProcess.inputs["input1"]
<pywps.Process.InAndOutputs.LiteralInput instance at 0x7faef3a1a098>

The WPSPRocess class comes with a default execute() method that is "overwritten" with the user's algortihm. The current instantiated class has the following code inside:

>>>import inspect
>>> inspect.getsource(dummyProcess.execute)
'def execute(self):
"""This method will be called by :class:`pywps.Wps.Execute.Execute`. Please
redefine this in your process instance
:returns: None if succeeded, error message otherwise."""
    pass'

The execute() method has to be replaced with the necessary algorithm. Class method substitution has to be done specifically for the new instance, the python procedure is properly documented here: [1]

>>> def execute(self):
...     self.outputs["output1"].value=self.inputs["input1"].value + 1
...     self.outputs["output2"].value=self.inputs["input2"].value - 1
...     return
... 
>>> import types
>>> dummyProcess.execute=types.MethodType(execute,dummyProcess)
>>> dummyProcess.execute
<bound method ?.execute of <pywps.Process.WPSProcess instance at 0xd0d248>>

Since we got a bound method reply, we can assume that it worked

The final stage is to perform the request, actually the performRequest() method can take 2 arguments: inputs and processes.

>>> wps2=pywps.Pywps(pywps.METHOD_GET)
>>> inspect.getargspec(w3.performRequest)
ArgSpec(args=['self', 'inputs', 'processes'], varargs=None, keywords=None, defaults=(None, None))

The inputs are returned from parseRequest(), while processes is a list containing out process instances. Therefore the dummyprocess can be run as followed:

>>> wps.performRequest(w3.parseRequest("request=Execute&service=WPS&version=1.0.0&
identifier=dummyprocess&datainputs=[input1=100;input2=100]"),processes=[dummyProcess])
.....
<wps:Data><wps:LiteralData dataType="integer">99</wps:LiteralData>           
</wps:Data>......<wps:LiteralData dataType="integer">101</wps:LiteralData>/wps:ExecuteResponse>
.....

Sometimes pywps may complain about missing parameters inside the configuration file (recall that the used file is a default one from the distribution) e.g:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pywps/__init__.py", line 237, in performRequest
    self.request = Execute(self,processes=processes)
  File "pywps/Wps/Execute/__init__.py", line 301, in __init__
    self.umn = UMN.UMN(self.process, self.getSessionId())
  File "pywps/Wps/Execute/UMN.py", line 123, in __init__
    errorLog=config.getConfigValue("mapserver","errorFile")
  File "pywps/config.py", line 28, in getConfigValue
    value = config.get(*args)
  File "/usr/lib64/python2.6/ConfigParser.py", line 540, in get
    raise NoOptionError(option, section)
ConfigParser.NoOptionError: No option 'errorfile' in section: 'mapserver'

In this case the mapserver script requested the value of variable "errorfile" from mapserver section, since ConfigParse couldn't fetch it, it raised an exception. The workaround is to dynamically set any necessary values:

>>> pywps.config.setConfigValue("mapserver","errorFile", False)

--Jmdj 17:43, 20 June 2012 (BST)