From 96c3902556bb43b51ee1e1a4a0bcb882364a5b47 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Sat, 23 Feb 2013 14:09:53 -0600 Subject: [PATCH 1/4] Implement macro mechanism for tool config XML. This should allow for a drastic reduction in 'code' duplication across XML files in a tool repository or inside of an XML file (repeated blocks in different when statements for instance). --- lib/galaxy/tools/__init__.py | 84 +++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 9bcb58a5bfe..a58d8f92360 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -17,6 +17,7 @@ from galaxy.util.bunch import Bunch from galaxy.util.template import fill_template from galaxy import util, jobs, model from galaxy.jobs import ParallelismInfo +from copy import deepcopy from elementtree import ElementTree from parameters import * from parameters.grouping import * @@ -520,7 +521,7 @@ class ToolBox( object ): def load_tool( self, config_file, guid=None, **kwds ): """Load a single tool from the file named by `config_file` and return an instance of `Tool`.""" # Parse XML configuration file and get the root element - tree = util.parse_xml( config_file ) + tree = self._load_and_preprocess_tool_xml( config_file ) root = tree.getroot() # Allow specifying a different tool subclass to instantiate if root.find( "type" ) is not None: @@ -704,7 +705,86 @@ class ToolBox( object ): rval = tools return rval - + + def _load_and_preprocess_tool_xml(self, config_file): + tree = util.parse_xml(config_file) + root = tree.getroot() + macros_el = root.find('macros') + if not macros_el: + return tree + tool_dir = os.path.dirname(config_file) + macros = self._load_macros(macros_el, tool_dir) + # HACK for elementtree, newer implementations (etree/lxml) won't + # require this parent_map data structure but elementtree does not + # track parents or recongnize .find('..'). + parent_map = dict((c, p) for p in tree.getiterator() for c in p) + for expand_el in root.findall('.//expand'): + macro_name = expand_el.get('macro') + macro_def = macros[macro_name] + self._xml_replace(expand_el, macro_def, parent_map) + return tree + + def _load_macros(self, macros_el, tool_dir): + macros = {} + # Import macros from external files. + macros.update(self._load_imported_macros(macros_el, tool_dir)) + # Load all directly defined macros. + macros.update(self._load_embedded_macros(macros_el, tool_dir)) + return macros + + def _load_embedded_macros(self, macros_el, tool_dir): + macros = {} + + macro_els = [] + if macros_el: + macro_els = macros_el.findall("macro") + for macro in macro_els: + macro_name = macro.get("name") + macros[macro_name] = self._load_macro_def(macro) + + return macros + + def _load_imported_macros(self, macros_el, tool_dir): + macros = {} + + macro_import_els = [] + if macros_el: + macro_import_els = macros_el.findall("import") + for macro_import_el in macro_import_els: + raw_import_path = macro_import_el.text + tool_relative_import_path = \ + os.path.basename(raw_import_path) # Sanitize this + import_path = \ + os.path.join(tool_dir, tool_relative_import_path) + file_macros = self._load_macro_file(import_path, tool_dir) + macros.update(file_macros) + + return macros + + def _load_macro_file(self, path, tool_dir): + tree = util.parse_xml(path) + root = tree.getroot() + return self._load_macros(root, tool_dir) + + def _load_macro_def(self, macro): + return list(macro.getchildren()) + + def _xml_replace(self, query, targets, parent_map): + #parent_el = query.find('..') ## Something like this would be better with newer xml library + parent_el = parent_map[query] + matching_index = -1 + #for index, el in enumerate(parent_el.iter('.')): ## Something like this for newer implementation + for index, el in enumerate(parent_el.getchildren()): + if el == query: + matching_index = index + break + assert matching_index >= 0 + current_index = matching_index + for target in targets: + current_index += 1 + parent_el.insert(current_index, deepcopy(target)) + parent_el.remove(query) + class ToolSection( object ): """ A group of tools with similar type/purpose that will be displayed as a From b741ce0d49ab2db80f668e090a5d6a7336a10405 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Sat, 23 Feb 2013 14:09:53 -0600 Subject: [PATCH 2/4] Extend tool config macro engine so that macros may be rendered inside other macros. --- lib/galaxy/tools/__init__.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index a58d8f92360..db32ab835bc 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -714,16 +714,24 @@ class ToolBox( object ): return tree tool_dir = os.path.dirname(config_file) macros = self._load_macros(macros_el, tool_dir) - # HACK for elementtree, newer implementations (etree/lxml) won't - # require this parent_map data structure but elementtree does not - # track parents or recongnize .find('..'). - parent_map = dict((c, p) for p in tree.getiterator() for c in p) - for expand_el in root.findall('.//expand'): - macro_name = expand_el.get('macro') - macro_def = macros[macro_name] - self._xml_replace(expand_el, macro_def, parent_map) + + self._expand_macros([root], macros) return tree + def _expand_macros(self, elements, macros): + for element in elements: + # HACK for elementtree, newer implementations (etree/lxml) won't + # require this parent_map data structure but elementtree does not + # track parents or recongnize .find('..'). + parent_map = dict((c, p) for p in element.getiterator() for c in p) + for expand_el in element.findall('.//expand'): + macro_name = expand_el.get('macro') + macro_def = macros[macro_name] + + # Recursively expand contained macros. + self._expand_macros(macro_def, macros) + self._xml_replace(expand_el, macro_def, parent_map) + def _load_macros(self, macros_el, tool_dir): macros = {} # Import macros from external files. @@ -785,6 +793,7 @@ class ToolBox( object ): parent_el.insert(current_index, deepcopy(target)) parent_el.remove(query) + class ToolSection( object ): """ A group of tools with similar type/purpose that will be displayed as a From e9dc68f0a61caeda26f5183d3db9e3ec4944d030 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Sat, 23 Feb 2013 14:09:53 -0600 Subject: [PATCH 3/4] Add ability to pass sub-blocks of XML to macros and corresponding ability to render those (via the new tag) from macros. --- lib/galaxy/tools/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index db32ab835bc..6ecb0006535 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -726,7 +726,16 @@ class ToolBox( object ): parent_map = dict((c, p) for p in element.getiterator() for c in p) for expand_el in element.findall('.//expand'): macro_name = expand_el.get('macro') - macro_def = macros[macro_name] + macro_def = deepcopy(macros[macro_name]) # deepcopy needed? + + yield_els = [yield_el for macro_def_el in macro_def for yield_el in macro_def_el.findall('.//yield')] + + expand_el_children = expand_el.getchildren() + macro_def_parent_map = \ + dict((c, p) for macro_def_el in macro_def for p in macro_def_el.getiterator() for c in p) + + for yield_el in yield_els: + self._xml_replace(yield_el, expand_el_children, macro_def_parent_map) # Recursively expand contained macros. self._expand_macros(macro_def, macros) From 05028a920fed000fa6ec43a85fadc1fed0d0ccab Mon Sep 17 00:00:00 2001 From: John Chilton Date: Sat, 23 Feb 2013 14:09:53 -0600 Subject: [PATCH 4/4] Rework some duplication out tophat wrappers using macros. This dmeonstrates how macros can reduce duplication inside a file (e.g. the dbKeyActions macro) and between files (e.g. the own_junctionsConditional macro). --- tools/ngs_rna/tophat2_wrapper.xml | 165 +++++------------------ tools/ngs_rna/tophat_color_wrapper.xml | 175 +++++++------------------ tools/ngs_rna/tophat_macros.xml | 72 ++++++++++ tools/ngs_rna/tophat_wrapper.xml | 165 +++++------------------ 4 files changed, 186 insertions(+), 391 deletions(-) create mode 100644 tools/ngs_rna/tophat_macros.xml diff --git a/tools/ngs_rna/tophat2_wrapper.xml b/tools/ngs_rna/tophat2_wrapper.xml index 5733bbe4a6e..fb0f1d19bca 100644 --- a/tools/ngs_rna/tophat2_wrapper.xml +++ b/tools/ngs_rna/tophat2_wrapper.xml @@ -143,23 +143,12 @@ - - - - - - - - - - - - - - - - - + + + + + + @@ -182,17 +171,7 @@ - - - - - - - - - - - + alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" /> @@ -201,40 +180,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -319,83 +265,40 @@ (params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes') - - - - - - - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - + - + + tophat_macros.xml + + + + + + + + + + + + + + + + diff --git a/tools/ngs_rna/tophat_color_wrapper.xml b/tools/ngs_rna/tophat_color_wrapper.xml index 72c0a277b29..399d2c9c46c 100644 --- a/tools/ngs_rna/tophat_color_wrapper.xml +++ b/tools/ngs_rna/tophat_color_wrapper.xml @@ -154,23 +154,12 @@ - - - - - - - - - - - - - - - - - + + + + + + @@ -194,17 +183,7 @@ - - - - - - - - - - - + @@ -230,20 +209,8 @@ - - - - - - - - - - - - - - + + @@ -301,17 +268,7 @@ - - - - - - - - - - - + @@ -399,90 +356,50 @@ ( singlePaired['pParams']['indel_search']['allow_indel_search'] == 'Yes' ) ) ) - - - - - - - - - - - - - + - + + + + + + + + + + + tophat_macros.xml + + ( ( ( 'sParams' in singlePaired ) and ( 'indel_search' in singlePaired['sParams'] ) and ( singlePaired['sParams']['indel_search']['allow_indel_search'] == 'Yes' ) ) or ( ( 'pParams' in singlePaired ) and ( 'indel_search' in singlePaired['pParams'] ) and ( singlePaired['pParams']['indel_search']['allow_indel_search'] == 'Yes' ) ) ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tools/ngs_rna/tophat_macros.xml b/tools/ngs_rna/tophat_macros.xml new file mode 100644 index 00000000000..51ebec16d9a --- /dev/null +++ b/tools/ngs_rna/tophat_macros.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/ngs_rna/tophat_wrapper.xml b/tools/ngs_rna/tophat_wrapper.xml index 234040bc664..83321cdb9d5 100644 --- a/tools/ngs_rna/tophat_wrapper.xml +++ b/tools/ngs_rna/tophat_wrapper.xml @@ -151,23 +151,12 @@ - - - - - - - - - - - - - - - - - + + + + + + @@ -298,17 +287,7 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to - - - - - - - - - - - + @@ -316,40 +295,7 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -387,83 +333,40 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to - - - - - - - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - + - + + tophat_macros.xml + + + + + + + + + + + + + + + +