Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.tool_util.linters.help
"""This module contains a linting function for a tool's help."""
from typing import (
TYPE_CHECKING,
)
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
from galaxy.util.etree import Element
[docs]
class HelpMissing(Linter):
[docs]
@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")
if root is None:
root = 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.", linter=cls.name(), node=root
)
[docs]
class HelpEmpty(Linter):
[docs]
@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.", linter=cls.name(), node=help)
[docs]
class HelpPresent(Linter):
[docs]
@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.", linter=cls.name(), node=help)
[docs]
class HelpTODO(Linter):
[docs]
@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.", linter=cls.name(), node=help)
[docs]
class HelpInvalidRST(Linter):
[docs]
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
help_text, node = _help_rst(tool_source)
if help_text is None:
return
if invalid_rst := rst_invalid(help_text):
lint_ctx.warn(f"Invalid reStructuredText found in help - [{invalid_rst}].", linter=cls.name(), node=node)
[docs]
class HelpValidRST(Linter):
[docs]
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
help_text, node = _help_rst(tool_source)
if help_text is None:
return
invalid_rst = rst_invalid(help_text)
if not invalid_rst:
lint_ctx.valid("Help contains valid reStructuredText.", linter=cls.name(), node=node)
def _help_rst(tool_source: "ToolSource") -> "tuple[str | None, Element | None]":
"""Return the help text to RST-validate and the XML node to anchor messages to.
The text is ``None`` (i.e. RST validation should be skipped) when there is no help,
when the help is empty, or when its format is not ``restructuredtext`` (e.g. Markdown
help, which YAML tool sources default to). ``node`` is the ``<help>`` element for XML
tool sources and ``None`` for others.
"""
help_content = tool_source.parse_help()
tool_xml = getattr(tool_source, "xml_tree", None)
node = tool_xml.find("./help") if tool_xml is not None else None
if help_content is None or not help_content.content.strip():
return None, node
if help_content.format != "restructuredtext":
return None, node
return help_content.content, node
[docs]
def rst_invalid(text: str) -> bool | str:
"""
Predicate to determine if text is invalid reStructuredText.
Return False if the supplied text is valid reStructuredText or
a string indicating the problem.
"""
invalid_rst: bool | str = False
try:
rst_to_html(text, error=True)
except Exception as e:
invalid_rst = unicodify(e)
return invalid_rst