diff --git a/birdy/client/base.py b/birdy/client/base.py index 9e83422..9605c8f 100644 --- a/birdy/client/base.py +++ b/birdy/client/base.py @@ -284,7 +284,7 @@ def _method_factory(self, pid: str) -> Callable: func_builder = FunctionBuilder( name=sanitize(pid), - doc=utils.build_process_doc(process), + doc=utils.build_process_doc(self._wps, process), args=["self"] + input_names, defaults=defaults, body=body, diff --git a/birdy/client/utils.py b/birdy/client/utils.py index b98a93d..ff3f547 100644 --- a/birdy/client/utils.py +++ b/birdy/client/utils.py @@ -121,12 +121,14 @@ def build_wps_client_doc( return "\n".join(doc) -def build_process_doc(process: Process) -> str: +def build_process_doc(wps: WebProcessingService, process: Process) -> str: """ Create docstring from process metadata. Parameters ---------- + wps : owslib.wps.WebProcessingService + A WPS service. process : owslib.wps.Process A WPS process. @@ -136,13 +138,16 @@ def build_process_doc(process: Process) -> str: The formatted docstring for this process. """ doc = [process.abstract or "", ""] + _process = wps.describeprocess(process.identifier) # Inputs if process.dataInputs: doc.append("Parameters") doc.append("----------") for i in process.dataInputs: - doc.append(f"{sanitize(i.identifier)} : {format_type(i)}") + doc.append( + f"{sanitize(i.identifier)} : {format_allowed_values(_process, i.identifier)}{format_type(i)}" + ) doc.append(f" {i.abstract or i.title}") # if i.metadata: # doc[-1] += " ({})".format(', '.join(['`{} <{}>`_'.format(m.title, m.href) for m in i.metadata])) @@ -160,6 +165,63 @@ def build_process_doc(process: Process) -> str: return "\n".join(doc) +def format_allowed_values(process: Process, input_id: str) -> str: + """ + Parse AllowedValues manually from raw XML for the given Process and Input. + + Parameters + ---------- + process : owslib.wps.Process + A WPS process. + input_id : str + An Input identifier. + + Returns + ------- + str + The AllowedValues for the given Input. + """ + nmax = 10 + doc = "" + ns = { + "wps": "http://www.opengis.net/wps/1.0.0", + "ows": "http://www.opengis.net/ows/1.1", + } + xml_tree = process._processDescription + for input_elem in xml_tree.xpath("DataInputs/Input"): + if input_elem.find("ows:Identifier", namespaces=ns).text == input_id: + if input_elem.find(".//ows:AllowedValues", namespaces=ns) is not None: + if input_elem.find(".//ows:Range", namespaces=ns) is not None: + ranges = process.xpath(".//ows:Range", namespaces=ns) + for r in ranges: + min_val = r.find(".//ows:MinimumValue", namespaces=ns).text + max_val = r.find(".//ows:MaximumValue", namespaces=ns).text + spacing = ( + r.find(".//ows:Spacing", namespaces=ns).text + if r.find(".//ows:Spacing", namespaces=ns) is not None + else 1 + ) + doc += ( + "{" + + f"'{min_val}'" + + "->" + + f"'{max_val}'" + + f" steps: '{spacing}'" + + "}" + ) + else: + values = input_elem.xpath(".//ows:Value", namespaces=ns) + allowed = [v.text for v in values] + av = ", ".join([f"'{i}'" for i in allowed[:nmax]]) + if len(allowed) > nmax: + av += ", ..." + doc += "{" + av + "}" + else: + doc += "{" + f"'{None}'" + "}" + break + return doc + + def format_type(obj: Any) -> str: """ Create docstring entry for input parameter from an OWSlib object. @@ -178,12 +240,6 @@ def format_type(obj: Any) -> str: doc = "" try: - if getattr(obj, "allowedValues", None): - av = ", ".join([f"'{i}'" for i in obj.allowedValues[:nmax]]) - if len(obj.allowedValues) > nmax: - av += ", ..." - doc += "{" + av + "}" - if getattr(obj, "dataType", None): doc += obj.dataType diff --git a/tests/test_client.py b/tests/test_client.py index 8c6e389..a2cbda6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -45,6 +45,14 @@ def test_wps_supported_languages(wps_offline): # noqa: D103 assert wps_offline.languages.supported == ["en-US", "fr-CA"] +def test_method_factory(wps_offline): + """Check the order of AllowedValues in the docstring of a WPSClient instance's method.""" + func_doc = wps_offline._method_factory(pid="inout").__doc__ + assert "{'1', '2', '3', '5', '7', '11'}" in func_doc + assert "{'rock', 'paper', 'scissor'}" in func_doc + assert "{'1'->'10' steps: '1'}{'100'->'200' steps: '10'}" in func_doc + + @pytest.mark.online def test_wps_with_language_arg(): # noqa: D103 wps = WPSClient(URL_EMU, language="fr-CA")