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.citations
"""This module contains citation linters.
Citations describe references that should be used when consumers
of the tool publish results.
"""
from typing import TYPE_CHECKING
from pydantic import ValidationError
from galaxy.tool_util.lint import Linter
from galaxy.tool_util.version import parse_version
from galaxy.tool_util_models.tool_source import Citation
# Profile at which invalid citations stop loading fatally rather than being
# dropped; see galaxyproject/galaxy#22795.
CITATION_FATAL_PROFILE = parse_version("26.1")
if TYPE_CHECKING:
from galaxy.tool_util.lint import LintContext
from galaxy.tool_util.parser.interface import ToolSource
[docs]
class CitationsMissing(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("./citations")
if root is None:
root = tool_xml.getroot()
citations = tool_xml.findall("citations")
if len(citations) == 0:
lint_ctx.warn("No citations found, consider adding citations to your tool.", linter=cls.name(), node=root)
[docs]
class CitationsNoText(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
citations = tool_xml.find("citations")
if citations is None:
return
for citation in citations:
citation_type = citation.attrib.get("type")
if citation_type in ["doi", "bibtex"] and (citation.text is None or not citation.text.strip()):
lint_ctx.error(f"Empty {citation_type} citation.", linter=cls.name(), node=citation)
[docs]
class CitationsFound(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("./citations")
if root is None:
root = tool_xml.getroot()
citations = tool_xml.find("citations")
if citations is not None and len(citations) > 0:
lint_ctx.valid(f"Found {len(citations)} citations.", linter=cls.name(), node=root)
[docs]
class CitationsInvalid(Linter):
"""Surface citations that the ``Citation`` model would reject at tool load.
Reuses the same pydantic validation the parser uses so the linter reports
exactly what fails to load, plus the legacy ``doi:`` prefix that is accepted
but normalized. For tools targeting profile >= 26.1 these are errors (an
invalid citation prevents loading and the legacy prefix is no longer
tolerated); for older profiles they are warnings (the citation is dropped).
"""
[docs]
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
tool_xml = getattr(tool_source, "xml_tree", None)
if not tool_xml:
return
citations = tool_xml.find("citations")
if citations is None:
return
report = (
lint_ctx.error if parse_version(tool_source.parse_profile()) >= CITATION_FATAL_PROFILE else lint_ctx.warn
)
for citation in citations:
content = (citation.text or "").strip()
if not content:
# Empty citations are reported by CitationsNoText.
continue
# An absent type routes to the model's content-shape check (a useful
# message) rather than failing on the required str field.
citation_type = citation.attrib.get("type") or ""
try:
parsed = Citation(type=citation_type, content=content)
except ValidationError as e:
report(
f"Citation '{content}' is invalid and will not load for tools with profile >= 26.1 "
f"(and is dropped for older profiles): {e.errors()[0]['msg']}",
linter=cls.name(),
node=citation,
)
continue
if parsed.content != content:
report(
f"Citation '{content}' uses the legacy 'doi:' prefix; use the bare DOI '{parsed.content}' instead.",
linter=cls.name(),
node=citation,
)
[docs]
class CitationsNoValid(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("./citations")
if root is None:
root = tool_xml.getroot()
citations = tool_xml.findall("citations")
if len(citations) != 1:
return
if len(citations[0]) == 0:
lint_ctx.warn("Found no valid citations.", linter=cls.name(), node=root)