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 tool_shed.util.readme_util
import logging
import os
from typing import (
Any,
TYPE_CHECKING,
)
from galaxy import web
from galaxy.tool_shed.util import basic_util
from galaxy.tool_shed.util.hg_util import get_file_data_from_manifest
from galaxy.tool_shed.util.shed_util_common import set_image_paths
from galaxy.util import (
rst_to_html,
unicodify,
)
from galaxy.util.sanitize_html import sanitize_html
from tool_shed.util.metadata_util import get_latest_downloadable_changeset_revision
if TYPE_CHECKING:
from tool_shed.structured_app import ToolShedApp
from tool_shed.webapp.model import Repository
log = logging.getLogger(__name__)
[docs]
def render_readme(readme_file_name: str, text: str) -> str:
"""Render repository-owner supplied README text as HTML that is safe to display."""
if not readme_file_name.endswith(".rst"):
return basic_util.to_html_string(text)
html = rst_to_html(text)
# Substitute the legacy placeholders literally - README text must never be evaluated as a template.
if "${static_path}" in html:
html = html.replace("${static_path}", web.url_for("/static"))
if "${host_url}" in html:
html = html.replace("${host_url}", web.url_for("/", qualified=True))
return sanitize_html(html)
[docs]
def build_readme_files_dict(
app: "ToolShedApp",
repository: "Repository",
changeset_revision: str,
metadata: dict[str, Any],
) -> dict[str, str]:
"""
Return a dictionary of readme file name <-> rendered HTML for all readme files defined in the received metadata.
README files of the latest downloadable revision are read from disk, older revisions from the repository manifest.
"""
readme_files_dict: dict[str, str] = {}
if not metadata or "readme_files" not in metadata:
return readme_files_dict
encoded_repository_id = app.security.encode_id(repository.id)
use_disk_files = changeset_revision == get_latest_downloadable_changeset_revision(app, repository)
image_changeset_revision = None if use_disk_files else changeset_revision
for relative_path_to_readme_file in metadata["readme_files"]:
readme_file_name = os.path.split(relative_path_to_readme_file)[1]
if use_disk_files:
text = _read_readme_from_disk(relative_path_to_readme_file)
else:
# Metadata paths are prefixed with the repository's location on disk.
path_in_repository = os.path.relpath(relative_path_to_readme_file, repository.repo_path())
text = _read_readme_from_manifest(repository, changeset_revision, path_in_repository)
if text:
readme_files_dict[readme_file_name] = _render_repository_readme(
app, encoded_repository_id, image_changeset_revision, readme_file_name, text
)
return readme_files_dict
def _read_readme_from_disk(relative_path_to_readme_file: str) -> str | None:
try:
with open(os.path.abspath(relative_path_to_readme_file), encoding="utf-8") as f:
return f.read()
except Exception:
log.exception("Error reading README file '%s' from disk", relative_path_to_readme_file)
return None
def _read_readme_from_manifest(repository: "Repository", changeset_revision: str, readme_path: str) -> str | None:
try:
data = get_file_data_from_manifest(readme_path, repository.hg_repo, changeset_revision)
except Exception:
log.exception("Error reading README file '%s' from repository manifest", readme_path)
return None
if data is None:
return None
# Translate newlines as reading the file from disk in text mode does.
return unicodify(data).replace("\r\n", "\n").replace("\r", "\n")
def _render_repository_readme(
app: "ToolShedApp",
encoded_repository_id: str,
image_changeset_revision: str | None,
readme_file_name: str,
text: str,
) -> str:
text = basic_util.size_string(text)
if ".. image:: " in text:
# Point relative image paths at the repository's image route.
text = set_image_paths(
app, text, encoded_repository_id=encoded_repository_id, changeset_revision=image_changeset_revision
)
return render_readme(readme_file_name, text)