From 3c60637d6c5e5fd03f82617db66c2702921ac412 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Sat, 2 Nov 2024 13:28:26 +0100 Subject: [PATCH] Add support for URI protocols in visualization datatype checks --- .../plugins/visualizations/visualization.dtd | 15 +++++---- .../visualization/plugins/config_parser.py | 17 ++++++---- lib/galaxy/visualization/plugins/registry.py | 33 ++++++++++++++++--- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/config/plugins/visualizations/visualization.dtd b/config/plugins/visualizations/visualization.dtd index ed5cd7ced5b2..a4640e9ca675 100644 --- a/config/plugins/visualizations/visualization.dtd +++ b/config/plugins/visualizations/visualization.dtd @@ -54,17 +54,18 @@ if result_type is 'datatype' the registry will assume the text is a datatype class name and parse it into the proper class before the test (often 'isinstance') is run. DEFAULT: no parsing (result should be a string) - allow_deferred: used in conjunction with type='isinstance' and test_attr='datatype'. If set to true, - the registry will allow the test to pass if the target is deferred (currently only HDAs can be deferred). + allow_uri_if_protocol: used in conjunction with type='isinstance' and test_attr='datatype'. Let you define + a list of protocols or schemes (e.g. 's3,https') that, in the case of a deferred target (e.g. currently only HDAs), + the registry will allow the test to pass if the the source URI has a scheme in the list. This is useful for visualizations that can work directly with URIs. - DEFAULT: false + DEFAULT: [] --> diff --git a/lib/galaxy/visualization/plugins/config_parser.py b/lib/galaxy/visualization/plugins/config_parser.py index 199a4d9718f7..ffefbe2dfb8a 100644 --- a/lib/galaxy/visualization/plugins/config_parser.py +++ b/lib/galaxy/visualization/plugins/config_parser.py @@ -6,7 +6,10 @@ ) import galaxy.model -from galaxy.util import asbool +from galaxy.util import ( + asbool, + listify, +) from galaxy.util.xml_macros import load log = logging.getLogger(__name__) @@ -301,13 +304,15 @@ def parse_tests(self, xml_tree_list): # result type should tell the registry how to convert the result before the test test_result_type = test_elem.get("result_type", "string") - # allow_deferred indicates that the visualization can work with deferred data_sources - # Can only be used with isinstance tests. By default, no visualization can work with deferred data_sources. - allow_deferred = False + # allow_uri_if_protocol indicates that the visualization can work with deferred data_sources which source URI + # matches any of the given protocols in this list. This is useful for visualizations that can work with URIs. + # Can only be used with isinstance tests. By default, an empty list means that the visualization doesn't support + # deferred data_sources. + allow_uri_if_protocol = [] # test functions should be sent an object to test, and the parsed result expected from the test if test_type == "isinstance": - allow_deferred = asbool(test_elem.get("allow_deferred", False)) + allow_uri_if_protocol = listify(test_elem.get("allow_uri_if_protocol")) # is test_attr attribute an instance of result # TODO: wish we could take this further but it would mean passing in the datatypes_registry @@ -340,7 +345,7 @@ def test_fn(o, result, getter=getter): "result": test_result, "result_type": test_result_type, "fn": test_fn, - "allow_deferred": allow_deferred, + "allow_uri_if_protocol": allow_uri_if_protocol, } ) diff --git a/lib/galaxy/visualization/plugins/registry.py b/lib/galaxy/visualization/plugins/registry.py index 52b7f657146d..2cb480fd15fd 100644 --- a/lib/galaxy/visualization/plugins/registry.py +++ b/lib/galaxy/visualization/plugins/registry.py @@ -8,6 +8,10 @@ import logging import os import weakref +from typing import ( + List, + Optional, +) from galaxy.exceptions import ObjectNotFound from galaxy.util import ( @@ -263,13 +267,12 @@ def is_object_applicable(self, trans, target_object, data_source_tests): it can be applied to the target_object. """ # log.debug( 'is_object_applicable( self, trans, %s, %s )', target_object, data_source_tests ) - is_deferred_target = self._is_deferred(target_object) for test in data_source_tests: test_type = test["type"] result_type = test["result_type"] test_result = test["result"] test_fn = test["fn"] - allow_deferred = test.get("allow_deferred", False) + supported_protocols = test.get("allow_uri_if_protocol", []) # log.debug( '%s %s: %s, %s, %s, %s', str( target_object ), 'is_object_applicable', # test_type, result_type, test_result, test_fn ) @@ -288,11 +291,33 @@ def is_object_applicable(self, trans, target_object, data_source_tests): continue # NOTE: tests are OR'd, if any test passes - the visualization can be applied - if test_fn(target_object, test_result) and (not is_deferred_target or allow_deferred): + if test_fn(target_object, test_result) and self._check_uri_support(target_object, supported_protocols): # log.debug( '\t test passed' ) return True return False - def _is_deferred(self, target_object): + def _is_deferred(self, target_object) -> bool: + """Whether the target object is a deferred object.""" return getattr(target_object, "state", None) == "deferred" + + def _deferred_source_uri(self, target_object) -> Optional[str]: + """Get the source uri from a deferred object.""" + sources = getattr(target_object, "sources", None) + if sources and sources[0]: + return sources[0].source_uri + return None + + def _check_uri_support(self, target_object, supported_protocols: List[str]) -> bool: + """Test if the target object is deferred and has a supported protocol.""" + if not self._is_deferred(target_object): + return True # not deferred, so no uri to check + + if not supported_protocols: + return False # no protocols defined, means no support for deferred objects + + deferred_source_uri = self._deferred_source_uri(target_object) + if deferred_source_uri: + protocol = deferred_source_uri.split("://")[0] + return protocol in supported_protocols + return False