def test_get_exception_from_xml_status(xml_file, ows_exception): # pylint: disable=C0123 # use type instead of isinstance, since we actually want to exclude classes inheritance xml_str = load_example(xml_file, text=True) xml = xml_util.fromstring(xml_str) exc = get_exception_from_xml_status(xml) assert isinstance(exc, OWSException) and type( exc) is not OWSException, "Should be the exact derived type" assert exc.code == ows_exception.code
def get_exception_from_xml_status(xml): # type: (Union[xml_util.XML, str]) -> Optional[owsexceptions.OWSException] """ Retrieves the :term:`OWS` exception that corresponds to the :term:`XML` status. Expects the following :term:`XML` status response structure (``ows:Exception`` block can be at any level): .. code-block:: xml <ows:Exception exceptionCode="NoApplicableCode" locator="None"> <ows:ExceptionText>Error message about the cause of the exception.</ows:ExceptionText> </ows:Exception> :param xml: XML tree object with exception details. :return: Matched :class:`owsexceptions.OWSException`. If no matching exception code is found within available exception classes, defaults to generic ``OWSException``. If any parsing error occurs, returns nothing. """ try: if isinstance(xml, (str, bytes)): xml = xml_util.fromstring(bytes2str(xml)) if not isinstance(xml, xml_util.XML): return None # Exception blocks can be with or without 'ows' prefix depending on which default namespace was defined ows_exc_xml = xml.find( "Exception", namespaces=xml.nsmap) # type: Optional[xml_util.XML] if ows_exc_xml is None: ows_exc_xml = xml.xpath( "//ows:Exception", namespaces=xml.nsmap)[0] # type: Optional[xml_util.XML] ows_exc_txt = ows_exc_xml.find( "ExceptionText", namespaces=xml.nsmap) # type: Optional[xml_util.XML] if ows_exc_txt is None: ows_exc_txt = ows_exc_xml.xpath("//ows:ExceptionText", namespaces=xml.nsmap)[0] ows_exc_msg = ows_exc_txt.text ows_exc_loc = ows_exc_xml.attrib.get( "locator") or ows_exc_xml.attrib.get( "locater") # some WPS have typo ows_exc_code = ows_exc_xml.attrib["exceptionCode"] for ows_exc_name in dir(owsexceptions): ows_exc_cls = getattr(owsexceptions, ows_exc_name) if (inspect.isclass(ows_exc_cls) and issubclass(ows_exc_cls, owsexceptions.OWSException) and ows_exc_cls is not owsexceptions.OWSException and ows_exc_code == ows_exc_cls.code): return ows_exc_cls(ows_exc_msg, code=ows_exc_code, locator=ows_exc_loc) return owsexceptions.OWSException(ows_exc_msg, code=ows_exc_code, locator=ows_exc_loc) except Exception as exc: LOGGER.error("Failed mapping of OWS Exception from error codes.", exc_info=exc) return None
def get_template_url(self): resp = request_extra("get", self.osdd_url, params=self.params, settings=self.settings) resp.raise_for_status() data = xml_util.fromstring(resp.content) xpath = "//*[local-name() = 'Url'][@rel='results']" url = data.xpath(xpath)[0] # type: xml_util.XML return url.attrib["template"]
def test_xml_strip_ns(): wps_xml = """ <wps100:Execute xmlns:wps100="http://www.opengis.net/wps/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" service="WPS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_request.xsd"/>""" doc = xml_util.fromstring(wps_xml) assert doc.tag == "{http://www.opengis.net/wps/1.0.0}Execute" xml_strip_ns(doc) assert doc.tag == "Execute"
def _fetch_datatsets_from_alternates_links(self, alternate_links): # Try loading from atom alternate link for link in alternate_links: if link["type"] == "application/atom+xml": resp = request_extra("get", link["href"], settings=self.settings) resp.raise_for_status() data = xml_util.fromstring(resp.content) xpath = "//*[local-name() = 'entry']/*[local-name() = 'link']" links = data.xpath(xpath) # type: List[xml_util.XML] return [link.attrib for link in links] return []
def test_getcaps_filtered_processes_by_visibility(self): resp = self.app.get( self.make_url("service=wps&request=getcapabilities")) assert resp.status_code == 200 assert resp.content_type in CONTENT_TYPE_ANY_XML resp.mustcontain("<wps:ProcessOfferings>") root = xml_util.fromstring(str2bytes( resp.text)) # test response has no 'content' process_offerings = list( filter(lambda e: "ProcessOfferings" in e.tag, root.iter(xml_util.Element))) assert len(process_offerings) == 1 processes = [p for p in process_offerings[0]] ids = [ pi.text for pi in [ list(filter(lambda e: e.tag.endswith("Identifier"), p))[0] for p in processes ] ] assert self.process_private.identifier not in ids assert self.process_public.identifier in ids
def check_wps_status( location=None, # type: Optional[str] response=None, # type: Optional[xml_util.XML] sleep_secs=2, # type: int verify=True, # type: bool settings=None, # type: Optional[AnySettingsContainer] ): # type: (...) -> WPSExecution """ Run :func:`owslib.wps.WPSExecution.checkStatus` with additional exception handling. :param location: job URL or file path where to look for job status. :param response: WPS response document of job status. :param sleep_secs: number of seconds to sleep before returning control to the caller. :param verify: flag to enable SSL verification. :param settings: application settings to retrieve any additional request parameters as applicable. :returns: OWSLib.wps.WPSExecution object. """ def _retry_file(): # type: () -> str LOGGER.warning( "Failed retrieving WPS status-location, attempting with local file." ) out_path = get_wps_local_status_location(location, settings) if not out_path: raise HTTPNotFound( f"Could not find file resource from [{location}].") LOGGER.info("Resolved WPS status-location using local file reference.") with open(out_path, mode="r", encoding="utf-8") as f: return f.read() execution = WPSExecution() if response: LOGGER.debug("Retrieving WPS status from XML response document...") xml_data = response elif location: xml_resp = HTTPNotFound() xml_data = None try: LOGGER.debug( "Attempt to retrieve WPS status-location from URL [%s]...", location) xml_resp = request_extra("get", location, verify=verify, settings=settings) xml_data = xml_resp.content except Exception as ex: LOGGER.debug( "Got exception during get status: [%r]. Will retry with local reference.", ex) if xml_resp.status_code != HTTPOk.code: LOGGER.debug( "WPS XML status not found: [%r]. Retrying with local reference.", xml_data) xml_data = _retry_file() else: raise Exception( "Missing status-location URL/file reference or response with XML object." ) if isinstance(xml_data, str): xml_data = xml_data.encode("utf8", errors="ignore") execution.checkStatus(response=xml_data, sleepSecs=sleep_secs) if execution.response is None: raise Exception("Missing response, cannot check status.") if not isinstance(execution.response, xml_util.XML): execution.response = xml_util.fromstring(execution.response) return execution
def test_get_exception_from_xml_status_unknown_other_exception(): xml = xml_util.fromstring("<FakeException>Not valid</FakeException>") exc = get_exception_from_xml_status(xml) assert exc is None
def wps_execute(self, version, accept): wps_url = get_wps_url(self.settings) if version == "1.0.0": test_content = "Test file in Docker - WPS KVP" wps_method = "GET" elif version == "2.0.0": test_content = "Test file in Docker - WPS XML" wps_method = "POST" else: raise ValueError("Invalid WPS version: {}".format(version)) test_content += " {} request - Accept {}".format(wps_method, accept.split("/")[-1].upper()) with contextlib.ExitStack() as stack_exec: # setup dir_name = tempfile.gettempdir() tmp_file = stack_exec.enter_context(tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt")) tmp_file.write(test_content) tmp_file.seek(0) for mock_exec in mocked_execute_process(): stack_exec.enter_context(mock_exec) # execute if version == "1.0.0": wps_inputs = ["file={}@mimeType={}".format(tmp_file.name, CONTENT_TYPE_TEXT_PLAIN)] wps_params = { "service": "WPS", "request": "Execute", "version": version, "identifier": self.process_id, "DataInputs": wps_inputs, } wps_headers = {"Accept": accept} wps_data = None else: wps_inputs = [("file", ComplexDataInput(tmp_file.name, mimeType=CONTENT_TYPE_TEXT_PLAIN))] wps_outputs = [(self.out_key, True)] # as reference wps_exec = WPSExecution(version=version, url=wps_url) wps_req = wps_exec.buildRequest(self.process_id, wps_inputs, wps_outputs) wps_data = xml_util.tostring(wps_req) wps_headers = {"Accept": accept, "Content-Type": CONTENT_TYPE_APP_XML} wps_params = None resp = mocked_sub_requests(self.app, wps_method, wps_url, params=wps_params, data=wps_data, headers=wps_headers, only_local=True) assert resp.status_code in [200, 201], ( "Failed with: [{}]\nTest: [{}]\nReason:\n{}".format(resp.status_code, test_content, resp.text) ) # parse response status if accept == CONTENT_TYPE_APP_XML: assert resp.content_type in CONTENT_TYPE_ANY_XML, test_content xml_body = xml_util.fromstring(str2bytes(resp.text)) status_url = xml_body.get("statusLocation") job_id = status_url.split("/")[-1].split(".")[0] elif accept == CONTENT_TYPE_APP_JSON: assert resp.content_type == CONTENT_TYPE_APP_JSON, test_content status_url = resp.json["location"] job_id = resp.json["jobID"] assert status_url assert job_id if accept == CONTENT_TYPE_APP_XML: wps_out_url = self.settings["weaver.wps_output_url"] weaver_url = self.settings["weaver.url"] assert status_url == f"{wps_out_url}/{job_id}.xml", "Status URL should be XML file for WPS-1 request" # remap to employ JSON monitor method (could be done with XML parsing otherwise) status_url = f"{weaver_url}/jobs/{job_id}" # job monitoring results = self.monitor_job(status_url) outputs = self.get_outputs(status_url) # validate XML status is updated accordingly wps_xml_status = os.path.join(self.settings["weaver.wps_output_dir"], job_id + ".xml") assert os.path.isfile(wps_xml_status) with open(wps_xml_status, "r") as status_file: assert "ProcessSucceeded" in status_file.read() self.validate_outputs(job_id, results, outputs, test_content)