diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py
index e7d99183efa..59d2ab1d95b 100755
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -101,6 +101,8 @@ JOB_RESOURCE_CONDITIONAL_XML = """
"""
+HELP_UNINITIALIZED = threading.Lock()
+
class ToolNotFoundException( Exception ):
pass
@@ -1680,43 +1682,9 @@ class Tool( object, Dictifiable ):
This implementation supports multiple pages.
"""
# TODO: Allow raw HTML or an external link.
- self.help = None
- self.help_by_page = list()
- help_header = ""
- help_footer = ""
- if hasattr( tool_source, 'root' ) and tool_source.root.find( 'help' ) is not None:
- self.help = tool_source.root.find( 'help' )
- if self.repository_id and self.help.text.find( '.. image:: ' ) >= 0:
- # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy.
- try:
- self.help.text = suc.set_image_paths( self.app, self.repository_id, self.help.text )
- except Exception, e:
- log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) )
- help_pages = self.help.findall( "page" )
- help_header = self.help.text
- try:
- self.help = Template( rst_to_html(self.help.text), input_encoding='utf-8',
- output_encoding='utf-8', default_filters=[ 'decode.utf8' ],
- encoding_errors='replace' )
- except:
- log.exception( "error in help for tool %s" % self.name )
- # Multiple help page case
- if help_pages:
- for help_page in help_pages:
- self.help_by_page.append( help_page.text )
- help_footer = help_footer + help_page.tail
- # Each page has to rendered all-together because of backreferences allowed by rst
- try:
- self.help_by_page = [ Template( rst_to_html( help_header + x + help_footer ),
- input_encoding='utf-8', output_encoding='utf-8',
- default_filters=[ 'decode.utf8' ],
- encoding_errors='replace' )
- for x in self.help_by_page ]
- except:
- log.exception( "error in multi-page help for tool %s" % self.name )
- # Pad out help pages to match npages ... could this be done better?
- while len( self.help_by_page ) < self.npages:
- self.help_by_page.append( self.help )
+ self.__help = HELP_UNINITIALIZED
+ self.__help_by_page = HELP_UNINITIALIZED
+ self.__help_source = tool_source
def parse_outputs( self, tool_source ):
"""
@@ -1891,6 +1859,67 @@ class Tool( object, Dictifiable ):
self.repository_owner = tool_shed_repository.owner
self.installed_changeset_revision = tool_shed_repository.installed_changeset_revision
+ @property
+ def help(self):
+ if self.__help is HELP_UNINITIALIZED:
+ self.__ensure_help()
+ return self.__help
+
+ @property
+ def help_by_page(self):
+ if self.__help_by_page is HELP_UNINITIALIZED:
+ self.__ensure_help()
+ return self.__help_by_page
+
+ def __ensure_help(self):
+ with HELP_UNINITIALIZED:
+ if self.__help is HELP_UNINITIALIZED:
+ self.__inititalize_help()
+
+ def __inititalize_help(self):
+ tool_source = self.__help_source
+ self.__help = None
+ self.__help_by_page = []
+ help_header = ""
+ help_footer = ""
+ help_text = tool_source.parse_help()
+ if help_text is not None:
+ if self.repository_id and help_text.find( '.. image:: ' ) >= 0:
+ # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy.
+ try:
+ help_text = suc.set_image_paths( self.app, self.repository_id, help_text )
+ except Exception, e:
+ log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) )
+ try:
+ self.__help = Template( rst_to_html(help_text), input_encoding='utf-8',
+ output_encoding='utf-8', default_filters=[ 'decode.utf8' ],
+ encoding_errors='replace' )
+ except:
+ log.exception( "error in help for tool %s" % self.name )
+
+ # Handle deprecated multi-page help text in XML case.
+ if hasattr(tool_source, "root"):
+ help_elem = tool_source.root.find("help")
+ help_header = help_text
+ help_pages = help_elem.findall( "page" )
+ # Multiple help page case
+ if help_pages:
+ for help_page in help_pages:
+ self.__help_by_page.append( help_page.text )
+ help_footer = help_footer + help_page.tail
+ # Each page has to rendered all-together because of backreferences allowed by rst
+ try:
+ self.__help_by_page = [ Template( rst_to_html( help_header + x + help_footer ),
+ input_encoding='utf-8', output_encoding='utf-8',
+ default_filters=[ 'decode.utf8' ],
+ encoding_errors='replace' )
+ for x in self.__help_by_page ]
+ except:
+ log.exception( "error in multi-page help for tool %s" % self.name )
+ # Pad out help pages to match npages ... could this be done better?
+ while len( self.__help_by_page ) < self.npages:
+ self.__help_by_page.append( self.__help )
+
def check_workflow_compatible( self, tool_source ):
"""
Determine if a tool can be used in workflows. External tools and the
diff --git a/lib/galaxy/tools/parser/interface.py b/lib/galaxy/tools/parser/interface.py
index c9e858ff463..494e03ab7c5 100644
--- a/lib/galaxy/tools/parser/interface.py
+++ b/lib/galaxy/tools/parser/interface.py
@@ -140,6 +140,12 @@ class ToolSource(object):
"""
return [], []
+ @abstractmethod
+ def parse_help(self):
+ """ Return RST definition of help text for tool or None if the tool
+ doesn't define help text.
+ """
+
def parse_tests_to_dict(self):
return {'tests': []}
diff --git a/lib/galaxy/tools/parser/xml.py b/lib/galaxy/tools/parser/xml.py
index f6354ea3444..b394b25dc84 100644
--- a/lib/galaxy/tools/parser/xml.py
+++ b/lib/galaxy/tools/parser/xml.py
@@ -162,6 +162,10 @@ class XmlToolSource(ToolSource):
parser = StdioParser(self.root)
return parser.stdio_exit_codes, parser.stdio_regexes
+ def parse_help(self):
+ help_elem = self.root.find( 'help' )
+ return help_elem.text if help_elem is not None else None
+
def parse_tests_to_dict(self):
tests_elem = self.root.find("tests")
tests = []
diff --git a/lib/galaxy/tools/parser/yaml.py b/lib/galaxy/tools/parser/yaml.py
index 5b0b993b7cb..3bd7519d3d3 100644
--- a/lib/galaxy/tools/parser/yaml.py
+++ b/lib/galaxy/tools/parser/yaml.py
@@ -68,6 +68,9 @@ class YamlToolSource(ToolSource):
exit_code_lower.error_level = StdioErrorLevel.FATAL
return [exit_code_lower, exit_code_high], []
+ def parse_help(self):
+ return self.root_dict.get("help", None)
+
def parse_outputs(self, tool):
outputs = self.root_dict.get("outputs", {})
output_defs = []
diff --git a/test/unit/tools/test_parsing.py b/test/unit/tools/test_parsing.py
index 02a36322cea..7fd1102339d 100644
--- a/test/unit/tools/test_parsing.py
+++ b/test/unit/tools/test_parsing.py
@@ -26,6 +26,7 @@ TOOL_XML_1 = """
+ This is HELP TEXT1!!!
@@ -75,6 +76,9 @@ inputs:
type: data
- name: nestsample
type: text
+help:
+|
+ This is HELP TEXT2!!!
tests:
- inputs:
foo: 5
@@ -173,6 +177,10 @@ class XmlLoaderTestCase(BaseLoaderTestCase):
assert exit[0].range_start == 1
assert isinf(exit[0].range_end)
+ def test_help(self):
+ help_text = self._tool_source.parse_help()
+ assert help_text.strip() == "This is HELP TEXT1!!!"
+
def test_tests(self):
tests_dict = self._tool_source.parse_tests_to_dict()
tests = tests_dict["tests"]
@@ -278,6 +286,10 @@ class YamlLoaderTestCase(BaseLoaderTestCase):
assert exit[1].range_start == 1
assert isinf(exit[1].range_end)
+ def test_help(self):
+ help_text = self._tool_source.parse_help()
+ assert help_text.strip() == "This is HELP TEXT2!!!"
+
def test_inputs(self):
input_pages = self._tool_source.parse_input_pages()
assert input_pages.inputs_defined