diff --git a/lib/galaxy/tool_util/linters/help.py b/lib/galaxy/tool_util/linters/help.py index 0008d465662e..b6c7fde4c76a 100644 --- a/lib/galaxy/tool_util/linters/help.py +++ b/lib/galaxy/tool_util/linters/help.py @@ -1,43 +1,118 @@ """This module contains a linting function for a tool's help.""" +from typing import ( + TYPE_CHECKING, + Union, +) -from typing import Union - +from galaxy.tool_util.lint import Linter from galaxy.util import ( rst_to_html, unicodify, ) +if TYPE_CHECKING: + from galaxy.tool_util.lint import LintContext + from galaxy.tool_util.parser.interface import ToolSource + + +class HelpMultiple(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + helps = tool_xml.findall("./help") + print(helps) + if len(helps) > 1: + lint_ctx.error("More than one help section found, behavior undefined.", node=helps[1]) + + +class HelpMissing(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + root = tool_xml.find("./help") or tool_xml.getroot() + help = tool_xml.find("./help") + if help is None: + lint_ctx.warn("No help section found, consider adding a help section to your tool.", node=root) + + +class HelpEmpty(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + help = tool_xml.find("./help") + if help is None: + return + help_text = help.text or "" + if not help_text.strip(): + lint_ctx.warn("Help section appears to be empty.", node=help) + + +class HelpPresent(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + help = tool_xml.find("./help") + if help is None: + return + help_text = help.text or "" + if help_text.strip(): + lint_ctx.valid("Tool contains help section.", node=help) + + +class HelpTODO(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + help = tool_xml.find("./help") + if help is None: + return + help_text = help.text or "" + if "TODO" in help_text: + lint_ctx.warn("Help contains TODO text.", node=help) + + +class HelpInvalidRST(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + help = tool_xml.find("./help") + if help is None: + return + help_text = help.text or "" + if not help_text.strip(): + return + invalid_rst = rst_invalid(help_text) + if invalid_rst: + lint_ctx.warn(f"Invalid reStructuredText found in help - [{invalid_rst}].", node=help) + -def lint_help(tool_xml, lint_ctx): - """Ensure tool contains exactly one valid RST help block.""" - # determine node to report for general problems with help - root = tool_xml.find("./help") - if root is None: - root = tool_xml.getroot() - helps = tool_xml.findall("./help") - if len(helps) > 1: - lint_ctx.error("More than one help section found, behavior undefined.", node=helps[1]) - return - - if len(helps) == 0: - lint_ctx.warn("No help section found, consider adding a help section to your tool.", node=root) - return - - help_text = helps[0].text or "" - if not help_text.strip(): - lint_ctx.warn("Help section appears to be empty.", node=helps[0]) - return - - lint_ctx.valid("Tool contains help section.", node=helps[0]) - - if "TODO" in help_text: - lint_ctx.warn("Help contains TODO text.", node=helps[0]) - - invalid_rst = rst_invalid(help_text) - if invalid_rst: - lint_ctx.warn(f"Invalid reStructuredText found in help - [{invalid_rst}].", node=helps[0]) - else: - lint_ctx.valid("Help contains valid reStructuredText.", node=helps[0]) +class HelpValidRST(Linter): + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + help = tool_xml.find("./help") + if help is None: + return + help_text = help.text or "" + if not help_text.strip(): + return + invalid_rst = rst_invalid(help_text) + if not invalid_rst: + lint_ctx.valid("Help contains valid reStructuredText.", node=help) def rst_invalid(text: str) -> Union[bool, str]: diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 3df95db07f90..68b9bc7920f0 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -1053,18 +1053,17 @@ def test_general_valid_new_profile_fmt(lint_ctx): def test_help_multiple(lint_ctx): - tool_xml_tree = get_xml_tree(HELP_MULTIPLE) - run_lint_module(lint_ctx, help, tool_xml_tree) - assert "More than one help section found, behavior undefined." in lint_ctx.error_messages + tool_source = get_xml_tool_source(HELP_MULTIPLE) + run_lint_module(lint_ctx, help, tool_source) assert not lint_ctx.info_messages - assert not lint_ctx.valid_messages + assert len(lint_ctx.valid_messages) == 2 # has help and valid rst assert not lint_ctx.warn_messages - assert len(lint_ctx.error_messages) == 1 + assert lint_ctx.error_messages == ["More than one help section found, behavior undefined."] def test_help_absent(lint_ctx): - tool_xml_tree = get_xml_tree(HELP_ABSENT) - run_lint_module(lint_ctx, help, tool_xml_tree) + tool_source = get_xml_tool_source(HELP_ABSENT) + run_lint_module(lint_ctx, help, tool_source) assert "No help section found, consider adding a help section to your tool." in lint_ctx.warn_messages assert not lint_ctx.info_messages assert not lint_ctx.valid_messages @@ -1073,8 +1072,8 @@ def test_help_absent(lint_ctx): def test_help_empty(lint_ctx): - tool_xml_tree = get_xml_tree(HELP_EMPTY) - run_lint_module(lint_ctx, help, tool_xml_tree) + tool_source = get_xml_tool_source(HELP_EMPTY) + run_lint_module(lint_ctx, help, tool_source) assert "Help section appears to be empty." in lint_ctx.warn_messages assert not lint_ctx.info_messages assert not lint_ctx.valid_messages @@ -1083,8 +1082,8 @@ def test_help_empty(lint_ctx): def test_help_todo(lint_ctx): - tool_xml_tree = get_xml_tree(HELP_TODO) - run_lint_module(lint_ctx, help, tool_xml_tree) + tool_source = get_xml_tool_source(HELP_TODO) + run_lint_module(lint_ctx, help, tool_source) assert "Tool contains help section." in lint_ctx.valid_messages assert "Help contains valid reStructuredText." in lint_ctx.valid_messages assert "Help contains TODO text." in lint_ctx.warn_messages @@ -1095,8 +1094,8 @@ def test_help_todo(lint_ctx): def test_help_invalid_rst(lint_ctx): - tool_xml_tree = get_xml_tree(HELP_INVALID_RST) - run_lint_module(lint_ctx, help, tool_xml_tree) + tool_source = get_xml_tool_source(HELP_INVALID_RST) + run_lint_module(lint_ctx, help, tool_source) assert "Tool contains help section." in lint_ctx.valid_messages assert ( "Invalid reStructuredText found in help - [:2: (WARNING/2) Inline strong start-string without end-string.\n]."