populator+LazyTool: default missing tool version + graceful materialise fallback

Two boot-time and panel-render fixes that the integration suite
surfaced once cold-start indexing covered every conf-discovered tool:

1. **Version defaulting in ``build_index_entry_from_source``.** Calling
   ``tool_source.parse_version()`` directly returns ``None`` for tools
   without an explicit ``<tool version="...">`` (test fixtures like
   ``sam_to_unsorted_bam.xml`` and ``implicit_conversion.xml``). The
   eager pipeline routes through
   ``galaxy.tool_util.parser.util.parse_tool_version_with_defaults``
   which falls back to ``"1.0.0"`` on profiles < 16.04. Use the same
   helper at index-build time so ``LazyTool.version`` is never
   ``None`` and ``ToolLineage.register_version`` can parse it.

2. **Materialise fallback in ``LazyTool.to_dict(link_details=True)``.**
   ``to_panel_view`` materialises every panel-visible tool to call
   ``to_dict(link_details=True)``. Some tools have XML the parameter
   factory chokes on at materialise time (``upload1``'s
   ``upload_dataset`` input_type, ``filter_data_table.xml``'s
   ``column="value"`` dynamic-options filter). The eager toolbox
   catches these in ``_load_tool_tag_set`` at boot and drops the tool
   from ``_tools_by_id``; the lazy path postpones the failure to first
   ``to_dict``, where it now logs a WARNING and falls back to the
   entry-only fast-path dict so the panel render still completes.

15/16 ``test_tool_source_storage.py`` integration tests pass; the
remaining failure is the noted pre-existing
``test_run_specific_version_executes_that_version`` flake.
This commit is contained in:
mvdbeek
2026-07-28 17:27:18 +02:00
parent 6e5fbde14e
commit e115ebf226
2 changed files with 26 additions and 6 deletions
+12 -1
View File
@@ -60,6 +60,7 @@ from galaxy.tool_source_store.index import (
ToolIndexEntry,
)
from galaxy.tool_util.parser import get_tool_source
from galaxy.tool_util.parser.util import parse_tool_version_with_defaults
from galaxy.tool_util.toolbox.parser import get_toolbox_parser
log = logging.getLogger(__name__)
@@ -435,10 +436,20 @@ def build_index_entry_from_source(
except Exception:
pass
# Honour the same version-default rules as ``Tool.__init__``: empty
# ``version`` on a pre-16.04-profile tool becomes "1.0.0"; on newer
# profiles it raises. Without this, ``ToolLineage.register_version``
# crashes on ``Version(None)`` during the eager walk.
try:
version = parse_tool_version_with_defaults(tool_id, tool_source)
except Exception as e:
log.warning("parse_tool_version_with_defaults raised for %s: %s", tool_id, e)
version = tool_source.parse_version() or "0"
return ToolIndexEntry(
id=tool_id,
uuid=uuid_val,
version=tool_source.parse_version(),
version=version,
name=tool_source.parse_name() or "",
description=tool_source.parse_description() or "",
panel_section_id=discovered.section_id,
+14 -5
View File
@@ -249,13 +249,22 @@ class LazyTool:
def to_dict(self, trans=None, link_details: bool = False, tool_help: bool = False, **kw) -> dict[str, Any]:
"""API serialisation. ``link_details=False`` stays on the entry fast path.
``link_details=True`` (used by ``/api/tools/<id>/build``) needs the full
:meth:`Tool.to_dict` payload (parameters, citations, ...). That triggers
materialise. The default and EDAM panel listings call with
``link_details=False`` and stay cheap.
``link_details=True`` (used by ``/api/tools/<id>/build`` and the panel
view walk) needs the full :meth:`Tool.to_dict` payload (parameters,
citations, ...). That triggers materialise. If materialise raises
(tool XML with options/filters the parser can't handle —
``upload_dataset``, ``column="value"`` against an unresolvable
column-name spec, …), fall back to the entry-only fast-path dict so
the panel listing still renders. The eager toolbox catches the same
errors at boot in ``_load_tool_tag_set`` and drops the tool from
``_tools_by_id``; we surface them lazily and degrade gracefully
instead.
"""
if link_details:
return self._materialize().to_dict(trans, link_details=True, tool_help=tool_help, **kw)
try:
return self._materialize().to_dict(trans, link_details=True, tool_help=tool_help, **kw)
except Exception as e:
log.warning("LazyTool.to_dict: materialise failed for %s, falling back to entry: %s", self.id, e)
entry = self._entry
return {
"id": self.id,