def _method_factory(self, pid): """Create a custom function signature with docstring, instantiate it and pass it to a wrapper which will actually call the process. Parameters ---------- pid: str Identifier of the WPS process. Returns ------- func A Python function calling the remote process, complete with docstring and signature. """ process = self._processes[pid] required_inputs_first = sorted(process.dataInputs, key=sort_inputs_key) input_names = [] # defaults will be set to the function's __defaults__: # A tuple containing default argument values for those arguments that have defaults, # or None if no arguments have a default value. defaults = [] for inpt in required_inputs_first: input_names.append(sanitize(inpt.identifier)) if inpt.minOccurs == 0 or inpt.defaultValue is not None: default = inpt.defaultValue if inpt.dataType != "ComplexData" else None defaults.append(utils.from_owslib(default, inpt.dataType)) defaults = tuple(defaults) if defaults else None body = dedent(""" inputs = locals() inputs.pop('self') return self._execute('{pid}', **inputs) """).format(pid=pid) func_builder = FunctionBuilder( name=sanitize(pid), doc=utils.build_process_doc(process), args=["self"] + input_names, defaults=defaults, body=body, filename=__file__, module=self.__module__, ) self._inputs[pid] = {} if hasattr(process, "dataInputs"): self._inputs[pid] = OrderedDict( (i.identifier, i) for i in process.dataInputs) self._outputs[pid] = {} if hasattr(process, "processOutputs"): self._outputs[pid] = OrderedDict( (o.identifier, o) for o in process.processOutputs) func = func_builder.get_func() return func
def _make_output(self, convert_objects=False): Output = namedtuple( sanitize(self.process.identifier) + 'Response', [sanitize(o.identifier) for o in self.processOutputs]) Output.__repr__ = utils.pretty_repr return Output(*[ self._process_output(o, convert_objects) for o in self.processOutputs ])
def _method_factory(self, pid): """Create a custom function signature with docstring, instantiate it and pass it to a wrapper which will actually call the process. Parameters ---------- pid: str Identifier of the WPS process. Returns ------- func A Python function calling the remote process, complete with docstring and signature. """ process = self._processes[pid] input_defaults = OrderedDict() for inpt in process.dataInputs: iid = sanitize(inpt.identifier) default = getattr(inpt, "defaultValue", None) if inpt.dataType != 'ComplexData' else None input_defaults[iid] = utils.from_owslib(default, inpt.dataType) body = dedent(""" inputs = locals() inputs.pop('self') return self._execute('{pid}', **inputs) """).format(pid=pid) func_builder = FunctionBuilder( name=sanitize(pid), doc=utils.build_process_doc(process), args=["self"] + list(input_defaults), defaults=tuple(input_defaults.values()), body=body, filename=__file__, module=self.__module__, ) self._inputs[pid] = {} if hasattr(process, "dataInputs"): self._inputs[pid] = OrderedDict( (i.identifier, i) for i in process.dataInputs ) self._outputs[pid] = {} if hasattr(process, "processOutputs"): self._outputs[pid] = OrderedDict( (o.identifier, o) for o in process.processOutputs ) func = func_builder.get_func() return func
def nb_form(wps, pid): """Return a Notebook form to enter input values and launch process.""" if wps._notebook: return notebook.interact(func=getattr(wps, sanitize(pid)), inputs=list(wps._inputs[pid].items())) else: return None
def _execute(self, pid, **kwargs): """Execute the process.""" wps_inputs = [] for name, input_param in self._inputs[pid].items(): value = kwargs.get(sanitize(name)) if value is not None: if isinstance(input_param.defaultValue, ComplexData): encoding = input_param.defaultValue.encoding mimetype = input_param.defaultValue.mimeType if isinstance(value, ComplexData): inp = value else: if utils.is_embedded_in_request(self._wps.url, value): # If encoding is None, this will return the actual encoding used (utf-8 or base64). value, encoding = embed(value, mimetype, encoding=encoding) else: value = fix_url(value) inp = utils.to_owslib(value, data_type=input_param.dataType, encoding=encoding, mimetype=mimetype) else: inp = utils.to_owslib(value, data_type=input_param.dataType) wps_inputs.append((name, inp)) wps_outputs = [ (o.identifier, "ComplexData" in o.dataType) for o in self._outputs[pid].values() ] mode = self._mode if self._processes[pid].storeSupported else SYNC try: wps_response = self._wps.execute( pid, inputs=wps_inputs, output=wps_outputs, mode=mode ) if self._interactive and self._processes[pid].statusSupported: if self._notebook: notebook.monitor(wps_response, sleep=.2) else: self._console_monitor(wps_response) except ServiceException as e: if "AccessForbidden" in str(e): raise UnauthorizedException( "You are not authorized to do a request of type: Execute" ) raise # Add the convenience methods of WPSResult to the WPSExecution class. This adds a `get` method. utils.extend_instance(wps_response, WPSResult) wps_response.attach(wps_outputs=self._outputs[pid], converters=self._converters) return wps_response
def nb_form(wps, pid): """Return a Notebook form to enter input values and launch process.""" if wps._notebook: return notebook.interact( func=getattr(wps, sanitize(pid)), inputs=wps._inputs[pid].items()) else: return None
def interact(func, inputs): """Return a Notebook form to enter input values and launch process. The output is stored in the `widget.result` attribute of the response. """ ws = {sanitize(key): input2widget(inpt) for key, inpt in inputs} out = widgets.interact_manual(func, **ws) out.widget.children[-2].description = 'Launch process' # IPython.display.display(out) return out
def output_format_widget_values(self, widgets): """Return the `output_formats` dict from output_formats widgets.""" out = {} for key, val in widgets.items(): utils.add_output_format(out, output_identifier=sanitize(key), mimetype=val.value, as_ref=True) if out: return out return {}
def test_sanitize(): assert utils.sanitize('output') == 'output' assert utils.sanitize('My Output 1') == 'my_output_1' assert utils.sanitize('a.b') == 'a_b' assert utils.sanitize('a-b') == 'a_b' assert utils.sanitize('return') == 'return_' assert utils.sanitize('Finally') == 'finally_'
def test_sanitize(): # noqa: D103 assert utils.sanitize("output") == "output" assert utils.sanitize("My Output 1") == "my_output_1" assert utils.sanitize("a.b") == "a_b" assert utils.sanitize("a-b") == "a_b" assert utils.sanitize("return") == "return_" assert utils.sanitize("Finally") == "finally_"
def _build_inputs(self, pid, **kwargs): """Build the input sequence from the function arguments.""" wps_inputs = [] for name, input_param in list(self._inputs[pid].items()): arg = kwargs.get(sanitize(name)) if arg is None: continue values = ( [ arg, ] if not isinstance(arg, (list, tuple)) else arg ) supported_mimetypes = [v.mimeType for v in input_param.supportedValues] for value in values: # if input_param.dataType == "ComplexData": seems simpler if isinstance(input_param.defaultValue, ComplexData): # Guess the mimetype of the input value mimetype, encoding = guess_type(value, supported_mimetypes) if encoding is None: encoding = input_param.defaultValue.encoding if isinstance(value, ComplexData): inp = value # Either embed the file content or just the reference. else: if utils.is_embedded_in_request(self._wps.url, value): # If encoding is None, this will return the actual encoding used (utf-8 or base64). value, encoding = embed(value, mimetype, encoding=encoding) else: value = fix_url(str(value)) inp = utils.to_owslib( value, data_type=input_param.dataType, encoding=encoding, mimetype=mimetype, ) else: inp = utils.to_owslib(value, data_type=input_param.dataType) wps_inputs.append((name, inp)) return wps_inputs
def __init__(self, func): self.result = None wps = func.__self__ pid = self.pid = [ k for k in wps._processes.keys() if sanitize(k) == func.__name__ ][0] self.process = wps._processes[pid] inputs = list(wps._inputs[pid].items()) outputs = list(wps._outputs[pid].items()) # Create widgets iw = self.input_widgets(inputs) ofw = self.output_formats_widgets(outputs) go = widgets.Button( description="Launch process", layout=widgets.Layout(width="100%"), button_style="success", ) out = widgets.Output() # Interaction logic def execute(change): """Execute callback when "Launch process" button is clicked.""" go.disabled = True of = self.output_format_widget_values(ofw) if of: of = {"output_formats": of} kwargs = {**self.input_widget_values(iw), **of} with out: self.result = func(**kwargs) # Connect callback to button go.on_click(execute) # Create GUI ui = self.build_ui(iw, ofw, go) IPython.display(ui, out)
def _make_output(self, convert_objects=False): Output = namedtuple(sanitize(self.process.identifier) + 'Response', [sanitize(o.identifier) for o in self.processOutputs]) Output.__repr__ = utils.pretty_repr return Output(*[self._process_output(o, convert_objects) for o in self.processOutputs])
def test_sanitize(): assert utils.sanitize('output') == 'output' assert utils.sanitize('My Output 1') == 'my_output_1' assert utils.sanitize('a.b') == 'a_b' assert utils.sanitize('a-b') == 'a_b'
def __init__( self, url, processes=None, converters=None, username=None, password=None, headers=None, verify=True, cert=None, verbose=False, progress=False, version=WPS_DEFAULT_VERSION, ): """ Args: url (str): Link to WPS provider. config (Config): an instance processes: Specify a subset of processes to bind. Defaults to all processes. converters (dict): Correspondence of {mimetype: class} to convert this mimetype to a python object. username (str): passed to :class:`owslib.wps.WebProcessingService` password (str): passed to :class:`owslib.wps.WebProcessingService` headers (str): passed to :class:`owslib.wps.WebProcessingService` verify (bool): passed to :class:`owslib.wps.WebProcessingService` cert (str): passed to :class:`owslib.wps.WebProcessingService` verbose (str): passed to :class:`owslib.wps.WebProcessingService` progress (bool): If True, enable interactive user mode. version (str): WPS version to use. """ self._converters = converters self._interactive = progress self._mode = ASYNC if progress else SYNC self._notebook = notebook.is_notebook() self._inputs = {} self._outputs = {} if not verify: import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) self._wps = WebProcessingService( url, version=version, username=username, password=password, verbose=verbose, headers=headers, verify=verify, cert=cert, skip_caps=True, ) try: self._wps.getcapabilities() except ServiceException as e: if "AccessForbidden" in str(e): raise UnauthorizedException( "You are not authorized to do a request of type: GetCapabilities" ) raise self._processes = self._get_process_description(processes) # Build the methods for pid in self._processes: setattr(self, sanitize(pid), types.MethodType(self._method_factory(pid), self)) self.logger = logging.getLogger('WPSClient') if progress: self._setup_logging() self.__doc__ = utils.build_wps_client_doc(self._wps, self._processes)
def __init__( self, url, processes=None, converters=None, username=None, password=None, headers=None, auth=None, verify=True, cert=None, verbose=False, progress=False, version=WPS_DEFAULT_VERSION, caps_xml=None, desc_xml=None, language=None, ): """ Args: url (str): Link to WPS provider. config (Config): an instance processes: Specify a subset of processes to bind. Defaults to all processes. converters (dict): Correspondence of {mimetype: class} to convert this mimetype to a python object. username (str): passed to :class:`owslib.wps.WebProcessingService` password (str): passed to :class:`owslib.wps.WebProcessingService` headers (str): passed to :class:`owslib.wps.WebProcessingService` auth (requests.auth.AuthBase): requests-style auth class to authenticate, see https://2.python-requests.org/en/master/user/authentication/ verify (bool): passed to :class:`owslib.wps.WebProcessingService` cert (str): passed to :class:`owslib.wps.WebProcessingService` verbose (str): passed to :class:`owslib.wps.WebProcessingService` progress (bool): If True, enable interactive user mode. version (str): WPS version to use. language (str): passed to :class:`owslib.wps.WebProcessingService` ex: 'fr-CA', 'en_US'. """ self._converters = converters self._interactive = progress self._mode = ASYNC if progress else SYNC self._notebook = notebook.is_notebook() self._inputs = {} self._outputs = {} if not verify: import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) if headers is None: headers = {} if auth is not None: if isinstance(auth, tuple) and len(auth) == 2: # special-case basic HTTP auth auth = requests.auth.HTTPBasicAuth(*auth) # We only need some headers from the requests.auth.AuthBase implementation # We prepare a dummy request, call the auth object with it, and get its headers dummy_request = requests.Request("get", "http://localhost") r = auth(dummy_request.prepare()) auth_headers = ["Authorization", "Proxy-Authorization", "Cookie"] headers.update( {h: r.headers[h] for h in auth_headers if h in r.headers}) self._wps = WebProcessingService(url, version=version, username=username, password=password, verbose=verbose, headers=headers, verify=verify, cert=cert, skip_caps=True, language=language) try: self._wps.getcapabilities(xml=caps_xml) except ServiceException as e: if "AccessForbidden" in str(e): raise UnauthorizedException( "You are not authorized to do a request of type: GetCapabilities" ) raise self._processes = self._get_process_description(processes, xml=desc_xml) # Build the methods for pid in self._processes: setattr(self, sanitize(pid), types.MethodType(self._method_factory(pid), self)) self.logger = logging.getLogger('WPSClient') if progress: self._setup_logging() self.__doc__ = utils.build_wps_client_doc(self._wps, self._processes)
def input_widgets(self, inputs): """Return input parameter widgets.""" return {sanitize(key): input2widget(inpt) for key, inpt in inputs}
def _execute(self, pid, **kwargs): """Execute the process.""" wps_inputs = [] for name, input_param in self._inputs[pid].items(): value = kwargs.get(sanitize(name)) if value is not None: if isinstance(input_param.defaultValue, ComplexData): encoding = input_param.defaultValue.encoding mimetype = input_param.defaultValue.mimeType if isinstance(value, ComplexData): inp = value else: if utils.is_embedded_in_request(self._wps.url, value): # If encoding is None, this will return the actual encoding used (utf-8 or base64). value, encoding = embed(value, mimetype, encoding=encoding) else: value = fix_url(value) inp = utils.to_owslib(value, data_type=input_param.dataType, encoding=encoding, mimetype=mimetype) else: inp = utils.to_owslib(value, data_type=input_param.dataType) wps_inputs.append((name, inp)) wps_outputs = [(o.identifier, "ComplexData" in o.dataType) for o in self._outputs[pid].values()] mode = self._mode if self._processes[pid].storeSupported else SYNC try: wps_response = self._wps.execute(pid, inputs=wps_inputs, output=wps_outputs, mode=mode) if self._interactive and self._processes[pid].statusSupported: if self._notebook: notebook.monitor(wps_response, sleep=.2) else: self._console_monitor(wps_response) except ServiceException as e: if "AccessForbidden" in str(e): raise UnauthorizedException( "You are not authorized to do a request of type: Execute") raise # Add the convenience methods of WPSResult to the WPSExecution class. This adds a `get` method. utils.extend_instance(wps_response, WPSResult) wps_response.attach(wps_outputs=self._outputs[pid], converters=self._converters) return wps_response