Repository: astropy/pyvo
Pull request #201 (2019-12-18 17:45:13)

Fix issue #200 - treat keywords str value as single keyword
This addresses a usability issue with regsearch() where if one mistakenly supplies a single `str` as the `keywords` value, too many results are returned.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index a956cf8..683bd78 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,8 @@
 
 - Handle description of None when describing a TAP service's tables. [#197]
 
+- Properly handle single string keywords value for regsearch(). [#201]
+
 1.0 (2019-09-20)
 ================
 
diff --git a/docs/registry/index.rst b/docs/registry/index.rst
index 97f0ac9..c3c0635 100644
--- a/docs/registry/index.rst
+++ b/docs/registry/index.rst
@@ -19,6 +19,10 @@ quasars.
 
 >>> services = regsearch(keywords=['quasar'])
 
+A single keyword can be specified as a single string instead of a list.
+
+>>> services = regsearch(keywords='quasar')
+
 Furthermore the search can be limited to a certain ``servicetype``, one of
 sia, ssa, scs, sla, tap.
 
diff --git a/pyvo/registry/regtap.py b/pyvo/registry/regtap.py
index 648ce5d..8a81faa 100644
--- a/pyvo/registry/regtap.py
+++ b/pyvo/registry/regtap.py
@@ -40,7 +40,7 @@ def search(keywords=None, servicetype=None, waveband=None, datamodel=None):
 
     Parameters
     ----------
-    keywords : list of str
+    keywords : str or list of str
        keyword terms to match to registry records.
        Use this parameter to find resources related to a
        particular topic.
@@ -87,6 +87,9 @@ def search(keywords=None, servicetype=None, waveband=None, datamodel=None):
     wheres = list()
     wheres.append("intf_role = 'std'")
 
+    if isinstance(keywords, str):
+        keywords = [keywords]
+
     if keywords:
         def _unions():
             for i, keyword in enumerate(keywords):


Test patch:
diff --git a/pyvo/registry/tests/test_regtap.py b/pyvo/registry/tests/test_regtap.py
index 5d5af7f..f90c0fc 100644
--- a/pyvo/registry/tests/test_regtap.py
+++ b/pyvo/registry/tests/test_regtap.py
@@ -56,6 +56,31 @@ def keywords_fixture(mocker):
         yield matcher
 
 
+@pytest.fixture()
+def single_keyword_fixture(mocker):
+    def keywordstest_callback(request, context):
+        data = dict(parse_qsl(request.body))
+        query = data['QUERY']
+
+        assert "WHERE isub0.res_subject ILIKE '%single%'" in query
+        assert "WHERE 1=ivo_hasword(ires0.res_description, 'single')" in query
+        assert "OR 1=ivo_hasword(ires0.res_title, 'single')" in query
+
+        assert "'ivo://ivoa.net/std/conesearch'" in query
+        assert "'ivo://ivoa.net/std/sia'" in query
+        assert "'ivo://ivoa.net/std/ssa'" in query
+        assert "'ivo://ivoa.net/std/slap'" in query
+        assert "'ivo://ivoa.net/std/tap'" in query
+
+        return get_pkg_data_contents('data/regtap.xml')
+
+    with mocker.register_uri(
+        'POST', 'http://dc.g-vo.org/tap/sync',
+        content=keywordstest_callback
+    ) as matcher:
+        yield matcher
+
+
 @pytest.fixture()
 def servicetype_fixture(mocker):
     def servicetypetest_callback(request, context):
@@ -119,6 +144,12 @@ def test_keywords():
     regsearch(keywords=['vizier', 'pulsar'])
 
 
+@pytest.mark.usefixtures('single_keyword_fixture', 'capabilities')
+def test_single_keyword():
+    regsearch(keywords=['single'])
+    regsearch(keywords='single')
+
+
 @pytest.mark.usefixtures('servicetype_fixture', 'capabilities')
 def test_servicetype():
     regsearch(servicetype='tap')