From 455d6e87e8a00386192cc44cb1f43477d4a5bd79 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 7 Jul 2026 09:22:53 +0200 Subject: [PATCH] Share tool_path resolution between the toolbox and tool discovery AbstractToolBox.__resolve_tool_path becomes the module-level resolve_tool_path() in tool_util.toolbox.base and discover uses it, dropping its own copy. This also fixes discover's fallback for confs without a tool_path attribute: it guessed /tools, while the toolbox has always used config.tool_path; discover_tools_from_config now takes that default (discover_tools passes config.tool_path). Also hoists the MODEL_TOOLS_PATH import to module level - the parent galaxy.tools package is imported with the subpackage anyway, so the local import bought nothing. Claude-Session: https://claude.ai/code/session_018L7ZmCv2ubKA3JNeSL8Pkr --- lib/galaxy/tool_util/toolbox/base.py | 30 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/galaxy/tool_util/toolbox/base.py b/lib/galaxy/tool_util/toolbox/base.py index 8a4dba9f119..b464763ccfa 100644 --- a/lib/galaxy/tool_util/toolbox/base.py +++ b/lib/galaxy/tool_util/toolbox/base.py @@ -171,6 +171,23 @@ class ToolLoadConfigurationConflict(Exception): pass +def resolve_tool_path(tool_path: str | None, config_filename: str, default_tool_path: "StrPath | None" = None) -> str: + """Resolve a tool conf's ``tool_path`` attribute to the directory its tool + files are relative to. + + Expands the ``${tool_conf_dir}`` template; falls back to + ``default_tool_path`` (the toolbox's ``tool_root_dir``, i.e. + ``config.tool_path``) when the conf doesn't set one. + """ + if not tool_path: + # Default to backward compatible config setting. + return str(default_tool_path) if default_tool_path else "" + # Allow use of ${tool_conf_dir} in toolbox config files. + tool_conf_dir = os.path.dirname(config_filename) + tool_path_vars = {"tool_conf_dir": tool_conf_dir} + return string.Template(tool_path).safe_substitute(tool_path_vars) + + class AbstractToolBox(ManagesIntegratedToolPanelMixin): """ Abstract container for managing a ToolPanel - containing tools and @@ -389,7 +406,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin): config_elems = [] tool_conf_type = "shed tool" if parsing_shed_tool_conf else "tool" log.debug("Tool path for %s configuration %s is %s", tool_conf_type, config_filename, tool_path) - tool_path = self.__resolve_tool_path(tool_path, config_filename) + tool_path = resolve_tool_path(tool_path, config_filename, self._tool_root_dir) # Only load the panel_dict under certain conditions. load_panel_dict = not self._integrated_tool_panel_config_has_contents for item in tool_conf_source.parse_items(): @@ -564,17 +581,6 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin): tool_id = tool.id return self._tool_panel.get_section_for_tool_id(tool_id) - def __resolve_tool_path(self, tool_path, config_filename): - if not tool_path: - # Default to backward compatible config setting. - tool_path = self._tool_root_dir - else: - # Allow use of __tool_conf_dir__ in toolbox config files. - tool_conf_dir = os.path.dirname(config_filename) - tool_path_vars = {"tool_conf_dir": tool_conf_dir} - tool_path = string.Template(tool_path).safe_substitute(tool_path_vars) - return tool_path - def add_tool_to_tool_panel_view(self, tool, view_panel_component): self.__add_tool_to_tool_panel(tool, view_panel_component)