Merge branch 'release_25.1' into release_26.0

This commit is contained in:
mvdbeek
2026-10-15 06:00:33 +02:00
5 changed files with 40 additions and 11 deletions
+1 -1
View File
@@ -1959,7 +1959,7 @@ class Tool(UsesDictVisibleKeys, ToolParameterBundle):
try:
return rst_to_html(help_content.content)
except Exception:
log.info("Exception while parsing help for tool with id '%s'", self.id)
log.warning("Exception while parsing help for tool with id '%s'", self.id, exc_info=True)
return ""
def render_help(self, static_path: str, host_url: str) -> str:
+10 -4
View File
@@ -1,5 +1,6 @@
import functools
import os
import threading
try:
import docutils.core
@@ -57,12 +58,17 @@ def get_publisher(error=False):
return pub
# Cached docutils publishers are stateful and not thread-safe.
_publish_lock = threading.Lock()
@functools.lru_cache(maxsize=None)
def rst_to_html(s, error=False):
if docutils is None:
raise Exception("Attempted to use rst_to_html but docutils unavailable.")
publisher = get_publisher(error=error)
publisher.set_source(s, None)
publisher.set_destination(None, None)
return publisher.publish(enable_exit_status=False)
with _publish_lock:
publisher = get_publisher(error=error)
publisher.set_source(s, None)
publisher.set_destination(None, None)
return publisher.publish(enable_exit_status=False)
+10 -5
View File
@@ -49,6 +49,11 @@ class TestTrsImport(SeleniumIntegrationTestCase):
def trs_config_dir(cls):
return cls.temp_config_dir("trs")
def _wait_for_import_redirect(self):
# The client redirects to the workflow list only once the import request returns,
# which includes the server fetching the workflow from the remote TRS server.
self.components.workflows.workflows_list.wait_for_visible(wait_type=self.wait_types.SHED_SEARCH)
def assert_workflow_imported(self, name):
# surround name with quotes to consider case where name contains colons
self.workflow_index_search_for(f'"{name}"')
@@ -76,7 +81,7 @@ class TestTrsImport(SeleniumIntegrationTestCase):
workflow_name="galaxy-workflow-dockstore-example-1"
).wait_for_and_click()
self.components.trs_search.import_button.wait_for_and_click()
self.sleep_for(self.wait_types.UX_RENDER)
self._wait_for_import_redirect()
self.workflow_index_open()
self.assert_workflow_imported("Test Workflow")
@@ -85,7 +90,7 @@ class TestTrsImport(SeleniumIntegrationTestCase):
self.components.trs_search.search.wait_for_and_send_keys("organization: iwc-workflows")
self.components.trs_search.search_result(workflow_name=TRS_NAME).wait_for_and_click()
self.components.trs_search.import_version(version="v0.4").wait_for_and_click()
self.sleep_for(self.wait_types.UX_RENDER)
self._wait_for_import_redirect()
self.workflow_index_open()
self.assert_workflow_imported(WORKFLOW_NAME)
self.components.workflows.trs_icon.wait_for_visible()
@@ -99,7 +104,7 @@ class TestTrsImport(SeleniumIntegrationTestCase):
self.components.trs_search.search.wait_for_and_send_keys(WORKFLOW_NAME)
self.components.trs_search.search_result(workflow_name=WORKFLOW_NAME).wait_for_and_click()
self.components.trs_search.import_button.wait_for_and_click()
self.sleep_for(self.wait_types.UX_RENDER)
self._wait_for_import_redirect()
self.workflow_index_open()
self.assert_workflow_imported(WORKFLOW_NAME)
@@ -132,7 +137,7 @@ class TestTrsImport(SeleniumIntegrationTestCase):
self.components.trs_import.select_server(server=server).wait_for_and_click()
self.components.trs_import.input.wait_for_and_send_keys(trs_id)
self.components.trs_import.import_version(version="v0.4").wait_for_and_click()
self.sleep_for(self.wait_types.UX_RENDER)
self._wait_for_import_redirect()
self.workflow_index_open()
self.assert_workflow_imported(WORKFLOW_NAME)
@@ -140,6 +145,6 @@ class TestTrsImport(SeleniumIntegrationTestCase):
self.go_to_trs_by_id()
self.components.trs_import.url_input.wait_for_and_send_keys(trs_url)
self.components.trs_import.url_import_button.wait_for_and_click()
self.sleep_for(self.wait_types.UX_RENDER)
self._wait_for_import_redirect()
self.workflow_index_open()
self.assert_workflow_imported(WORKFLOW_NAME)
@@ -318,7 +318,8 @@ def test_timeout_kills_worker_under_waiting_wrapper(tmp_path, short_timeout_grac
pid = int(pid_file.read_text())
try:
child = psutil.Process(pid)
assert child.status() == psutil.STATUS_ZOMBIE
# The orphaned worker is reaped asynchronously by init or a subreaper.
assert child.status() in (psutil.STATUS_ZOMBIE, psutil.STATUS_DEAD)
except psutil.NoSuchProcess:
pass
+17
View File
@@ -0,0 +1,17 @@
from concurrent.futures import ThreadPoolExecutor
from galaxy.util.rst_to_html import rst_to_html
def test_rst_to_html_basic():
html = rst_to_html("**bold**")
assert "<strong>bold</strong>" in html
def test_rst_to_html_concurrent_conversions():
documents = [f"Section {i}\n=========={'=' * len(str(i))}\n\nParagraph *{i}* with ``code``.\n" for i in range(50)]
with ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(rst_to_html, documents))
for i, html in enumerate(results):
assert f"Section {i}" in html
assert f"<em>{i}</em>" in html