mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-21 05:45:37 +08:00
lazy toolbox: restore converter and membership filters in tests_summary
This commit is contained in:
@@ -80,6 +80,12 @@ class ToolIndexEntry(BaseModel):
|
||||
# ``interactive_tool``, ``data_source``, ...). Filter authors and
|
||||
# ``DataManagerTool.allow_user_access`` (admin-only) both branch on this.
|
||||
tool_type: str = "default"
|
||||
# True for datatype-converter discoveries (``CONVERTER_TOOL_CONF``). The
|
||||
# ``tool_type`` alone can't identify them — converters keep whatever type
|
||||
# their XML declares — so the populator stamps this flag from the
|
||||
# discovery source. ``tests_summary`` excludes them to mirror the eager
|
||||
# ``if not tool.is_datatype_converter`` filter.
|
||||
is_datatype_converter: bool = False
|
||||
# User-facing tags from ``<tool>`` config (distinct from ``labels``).
|
||||
# Surfaced for custom tool filters that bucket tools by tag.
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
@@ -396,8 +402,9 @@ class ToolIndex(BaseModel):
|
||||
summary: dict[str, dict[str, dict]] = {}
|
||||
for entry in self.entries.values():
|
||||
# Match the eager fallback in services.tools.ToolsService.get_tests_summary:
|
||||
# tools without tests are excluded entirely.
|
||||
if not entry.test_count:
|
||||
# tools without tests, and datatype converters, are excluded entirely
|
||||
# (the eager loop skips ``tool.is_datatype_converter``).
|
||||
if not entry.test_count or entry.is_datatype_converter:
|
||||
continue
|
||||
if entry.id not in summary:
|
||||
summary[entry.id] = {}
|
||||
|
||||
@@ -498,6 +498,7 @@ def build_index_entry_from_source(
|
||||
version=version,
|
||||
name=tool_source.parse_name() or "",
|
||||
description=tool_source.parse_description() or "",
|
||||
is_datatype_converter=discovered.tool_conf == CONVERTER_TOOL_CONF,
|
||||
icon=icon,
|
||||
xrefs=xrefs,
|
||||
is_workflow_compatible=is_workflow_compatible,
|
||||
|
||||
@@ -575,7 +575,13 @@ class ToolsService(ServiceBase):
|
||||
"""
|
||||
lazy_toolbox = self._get_lazy_toolbox(trans)
|
||||
if lazy_toolbox and lazy_toolbox.tool_index:
|
||||
return lazy_toolbox.tool_index.get_tests_summary()
|
||||
# The index is store-wide and ``get_tests_summary`` already drops
|
||||
# datatype converters; scope the result to tools this toolbox
|
||||
# actually holds so ids present in the store but never loaded here
|
||||
# don't leak into the summary (the eager loop below only ever sees
|
||||
# loaded tools).
|
||||
summary = lazy_toolbox.tool_index.get_tests_summary()
|
||||
return {tool_id: versions for tool_id, versions in summary.items() if lazy_toolbox.has_tool(tool_id)}
|
||||
|
||||
# Fallback to traditional toolbox iteration
|
||||
test_counts_by_tool: dict[str, dict] = {}
|
||||
|
||||
@@ -110,3 +110,11 @@ def test_add_entry_invalidates_derived_metadata(index_entry, tool_index):
|
||||
|
||||
assert [requirement["name"] for requirement in index.get_all_requirements()] == ["one", "two"]
|
||||
assert set(index.get_tests_summary()) == {"one", "two"}
|
||||
|
||||
|
||||
def test_tests_summary_excludes_datatype_converters(index_entry, tool_index):
|
||||
index = tool_index(
|
||||
index_entry("real_tool", version="1.0", test_count=2),
|
||||
index_entry("convert_fasta", version="1.0", test_count=1, is_datatype_converter=True),
|
||||
)
|
||||
assert set(index.get_tests_summary()) == {"real_tool"}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
"""Pin ``build_index_entry_from_source`` metadata capture."""
|
||||
|
||||
from datetime import (
|
||||
datetime,
|
||||
timezone,
|
||||
)
|
||||
|
||||
from galaxy.tool_util.parser import get_tool_source
|
||||
from galaxy.tools.source_store.discover import (
|
||||
CONVERTER_TOOL_CONF,
|
||||
DiscoveredTool,
|
||||
)
|
||||
from galaxy.tools.source_store.interface import StoredToolSource
|
||||
from galaxy.tools.source_store.populator import (
|
||||
build_index_entry_from_source,
|
||||
MAX_HELP_TEXT_CHARS,
|
||||
)
|
||||
|
||||
_TOOL_XML = """<tool id="help_tool" name="Help Tool" version="1.0">
|
||||
<command>echo</command>
|
||||
<inputs/>
|
||||
<outputs/>
|
||||
<help>This wraps the quaxifier subroutine.</help>
|
||||
</tool>
|
||||
"""
|
||||
|
||||
_TOOL_XML_NO_HELP = """<tool id="plain_tool" name="Plain Tool" version="1.0">
|
||||
<command>echo</command>
|
||||
<inputs/>
|
||||
<outputs/>
|
||||
</tool>
|
||||
"""
|
||||
|
||||
|
||||
def _stored(path, tool_source):
|
||||
return StoredToolSource(
|
||||
hash="deadbeef",
|
||||
tool_source_class=type(tool_source).__name__,
|
||||
raw_source=tool_source.to_string(),
|
||||
tool_id=tool_source.parse_id(),
|
||||
tool_version=tool_source.parse_version(),
|
||||
tool_dir=str(path.parent),
|
||||
source_path=str(path),
|
||||
stored_at=datetime.now(timezone.utc),
|
||||
metadata={},
|
||||
)
|
||||
|
||||
|
||||
def _build(tmp_path, xml, tool_conf="tool_conf.xml"):
|
||||
path = tmp_path / "tool.xml"
|
||||
path.write_text(xml)
|
||||
tool_source = get_tool_source(config_file=str(path))
|
||||
discovered = DiscoveredTool(path=str(path), tool_conf=tool_conf, tool_path=str(tmp_path))
|
||||
return build_index_entry_from_source(discovered, _stored(path, tool_source), tool_source)
|
||||
|
||||
|
||||
def test_entry_marks_datatype_converter(tmp_path):
|
||||
plain = _build(tmp_path, _TOOL_XML)
|
||||
converter = _build(tmp_path, _TOOL_XML, tool_conf=CONVERTER_TOOL_CONF)
|
||||
assert plain is not None and plain.is_datatype_converter is False
|
||||
assert converter is not None and converter.is_datatype_converter is True
|
||||
Reference in New Issue
Block a user