diff --git a/CHANGES.rst b/CHANGES.rst index 2ede9d46..dfb8216e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Enhancements and Fixes - Support VOTableFile in accessible_table and broadcast_samp [#745] +- Declaratively define new-style standard IDs in Servicetype constraint [#744] + - Fix raise_if_error() to use cached job phase instead of re-polling [#743] - Add ``pyvo.registry.get_RegTAP_service_url()`` to allow users to inspect the diff --git a/pyvo/registry/rtcons.py b/pyvo/registry/rtcons.py index ea45fbc2..32ac97d0 100644 --- a/pyvo/registry/rtcons.py +++ b/pyvo/registry/rtcons.py @@ -37,8 +37,6 @@ ("image", "sia"), ("sia", "sia"), ("sia1", "sia"), - # SIA2 is irregular - # funky scheme used by SIA2 without breaking everything else ("spectrum", "ssa"), ("ssap", "ssa"), ("ssa", "ssa"), @@ -50,6 +48,14 @@ ("tap", "tap"), ]} +# New-style standard IDs carry a #fragment with a minor version and cannot +# be matched with equality, so we match them with LIKE patterns instead. +NEW_STYLE_STDIDS = { + "sia2": ("ivo://ivoa.net/std/sia#query-2.%", "ivo://ivoa.net/std/sia#query-aux-2.%"), + "hats": ("ivo://ivoa.net/std/hats#hats-%", None), + "hips": ("ivo://ivoa.net/std/hips#hips-1.%", None), +} + class RegTAPFeatureMissing(dalq.DALQueryError): """ @@ -411,7 +417,7 @@ def __init__(self, *stds): match records that have any of them. """ self.stdids = set() - self.extra_fragments = [] + self.like_patterns = set() for std in stds: if std in ('image', 'spectrum'): @@ -425,38 +431,34 @@ def __init__(self, *stds): self.stdids.add(SERVICE_TYPE_MAP[std]) elif "://" in std: self.stdids.add(std) - elif std == 'sia2': - self.extra_fragments.append( - "standard_id like 'ivo://ivoa.net/std/sia#query-2.%'") - elif std == 'hats': - self.extra_fragments.append( - "standard_id like 'ivo://ivoa.net/std/hats#hats-%'") - elif std == 'hips': - self.extra_fragments.append( - "standard_id like 'ivo://ivoa.net/std/hips#hipslist-%'") + elif std in NEW_STYLE_STDIDS: + self.like_patterns.add(NEW_STYLE_STDIDS[std][0]) else: raise dalq.DALQueryError("Service type {} is neither a full" " standard URI nor one of the bespoke identifiers" - " {}, sia2, hats, hips".format(std, ", ".join(SERVICE_TYPE_MAP))) + " {}, {}".format(std, ", ".join(SERVICE_TYPE_MAP), + ", ".join(NEW_STYLE_STDIDS))) def clone(self): """returns a copy of this servicetype constraint. """ new_constraint = Servicetype() new_constraint.stdids = set(self.stdids) - new_constraint.extra_fragments = self.extra_fragments[:] + new_constraint.like_patterns = set(self.like_patterns) return new_constraint def get_search_condition(self, service): - # we sort the stdids to make it easy for tests (and it's + # we sort to make it easy for tests (and it's # virtually free for the small sets we have here). fragments = [] std_ids = ", ".join(make_sql_literal(s) for s in sorted(self.stdids)) if std_ids: fragments.append(f"standard_id IN ({std_ids})") + for pattern in sorted(self.like_patterns): + fragments.append(f"standard_id like '{pattern}'") - return " OR ".join(fragments + self.extra_fragments) + return " OR ".join(fragments) def include_auxiliary_services(self): """returns a Servicetype constraint that has self's @@ -465,11 +467,10 @@ def include_auxiliary_services(self): This is a convenience to maintain registry.search's signature. """ expanded = self.clone() - expanded.stdids |= { - std + '#aux' for std in expanded.stdids} - if "standard_id like 'ivo://ivoa.net/std/sia#query-2.%'" in expanded.extra_fragments: - expanded.extra_fragments.append( - "standard_id like 'ivo://ivoa.net/std/sia#query-aux-2.%'") + expanded.stdids |= {std + '#aux' for std in expanded.stdids} + for main_pattern, aux_pattern in NEW_STYLE_STDIDS.values(): + if main_pattern in expanded.like_patterns and aux_pattern: + expanded.like_patterns.add(aux_pattern) return expanded diff --git a/pyvo/registry/tests/test_rtcons.py b/pyvo/registry/tests/test_rtcons.py index 57057be5..605c69b4 100644 --- a/pyvo/registry/tests/test_rtcons.py +++ b/pyvo/registry/tests/test_rtcons.py @@ -185,6 +185,20 @@ def test_sia2_aux(self): " OR standard_id like 'ivo://ivoa.net/std/sia#query-2.%'" " OR standard_id like 'ivo://ivoa.net/std/sia#query-aux-2.%'")) + def test_hats(self): + assert (rtcons.Servicetype("hats").get_search_condition(FAKE_GAVO) + == "standard_id like 'ivo://ivoa.net/std/hats#hats-%'") + + def test_hats_aux(self): + # hats has no auxiliary service concept; aux expansion should not add a pattern + constraint = rtcons.Servicetype("hats").include_auxiliary_services() + assert (constraint.get_search_condition(FAKE_GAVO) + == "standard_id like 'ivo://ivoa.net/std/hats#hats-%'") + + def test_hips(self): + assert (rtcons.Servicetype("hips").get_search_condition(FAKE_GAVO) + == "standard_id like 'ivo://ivoa.net/std/hips#hips-1.%'") + def test_image_deprecated(self): with pytest.warns(AstropyDeprecationWarning): assert (rtcons.Servicetype("image").get_search_condition(FAKE_GAVO)