From a292072e6830ea6e239ebb69ab3574d81236618b Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 20 Mar 2024 14:48:15 -0700 Subject: [PATCH 1/9] Add the `registry-search` command --- datalad_registry_client/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datalad_registry_client/__init__.py b/datalad_registry_client/__init__.py index 3e2be074..a5d8bda9 100644 --- a/datalad_registry_client/__init__.py +++ b/datalad_registry_client/__init__.py @@ -19,6 +19,12 @@ "registry-get-urls", "registry_get_urls", ), + ( + "datalad_registry_client.search", + "RegistrySearch", + "registry-search", + "registry_search", + ), ], ) From e325bb30b902818b38347d82b985a91949024ac2 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 20 Mar 2024 14:51:09 -0700 Subject: [PATCH 2/9] Start implementation of `registry-search` command with a template --- datalad_registry_client/search.py | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 datalad_registry_client/search.py diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py new file mode 100644 index 00000000..09b0d8a6 --- /dev/null +++ b/datalad_registry_client/search.py @@ -0,0 +1,74 @@ +"""DataLad demo command""" + +__docformat__ = "restructuredtext" + +import logging +from os.path import abspath, curdir + +from datalad.distribution.dataset import datasetmethod +from datalad.interface.base import Interface, build_doc, eval_results +from datalad.interface.results import get_status_dict +from datalad.support.constraints import EnsureChoice +from datalad.support.param import Parameter + +lgr = logging.getLogger("datalad.helloworld.hello_cmd") + + +# decoration auto-generates standard help +@build_doc +# all commands must be derived from Interface +class HelloWorld(Interface): + # first docstring line is used a short description in the cmdline help + # the rest is put in the verbose help and manpage + """Short description of the command + + Long description of arbitrary volume. + """ + + # parameters of the command, must be exhaustive + _params_ = dict( + # name of the parameter, must match argument name + language=Parameter( + # cmdline argument definitions, incl aliases + args=("-l", "--language"), + # documentation + doc="""language to say "hello" in""", + # type checkers, constraint definition is automatically + # added to the docstring + constraints=EnsureChoice("en", "de"), + ), + ) + + @staticmethod + # decorator binds the command to the Dataset class as a method + @datasetmethod(name="hello_cmd") + # generic handling of command results (logging, rendering, filtering, ...) + @eval_results + # signature must match parameter list above + # additional generic arguments are added by decorators + def __call__(language="en"): + if language == "en": + msg = "Hello!" + elif language == "de": + msg = "Tachchen!" + else: + msg = ("unknown language: '%s'", language) + + # commands should be implemented as generators and should + # report any results by yielding status dictionaries + yield get_status_dict( + # an action label must be defined, the command name make a good + # default + action="demo", + # most results will be about something associated with a dataset + # (component), reported paths MUST be absolute + path=abspath(curdir), + # status labels are used to identify how a result will be reported + # and can be used for filtering + status="ok" if language in ("en", "de") else "error", + # arbitrary result message, can be a str or tuple. in the latter + # case string expansion with arguments is delayed until the + # message actually needs to be rendered (analog to exception + # messages) + message=msg, + ) From aaebf21a29c1e5f1aaeb6455b7d8edf70e6df836 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 20 Mar 2024 15:03:24 -0700 Subject: [PATCH 3/9] Adjust logger name from template --- datalad_registry_client/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index 09b0d8a6..1a0c23c0 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -11,7 +11,7 @@ from datalad.support.constraints import EnsureChoice from datalad.support.param import Parameter -lgr = logging.getLogger("datalad.helloworld.hello_cmd") +lgr = logging.getLogger("datalad.registry.search") # decoration auto-generates standard help From fc03dc7e5971d7faf1588d08318d336212b11e52 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 20 Mar 2024 15:06:57 -0700 Subject: [PATCH 4/9] Adjust command class name from template --- datalad_registry_client/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index 1a0c23c0..54d7b2cf 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -17,7 +17,7 @@ # decoration auto-generates standard help @build_doc # all commands must be derived from Interface -class HelloWorld(Interface): +class RegistrySearch(Interface): # first docstring line is used a short description in the cmdline help # the rest is put in the verbose help and manpage """Short description of the command From d8cc70ff1b08069a3833d03704cf53feb85322c1 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 20 Mar 2024 15:13:08 -0700 Subject: [PATCH 5/9] Adjust name argument of `datasetmethod` in template --- datalad_registry_client/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index 54d7b2cf..dd9c2b2b 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -41,7 +41,7 @@ class RegistrySearch(Interface): @staticmethod # decorator binds the command to the Dataset class as a method - @datasetmethod(name="hello_cmd") + @datasetmethod(name="registry_search") # generic handling of command results (logging, rendering, filtering, ...) @eval_results # signature must match parameter list above From dccb540d9b29bc4bf741fddd2fa22343ddc5fe30 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Thu, 21 Mar 2024 12:36:29 -0700 Subject: [PATCH 6/9] Add template `custom_result_renderer` method For customized output format --- datalad_registry_client/search.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index dd9c2b2b..368def9e 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -72,3 +72,9 @@ def __call__(language="en"): # messages) message=msg, ) + + @staticmethod + def custom_result_renderer(res, **_kwargs): + from datalad.ui import ui + + ui.message(res["message"]) From 8e524280abc9eb7c789e1c669dbed49899478f0b Mon Sep 17 00:00:00 2001 From: Isaac To Date: Thu, 21 Mar 2024 12:43:25 -0700 Subject: [PATCH 7/9] Add template code for Parameters --- datalad_registry_client/search.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index 368def9e..c3d82d22 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -8,9 +8,11 @@ from datalad.distribution.dataset import datasetmethod from datalad.interface.base import Interface, build_doc, eval_results from datalad.interface.results import get_status_dict -from datalad.support.constraints import EnsureChoice +from datalad.support.constraints import EnsureNone, EnsureStr from datalad.support.param import Parameter +from . import DEFAULT_BASE_ENDPOINT + lgr = logging.getLogger("datalad.registry.search") @@ -28,14 +30,24 @@ class RegistrySearch(Interface): # parameters of the command, must be exhaustive _params_ = dict( # name of the parameter, must match argument name - language=Parameter( + search_str=Parameter( # cmdline argument definitions, incl aliases - args=("-l", "--language"), + args=("-s", "--search-str"), # documentation - doc="""language to say "hello" in""", + doc="""The search string used to perform a search identical to the one + offered by the Web UI. Please consult the Web UI for the expected syntax of + this search string by clicking on the "Show Search Query Syntax" button.""", # type checkers, constraint definition is automatically # added to the docstring - constraints=EnsureChoice("en", "de"), + constraints=EnsureStr(), + required=True, + ), + base_endpoint=Parameter( + args=("-e", "--base-endpoint"), + doc=f"""The base API endpoint of the DataLad Registry instance to interact + with. This defaults to the datalad_registry.base_endpoint option if set, + or {DEFAULT_BASE_ENDPOINT} otherwise.""", + constraints=EnsureStr() | EnsureNone(), ), ) From d0dea9d29b13f8103a699d42732d3358481827c2 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Thu, 21 Mar 2024 15:49:51 -0700 Subject: [PATCH 8/9] Adjust template code to output provided arguments --- datalad_registry_client/search.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index c3d82d22..63f9b696 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -3,8 +3,9 @@ __docformat__ = "restructuredtext" import logging -from os.path import abspath, curdir +from typing import Optional +from datalad import cfg from datalad.distribution.dataset import datasetmethod from datalad.interface.base import Interface, build_doc, eval_results from datalad.interface.results import get_status_dict @@ -58,35 +59,35 @@ class RegistrySearch(Interface): @eval_results # signature must match parameter list above # additional generic arguments are added by decorators - def __call__(language="en"): - if language == "en": - msg = "Hello!" - elif language == "de": - msg = "Tachchen!" - else: - msg = ("unknown language: '%s'", language) + def __call__(search_str: str, base_endpoint: Optional[str] = None): + # Set `base_endpoint` to the default if it is not provided. + if base_endpoint is None: + base_endpoint = cfg.get( + "datalad_registry.base_endpoint", DEFAULT_BASE_ENDPOINT + ) # commands should be implemented as generators and should # report any results by yielding status dictionaries yield get_status_dict( # an action label must be defined, the command name make a good # default - action="demo", - # most results will be about something associated with a dataset - # (component), reported paths MUST be absolute - path=abspath(curdir), + action="registry-search", # status labels are used to identify how a result will be reported # and can be used for filtering - status="ok" if language in ("en", "de") else "error", + status="ok", # arbitrary result message, can be a str or tuple. in the latter # case string expansion with arguments is delayed until the # message actually needs to be rendered (analog to exception # messages) - message=msg, + message=( + "search_str: '%s'; base_endpoint: '%s'", + search_str, + base_endpoint, + ), ) @staticmethod def custom_result_renderer(res, **_kwargs): from datalad.ui import ui - ui.message(res["message"]) + ui.message(res["message"][0] % res["message"][1:]) From b4a3fd4af887849cf04fc92b64a736b7fb0f4b60 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Fri, 22 Mar 2024 17:38:55 -0700 Subject: [PATCH 9/9] Unbind `RegistrySearch.__call__()` as a `Dataset` method Don't really see any need calling this function from `Dataset` class --- datalad_registry_client/search.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/datalad_registry_client/search.py b/datalad_registry_client/search.py index 63f9b696..010ba90a 100644 --- a/datalad_registry_client/search.py +++ b/datalad_registry_client/search.py @@ -6,7 +6,6 @@ from typing import Optional from datalad import cfg -from datalad.distribution.dataset import datasetmethod from datalad.interface.base import Interface, build_doc, eval_results from datalad.interface.results import get_status_dict from datalad.support.constraints import EnsureNone, EnsureStr @@ -53,8 +52,6 @@ class RegistrySearch(Interface): ) @staticmethod - # decorator binds the command to the Dataset class as a method - @datasetmethod(name="registry_search") # generic handling of command results (logging, rendering, filtering, ...) @eval_results # signature must match parameter list above