27
27
# THE SOFTWARE.
28
28
# -------------------------------------------------------------------------------
29
29
30
- try :
31
- from urllib .error import HTTPError
32
- from urllib .request import Request , urlopen
33
- except ImportError :
34
- # Python 2 backward compatibility
35
- from urllib2 import urlopen , Request , HTTPError
36
-
37
30
from contextlib import closing
38
31
from logging import LoggerAdapter , getLogger
39
32
from time import sleep
33
+ from urllib .error import HTTPError
34
+ from urllib .parse import urljoin
35
+ from urllib .request import Request , urlopen
40
36
from xml .etree import ElementTree
41
37
42
38
from .time_util import Timer
@@ -104,6 +100,10 @@ class WPS10Service:
104
100
request
105
101
"""
106
102
103
+ DEFAULT_CONTENT_TYPE = "application/xml; charset=utf-8"
104
+ RETRY_TIME = 20 # seconds
105
+ STATUS_POLL_RETRIES = 3 # re-try attempts
106
+
107
107
STATUS = {
108
108
"{http://www.opengis.net/wps/1.0.0}ProcessAccepted" : "ACCEPTED" ,
109
109
"{http://www.opengis.net/wps/1.0.0}ProcessFailed" : "FAILED" ,
@@ -120,12 +120,17 @@ def __init__(self, url, headers=None, logger=None):
120
120
self .headers = headers or {}
121
121
self .logger = self ._LoggerAdapter (logger or getLogger (__name__ ), {})
122
122
123
- def retrieve (self , request , handler = None ):
123
+ def retrieve (self , request , handler = None , content_type = None ):
124
124
"""Send a synchronous POST WPS request to a server and retrieve
125
125
the output.
126
126
"""
127
+ headers = {
128
+ ** self .headers ,
129
+ "Content-Type" : content_type or self .DEFAULT_CONTENT_TYPE ,
130
+ }
131
+
127
132
return self ._retrieve (
128
- Request (self .url , request , self . headers ), handler , self .error_handler
133
+ Request (self .url , request , headers ), handler , self .error_handler
129
134
)
130
135
131
136
def retrieve_async (
@@ -136,16 +141,20 @@ def retrieve_async(
136
141
cleanup_handler = None ,
137
142
polling_interval = 1 ,
138
143
output_name = "output" ,
144
+ content_type = None ,
139
145
):
140
146
"""Send an asynchronous POST WPS request to a server and retrieve
141
147
the output.
142
148
"""
143
149
timer = Timer ()
144
150
status , percentCompleted , status_url , execute_response = self .submit_async (
145
- request
151
+ request ,
152
+ content_type = content_type ,
146
153
)
147
154
wpsstatus = WPSStatus ()
148
- wpsstatus .update (status , percentCompleted , status_url , execute_response )
155
+ wpsstatus .update (
156
+ status , percentCompleted , urljoin (self .url , status_url ), execute_response
157
+ )
149
158
150
159
def log_wpsstatus (wpsstatus ):
151
160
self .logger .info (
@@ -169,7 +178,7 @@ def log_wpsstatus_percentCompleted(wpsstatus):
169
178
170
179
last_status = wpsstatus .status
171
180
last_percentCompleted = wpsstatus .percentCompleted
172
- wpsstatus .update (* self .poll_status (wpsstatus .url ))
181
+ wpsstatus .update (* self .poll_status (urljoin ( self . url , wpsstatus .url ) ))
173
182
174
183
if wpsstatus .status != last_status :
175
184
log_wpsstatus (wpsstatus )
@@ -197,7 +206,9 @@ def retrieve_async_output(self, status_url, output_name, handler=None):
197
206
"""Retrieve asynchronous job output reference."""
198
207
self .logger .debug ("Retrieving asynchronous job output '%s'." , output_name )
199
208
output_url = self .parse_output_reference (status_url , output_name )
200
- return self ._retrieve (Request (output_url , None , self .headers ), handler )
209
+ return self ._retrieve (
210
+ Request (urljoin (self .url , output_url ), None , self .headers ), handler
211
+ )
201
212
202
213
@staticmethod
203
214
def parse_output_reference (xml , identifier ):
@@ -210,25 +221,60 @@ def parse_output_reference(xml, identifier):
210
221
elm_reference = elm .find (
211
222
"./{http://www.opengis.net/wps/1.0.0}Reference"
212
223
)
213
- return elm_reference .attrib ["href" ]
224
+ return (
225
+ elm_reference .attrib .get ("{http://www.w3.org/1999/xlink}href" )
226
+ or elm_reference .attrib ["href" ]
227
+ )
214
228
215
- def submit_async (self , request ):
229
+ def submit_async (self , request , content_type = None ):
216
230
"""Send a POST WPS asynchronous request to a server and retrieve
217
231
the status URL.
218
232
"""
219
233
self .logger .debug ("Submitting asynchronous job." )
234
+ headers = {
235
+ ** self .headers ,
236
+ "Content-Type" : content_type or self .DEFAULT_CONTENT_TYPE ,
237
+ }
220
238
return self ._retrieve (
221
- Request (self .url , request , self . headers ),
239
+ Request (self .url , request , headers ),
222
240
self .parse_status ,
223
241
self .error_handler ,
224
242
)
225
243
226
244
def poll_status (self , status_url ):
227
245
"""Poll status of an asynchronous WPS job."""
228
246
self .logger .debug ("Polling asynchronous job status." )
229
- return self ._retrieve (
230
- Request (status_url , None , self .headers ), self .parse_status
231
- )
247
+
248
+ for index in range (self .STATUS_POLL_RETRIES + 1 ):
249
+
250
+ if index == 0 :
251
+ self .logger .debug ("Polling asynchronous job status." )
252
+ else :
253
+ self .logger .debug (
254
+ "Polling asynchronous job status. Retry attempt #%s." , index
255
+ )
256
+
257
+ try :
258
+ return self ._retrieve (
259
+ Request (status_url , None , self .headers ), self .parse_status
260
+ )
261
+ except Exception as error :
262
+ if index < self .STATUS_POLL_RETRIES :
263
+ self .logger .error (
264
+ "Status poll failed. Retrying in %s seconds. %s: %s" ,
265
+ self .RETRY_TIME ,
266
+ error .__class__ .__name__ ,
267
+ error ,
268
+ )
269
+ else :
270
+ self .logger .error (
271
+ "Status poll failed. No more retries. %s: %s" ,
272
+ error .__class__ .__name__ ,
273
+ error ,
274
+ )
275
+ raise
276
+
277
+ sleep (self .RETRY_TIME )
232
278
233
279
@classmethod
234
280
def parse_status (cls , response ):
0 commit comments