Merging wtih dev

This commit is contained in:
anuprulez
2017-04-03 16:41:16 +02:00
parent fa086edf22
commit 0c3f7989bb
17 changed files with 325 additions and 65 deletions
+1 -2
View File
@@ -66,8 +66,7 @@ lib/galaxy/datatypes/sequence.py
lib/galaxy/datatypes/tabular.py
lib/galaxy/datatypes/text.py
lib/galaxy/datatypes/tracks.py
lib/galaxy/datatypes/util/generic_util.py
lib/galaxy/datatypes/util/__init__.py
lib/galaxy/datatypes/util/
lib/galaxy/eggs/
lib/galaxy/exceptions/__init__.py
lib/galaxy/external_services/__init__.py
+4
View File
@@ -1089,6 +1089,10 @@ use_interactive = True
# collections.
#force_beta_workflow_scheduled_for_collections=False
# This is the maximum amount of time a workflow invocation may stay in an active
# scheduling state in seconds. Set to -1 to disable this maximum and allow any workflow
# invocation to schedule indefinitely. The default corresponds to 1 month.
#maximum_workflow_invocation_duration = 2678400
# Force serial scheduling of workflows within the context of a particular history
#history_local_serial_workflow_scheduling=False
+1
View File
@@ -327,6 +327,7 @@ class Configuration( object ):
self.force_beta_workflow_scheduled_for_collections = string_as_bool( kwargs.get( 'force_beta_workflow_scheduled_for_collections', 'False' ) )
self.history_local_serial_workflow_scheduling = string_as_bool( kwargs.get( 'history_local_serial_workflow_scheduling', 'False' ) )
self.maximum_workflow_invocation_duration = int( kwargs.get( "maximum_workflow_invocation_duration", 2678400 ) )
# Per-user Job concurrency limitations
self.cache_user_job_count = string_as_bool( kwargs.get( 'cache_user_job_count', False ) )
+12 -3
View File
@@ -16,7 +16,7 @@ from galaxy.datatypes import metadata
from galaxy.datatypes.metadata import MetadataElement
from galaxy.datatypes.sniff import get_headers
from galaxy.datatypes.tabular import Tabular
from galaxy.datatypes.util.gff_util import parse_gff_attributes
from galaxy.datatypes.util.gff_util import parse_gff3_attributes, parse_gff_attributes
from galaxy.web import url_for
from . import (
@@ -649,6 +649,7 @@ class Gff( Tabular, _RemoteCallMixin ):
edam_data = "data_1255"
edam_format = "format_2305"
file_ext = "gff"
valid_gff_frame = ['.', '0', '1', '2']
column_names = [ 'Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Group' ]
data_sources = { "data": "interval_index", "index": "bigwig", "feature_search": "fli" }
track_type = Interval.track_type
@@ -868,6 +869,8 @@ class Gff( Tabular, _RemoteCallMixin ):
return False
if hdr[6] not in data.valid_strand:
return False
if hdr[7] not in self.valid_gff_frame:
return False
return True
except:
return False
@@ -902,7 +905,7 @@ class Gff3( Gff ):
edam_format = "format_1975"
file_ext = "gff3"
valid_gff3_strand = ['+', '-', '.', '?']
valid_gff3_phase = ['.', '0', '1', '2']
valid_gff3_phase = Gff.valid_gff_frame
column_names = [ 'Seqid', 'Source', 'Type', 'Start', 'End', 'Score', 'Strand', 'Phase', 'Attributes' ]
track_type = Interval.track_type
@@ -945,7 +948,7 @@ class Gff3( Gff ):
def sniff( self, filename ):
"""
Determines whether the file is in gff version 3 format
Determines whether the file is in GFF version 3 format
GFF 3 format:
@@ -969,6 +972,9 @@ class Gff3( Gff ):
>>> fname = get_test_fname( 'test.gff' )
>>> Gff3().sniff( fname )
False
>>> fname = get_test_fname( 'test.gtf' )
>>> Gff3().sniff( fname )
False
>>> fname = get_test_fname('gff_version_3.gff')
>>> Gff3().sniff( fname )
True
@@ -1005,6 +1011,7 @@ class Gff3( Gff ):
return False
if hdr[7] not in self.valid_gff3_phase:
return False
parse_gff3_attributes(hdr[8])
return True
except:
return False
@@ -1069,6 +1076,8 @@ class Gtf( Gff ):
return False
if hdr[6] not in data.valid_strand:
return False
if hdr[7] not in self.valid_gff_frame:
return False
# Check attributes for gene_id, transcript_id
attributes = parse_gff_attributes( hdr[8] )
+25 -2
View File
@@ -3,8 +3,8 @@ Provides utilities for working with GFF files.
"""
import copy
from bx.intervals.io import GenomicInterval, MissingFieldError, NiceReaderWrapper, ParseError, GenomicIntervalReader
from bx.tabular.io import Header, Comment
from bx.intervals.io import GenomicInterval, GenomicIntervalReader, MissingFieldError, NiceReaderWrapper, ParseError
from bx.tabular.io import Comment, Header
from galaxy.util.odict import odict
@@ -347,6 +347,29 @@ def parse_gff_attributes( attr_str ):
return attributes
def parse_gff3_attributes( attr_str ):
"""
Parses a GFF3 attribute string and returns a dictionary of name-value
pairs. The general format for a GFF3 attributes string is
name1=value1;name2=value2
"""
attributes_list = attr_str.split(";")
attributes = {}
for tag_value_pair in attributes_list:
pair = tag_value_pair.strip().split("=")
if len(pair) == 1:
raise Exception("Attribute '%s' does not contain a '='" % tag_value_pair)
if pair == '':
continue
tag = pair[0].strip()
if tag == '':
raise Exception("Empty tag in attribute '%s'" % tag_value_pair)
value = pair[1].strip()
attributes[tag] = value
return attributes
def gff_attributes_to_str( attrs, gff_format ):
"""
Convert GFF attributes to string. Supported formats are GFF3, GTF.
+5
View File
@@ -4090,6 +4090,11 @@ class WorkflowInvocation( object, Dictifiable ):
return True
return False
@property
def seconds_since_created( self ):
create_time = self.create_time or galaxy.model.orm.now.now() # In case not flushed yet
return (galaxy.model.orm.now.now() - create_time).total_seconds()
class WorkflowInvocationToSubworkflowInvocationAssociation( object, Dictifiable ):
dict_collection_visible_keys = ( 'id', 'workflow_step_id', 'workflow_invocation_id', 'subworkflow_invocation_id' )
+21
View File
@@ -131,6 +131,10 @@ class CondaDependencyResolver(DependencyResolver, MultipleDependencyResolver, Li
if not all_resolved:
return None
environments = set([os.path.basename(dependency.environment_path) for dependency in all_resolved])
return self.uninstall_environments(environments)
def uninstall_environments(self, environments):
environments = [env if not env.startswith(self.conda_context.envs_path) else os.path.basename(env) for env in environments]
return_codes = [self.conda_context.exec_remove([env]) for env in environments]
final_return_code = 0
for env, return_code in zip(environments, return_codes):
@@ -286,6 +290,23 @@ class CondaDependencyResolver(DependencyResolver, MultipleDependencyResolver, Li
def _expand_requirement(self, requirement):
return self._expand_specs(self._expand_mappings(requirement))
def unused_dependency_paths(self, toolbox_requirements_status):
"""
Identify all local environments that are not needed to build requirements_status.
We try to resolve the requirements, and we note every environment_path that has been taken.
"""
used_paths = set()
for dependencies in toolbox_requirements_status.values():
for dependency in dependencies:
if dependency.get('dependency_type') == 'conda':
path = os.path.basename(dependency['environment_path'])
used_paths.add(path)
dir_contents = set(os.listdir(self.conda_context.envs_path) if os.path.exists(self.conda_context.envs_path) else [])
unused_paths = dir_contents.difference(used_paths) # New set with paths in dir_contents but not in used_paths
unused_paths = [os.path.join(self.conda_context.envs_path, p) for p in unused_paths]
return unused_paths
def list_dependencies(self):
for install_target in installed_conda_targets(self.conda_context):
name = install_target.package
+48
View File
@@ -71,6 +71,35 @@ class DependencyResolversView(object):
return return_code
return None
@property
def unused_dependency_paths(self):
"""List dependencies that are not currently installed."""
unused_dependencies = []
toolbox_requirements_status = self.toolbox_requirements_status
for resolver in self._dependency_resolvers:
if hasattr(resolver, 'unused_dependency_paths'):
unused_dependencies.extend(resolver.unused_dependency_paths(toolbox_requirements_status))
return set(unused_dependencies)
def remove_unused_dependency_paths(self, envs):
"""
Remove dependencies that are not currently used.
Returns a list of all environments that have been successfully removed.
"""
envs_to_remove = set(envs)
toolbox_requirements_status = self.toolbox_requirements_status
removed_environments = set()
for resolver in self._dependency_resolvers:
if hasattr(resolver, 'unused_dependency_paths') and hasattr(resolver, 'uninstall_environments'):
unused_dependencies = resolver.unused_dependency_paths(toolbox_requirements_status)
can_remove = envs_to_remove & set(unused_dependencies)
exit_code = resolver.uninstall_environments(can_remove)
if exit_code == 0:
removed_environments = removed_environments.union(can_remove)
envs_to_remove = envs_to_remove.difference(can_remove)
return list(removed_environments)
def install_dependencies(self, requirements):
return self._dependency_manager._requirements_to_dependencies_dict(requirements, **{'install': True})
@@ -161,6 +190,25 @@ class DependencyResolversView(object):
"""
return [index for index, resolver in enumerate(self._dependency_resolvers) if resolver.can_uninstall_dependencies and not resolver.disabled]
@property
def tool_ids_by_requirements(self):
"""Dictionary with requirements as keys, and tool_ids as values."""
tool_ids_by_requirements = {}
if not self._app.toolbox.tools_by_id:
return {}
for tid, tool in self._app.toolbox.tools_by_id.items():
if tool.tool_requirements not in tool_ids_by_requirements:
tool_ids_by_requirements[tool.tool_requirements] = [tid]
else:
tool_ids_by_requirements[tool.tool_requirements].append(tid)
return tool_ids_by_requirements
@property
def toolbox_requirements_status(self):
return {r: self.get_requirements_status(tool_requirements_d={tids[0]: r},
installed_tool_dependencies=self._app.toolbox.tools_by_id[tids[0]].installed_tool_dependencies)
for r, tids in self.tool_ids_by_requirements.items()}
def get_requirements_status(self, tool_requirements_d, installed_tool_dependencies=None):
dependencies = self.show_dependencies(tool_requirements_d, installed_tool_dependencies)
# dependencies is a dict keyed on tool_ids, value is a ToolRequirements object for that tool.
+17 -10
View File
@@ -1122,9 +1122,18 @@ class Admin( object ):
@web.expose
@web.require_admin
def manage_tool_dependencies( self, trans, install_dependencies=False, uninstall_dependencies=False, selected_tool_ids=None, viewkey='View tool-centric dependencies'):
def manage_tool_dependencies( self,
trans,
install_dependencies=False,
uninstall_dependencies=False,
remove_unused_dependencies=False,
selected_tool_ids=None,
selected_environments_to_uninstall=None,
viewkey='View tool-centric dependencies'):
if not selected_tool_ids:
selected_tool_ids = []
if not selected_environments_to_uninstall:
selected_environments_to_uninstall = []
tools_by_id = trans.app.toolbox.tools_by_id
view = six.next(six.itervalues(trans.app.toolbox.tools_by_id))._view
if selected_tool_ids:
@@ -1136,17 +1145,15 @@ class Admin( object ):
[view.install_dependencies(r) for r in requirements]
elif uninstall_dependencies:
[view.uninstall_dependencies(index=None, requirements=r) for r in requirements]
tool_ids_by_requirements = {}
for tid, tool in trans.app.toolbox.tools_by_id.items():
if tool.tool_requirements not in tool_ids_by_requirements:
tool_ids_by_requirements[tool.tool_requirements] = [tid]
else:
tool_ids_by_requirements[tool.tool_requirements].append(tid)
requirements_status = {r: view.get_requirements_status({tid: r}, tools_by_id[tids[0]].installed_tool_dependencies) for r, tids in tool_ids_by_requirements.items()}
if selected_environments_to_uninstall and remove_unused_dependencies:
if not isinstance(selected_environments_to_uninstall, list):
selected_environments_to_uninstall = [selected_environments_to_uninstall]
view.remove_unused_dependency_paths(selected_environments_to_uninstall)
return trans.fill_template( '/webapps/galaxy/admin/manage_dependencies.mako',
tools=tools_by_id,
requirements_status=requirements_status,
tool_ids_by_requirements=tool_ids_by_requirements,
requirements_status=view.toolbox_requirements_status,
tool_ids_by_requirements=view.tool_ids_by_requirements,
unused_environments=view.unused_dependency_paths,
viewkey=viewkey )
@web.expose
+8 -4
View File
@@ -508,7 +508,7 @@ class PauseModule( WorkflowModule ):
return state
def execute( self, trans, progress, invocation, step ):
progress.mark_step_outputs_delayed( step )
progress.mark_step_outputs_delayed( step, why="executing pause step" )
return None
def recover_mapping( self, step, step_invocations, progress ):
@@ -522,7 +522,8 @@ class PauseModule( WorkflowModule ):
return
elif action is False:
raise CancelWorkflowEvaluation()
raise DelayedWorkflowEvaluation()
delayed_why = "workflow paused at this step waiting for review"
raise DelayedWorkflowEvaluation(why=delayed_why)
def do_invocation_step_action( self, step, action ):
""" Update or set the workflow invocation state action - generic
@@ -834,7 +835,8 @@ class ToolModule( WorkflowModule ):
workflow_invocation_uuid=invocation.uuid.hex
)
except ToolInputsNotReadyException:
raise DelayedWorkflowEvaluation()
delayed_why = "tool [%s] inputs are not ready, this special tool requires inputs to be ready" % tool.id
raise DelayedWorkflowEvaluation(why=delayed_why)
if collection_info:
step_outputs = dict( execution_tracker.implicit_collections )
@@ -1020,7 +1022,9 @@ def load_module_sections( trans ):
class DelayedWorkflowEvaluation(Exception):
pass
def __init__(self, why=None):
self.why = why
class CancelWorkflowEvaluation(Exception):
+29 -11
View File
@@ -145,6 +145,17 @@ class WorkflowInvoker( object ):
def invoke( self ):
workflow_invocation = self.workflow_invocation
maximum_duration = getattr( self.trans.app.config, "maximum_workflow_invocation_duration", -1 )
if maximum_duration > 0 and workflow_invocation.seconds_since_created > maximum_duration:
log.debug("Workflow invocation [%s] exceeded maximum number of seconds allowed for scheduling [%s], failing." % (workflow_invocation.id, maximum_duration))
workflow_invocation.state = model.WorkflowInvocation.states.FAILED
# All jobs ran successfully, so we can save now
self.trans.sa_session.add( workflow_invocation )
# Not flushing in here, because web controller may create multiple
# invocations.
return self.progress.outputs
remaining_steps = self.progress.remaining_steps()
delayed_steps = False
for step in remaining_steps:
@@ -165,9 +176,9 @@ class WorkflowInvoker( object ):
# https://github.com/galaxyproject/galaxy/issues/2259
if job:
workflow_invocation_step.job_id = job.id
except modules.DelayedWorkflowEvaluation:
except modules.DelayedWorkflowEvaluation as de:
step_delayed = delayed_steps = True
self.progress.mark_step_outputs_delayed( step )
self.progress.mark_step_outputs_delayed( step, why=de.why )
except Exception:
log.exception(
"Failed to schedule %s, problem occurred on %s.",
@@ -176,8 +187,8 @@ class WorkflowInvoker( object ):
)
raise
step_verb = "invoked" if not step_delayed else "delayed"
log.debug("Workflow step %s of invocation %s %s %s" % (step.id, workflow_invocation.id, step_verb, step_timer))
if not step_delayed:
log.debug("Workflow step %s of invocation %s invoked %s" % (step.id, workflow_invocation.id, step_timer))
if delayed_steps:
state = model.WorkflowInvocation.states.READY
@@ -207,14 +218,16 @@ class WorkflowInvoker( object ):
# No steps created yet - have to delay evaluation.
if not step_invocations:
raise modules.DelayedWorkflowEvaluation()
delayed_why = "depends on step [%s] but that step has not been invoked yet" % output_id
raise modules.DelayedWorkflowEvaluation(why=delayed_why)
for step_invocation in step_invocations:
job = step_invocation.job
if job:
# At least one job in incomplete.
if not job.finished:
raise modules.DelayedWorkflowEvaluation()
delayed_why = "depends on step [%s] but one or more jobs created from that step have not finished yet" % output_id
raise modules.DelayedWorkflowEvaluation(why=delayed_why)
if job.state != job.states.OK:
raise modules.CancelWorkflowEvaluation()
@@ -292,7 +305,8 @@ class WorkflowProgress( object ):
raise Exception(message)
step_outputs = self.outputs[ output_step_id ]
if step_outputs is STEP_OUTPUT_DELAYED:
raise modules.DelayedWorkflowEvaluation()
delayed_why = "dependent step [%s] delayed, so this step must be delayed" % output_step_id
raise modules.DelayedWorkflowEvaluation(why=delayed_why)
output_name = connection.output_name
try:
replacement = step_outputs[ output_name ]
@@ -313,7 +327,8 @@ class WorkflowProgress( object ):
# TODO: consider distinguish between cancelled and failed?
raise modules.CancelWorkflowEvaluation()
raise modules.DelayedWorkflowEvaluation()
delayed_why = "dependent collection [%s] not yet populated with datasets" % replacement.id
raise modules.DelayedWorkflowEvaluation(why=delayed_why)
return replacement
def get_replacement_workflow_output( self, workflow_output ):
@@ -338,7 +353,10 @@ class WorkflowProgress( object ):
def set_step_outputs(self, step, outputs):
self.outputs[ step.id ] = outputs
def mark_step_outputs_delayed(self, step):
def mark_step_outputs_delayed(self, step, why=None):
if why:
message = "Marking step %s outputs of invocation %s delayed (%s)" % (step.id, self.workflow_invocation.id, why)
log.debug(message)
self.outputs[ step.id ] = STEP_OUTPUT_DELAYED
def _subworkflow_invocation(self, step):
@@ -395,8 +413,8 @@ class WorkflowProgress( object ):
def _recover_mapping( self, step, step_invocations ):
try:
step.module.recover_mapping( step, step_invocations, self )
except modules.DelayedWorkflowEvaluation:
self.mark_step_outputs_delayed( step )
except modules.DelayedWorkflowEvaluation as de:
self.mark_step_outputs_delayed( step, de.why )
__all__ = ( 'invoke', 'WorkflowRunConfig' )
@@ -23,10 +23,62 @@
</%def>
<%def name="render_tool_centric_table( tools, requirements_status)">
<tr>
<th bgcolor="#D8D8D8">Select</th>
<th bgcolor="#D8D8D8">Name</th>
<th bgcolor="#D8D8D8">ID</th>
<th bgcolor="#D8D8D8">Requirement</th>
<th bgcolor="#D8D8D8">Version</th>
<th bgcolor="#D8D8D8">Resolver</th>
<th bgcolor="#D8D8D8">Exact</th>
<th bgcolor="#D8D8D8"></th>
</tr>
<% ctr = 0 %>
%for tool in tools.values():
%if tool.tool_requirements:
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
<input type="checkbox" name="selected_tool_ids" value="${tool.id}"/>
</td>
<td>${ tool.name | h }</td>
<td>${ tool.id | h }</td>
${render_tool_dependencies( requirements_status[tool.tool_requirements], ctr=ctr) }
</tr>
<% ctr += 1 %>
%endif
%endfor
</%def>
<%def name="render_unused_dependencies(unused_environments)">
<tr>
<th bgcolor="#D8D8D8">Select</th>
<th bgcolor="#D8D8D8">Name</th>
<th bgcolor="#D8D8D8">ID</th>
<th bgcolor="#D8D8D8">Environment Path</th>
</tr>
<% ctr = 0 %>
%for path in unused_environments:
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
<input type="checkbox" name="selected_environments_to_uninstall" value="${path}"/>
</td>
<td>${ path | h }</td>
</tr>
<% ctr += 1 %>
%endfor
</%def>
<%def name="render_dependencies_details(tools, requirements_status, tool_ids_by_requirements)">
<tr>
<th bgcolor="#D8D8D8">Select</th>
<th bgcolor="#D8D8D8">Used by</th>
<th bgcolor="#D8D8D8">Environment Path</th>
<th bgcolor="#D8D8D8">Requirement</th>
<th bgcolor="#D8D8D8">Version</th>
<th bgcolor="#D8D8D8">Resolver</th>
@@ -34,26 +86,26 @@
<th bgcolor="#D8D8D8"></th>
</tr>
<% ctr = 0 %>
%for tool in tools.values():
%if tool.tool_requirements:
%for requirements, r_status in requirements_status.items():
%if requirements:
<% tool_ids = tool_ids_by_requirements[requirements] %>
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
<input type="checkbox" name="selected_tool_ids" value="${tool.id}"/>
<input type="checkbox" name="selected_tool_ids" value="${tool_ids[0]}"/>
</td>
<td>${ tool.name | h }</td>
<td>${ tool.id | h }</td>
${render_tool_dependencies( requirements_status[tool.tool_requirements], ctr=ctr) }
<td>${ ", ".join([tools[tid].name for tid in tool_ids]) | h }</td>
${render_tool_dependencies( r_status, ctr=ctr, show_environment_path=True, ncols_extra=3) }
</tr>
<% ctr += 1 %>
%endif
<% ctr += 1 %>
%endfor
</%def>
<%def name="render_dependencies_details( tools, requirements_status, tool_ids_by_requirements)">
<%def name="render_dependencies_details(tools, requirements_status, tool_ids_by_requirements)">
<tr>
<th bgcolor="#D8D8D8">Select</th>
<th bgcolor="#D8D8D8">Used by</th>
@@ -94,22 +146,36 @@
<form name="manage_tool_dependencies" action="${h.url_for( controller='admin', action='manage_tool_dependencies' )}">
%if viewkey == "Switch to tool-centric view":
<input type="submit" name="viewkey" value="Switch to details view"/>
<input type="submit" name="viewkey" value="Switch to unused dependencies view"/>
<div class="toolForm">
<div class="toolFormTitle">Tool-centric dependencies</div>
<div class="toolFormBody">
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
${render_tool_centric_table(tools, requirements_status)}
%elif viewkey == "Switch to unused dependencies view":
<input type="submit" name="viewkey" value="Switch to details view"/>
<input type="submit" name="viewkey" value="Switch to tool-centric view"/>
<div class="toolForm">
<div class="toolFormTitle">Unused dependency environments</div>
<div class="toolFormBody">
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
${render_unused_dependencies(unused_environments)}
%else:
<input type="submit" name="viewkey" value="Switch to tool-centric view"/>
<input type="submit" name="viewkey" value="Switch to unused dependencies view"/>
<div class="toolForm">
<div class="toolFormTitle">Dependency details</div>
<div class="toolFormBody">
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
${render_dependencies_details(tools, requirements_status, tool_ids_by_requirements)}
${render_dependencies_details(tools, requirements_status, tool_ids_by_requirements)}
%endif
</table>
</div>
</div>
<input type="submit" name="install_dependencies" value="Install checked dependencies using Conda"/>
<input type="submit" name="uninstall_dependencies" value="Uninstall checked dependencies using Conda"/>
%if viewkey =="Switch to unused dependencies view":
<input type="submit" name="remove_unused_dependencies" value="Remove checked paths"/>
%else:
<input type="submit" name="install_dependencies" value="Install checked dependencies using Conda"/>
<input type="submit" name="uninstall_dependencies" value="Uninstall checked dependencies using Conda"/>
%endif
</form>
+4 -18
View File
@@ -18,10 +18,6 @@ from base.populators import (
from galaxy.exceptions import error_codes
from galaxy.tools.verify.test_data import TestDataResolver
from base.workflows_format_2 import (
convert_and_import_workflow,
ImporterGalaxyInterface,
)
SIMPLE_NESTED_WORKFLOW_YAML = """
class: GalaxyWorkflow
@@ -69,7 +65,7 @@ test_data:
"""
class BaseWorkflowsApiTestCase( api.ApiTestCase, ImporterGalaxyInterface ):
class BaseWorkflowsApiTestCase( api.ApiTestCase ):
# TODO: Find a new file for this class.
def setUp( self ):
@@ -88,20 +84,12 @@ class BaseWorkflowsApiTestCase( api.ApiTestCase, ImporterGalaxyInterface ):
names = [w[ "name" ] for w in index_response.json()]
return names
# Import importer interface...
def import_workflow(self, workflow, **kwds):
workflow_str = dumps(workflow, indent=4)
data = {
'workflow': workflow_str,
}
data.update(**kwds)
upload_response = self._post( "workflows", data=data )
self._assert_status_code_is( upload_response, 200 )
return upload_response.json()
upload_response = self.workflow_populator.import_workflow(workflow, **kwds)
return upload_response
def _upload_yaml_workflow(self, has_yaml, **kwds):
workflow = convert_and_import_workflow(has_yaml, galaxy_interface=self, **kwds)
return workflow[ "id" ]
return self.workflow_populator.upload_yaml_workflow(has_yaml, **kwds)
def _setup_workflow_run( self, workflow, inputs_by='step_id', history_id=None ):
uploaded_workflow_id = self.workflow_populator.create_workflow( workflow )
@@ -869,8 +857,6 @@ test_data:
@skip_without_tool( "cat1" )
@skip_without_tool( "collection_paired_test" )
def test_workflow_run_zip_collections( self ):
# A more advanced output collection workflow, testing regression of
# https://github.com/galaxyproject/galaxy/issues/776
history_id = self.dataset_populator.new_history()
workflow_id = self._upload_yaml_workflow("""
class: GalaxyWorkflow
+1
View File
@@ -279,6 +279,7 @@ steps:
def _steps_by_label(self, workflow_as_dict):
by_label = {}
assert "steps" in workflow_as_dict, workflow_as_dict
for step in workflow_as_dict["steps"].values():
by_label[step['label']] = step
return by_label
+1 -1
View File
@@ -107,7 +107,7 @@
},
"post_job_actions": {},
"tool_errors": null,
"tool_id": "cat1",
"tool_id": "cat",
"tool_state": "{\"__page__\": 0, \"__rerun_remap_job_id__\": null, \"input1\": \"null\", \"queries\": \"[]\"}",
"tool_version": "1.0.0",
"type": "tool",
+21 -1
View File
@@ -9,6 +9,10 @@ from pkg_resources import resource_string
from six import StringIO
from base import api_asserts
from base.workflows_format_2 import (
convert_and_import_workflow,
ImporterGalaxyInterface,
)
# Simple workflow that takes an input and call cat wrapper on it.
workflow_str = resource_string( __name__, "data/test_workflow_1.ga" )
@@ -257,6 +261,10 @@ class BaseWorkflowPopulator( object ):
upload_response = self._post( "workflows/upload", data=data )
return upload_response
def upload_yaml_workflow(self, has_yaml, **kwds):
workflow = convert_and_import_workflow(has_yaml, galaxy_interface=self, **kwds)
return workflow[ "id" ]
def wait_for_invocation( self, workflow_id, invocation_id, timeout=DEFAULT_TIMEOUT ):
url = "workflows/%s/usage/%s" % ( workflow_id, invocation_id )
return wait_on_state( lambda: self._get( url ), timeout=timeout )
@@ -268,7 +276,7 @@ class BaseWorkflowPopulator( object ):
self.dataset_populator.wait_for_history( history_id, assert_ok=assert_ok, timeout=timeout )
class WorkflowPopulator( BaseWorkflowPopulator ):
class WorkflowPopulator( BaseWorkflowPopulator, ImporterGalaxyInterface ):
def __init__( self, galaxy_interactor ):
self.galaxy_interactor = galaxy_interactor
@@ -280,6 +288,18 @@ class WorkflowPopulator( BaseWorkflowPopulator ):
def _get( self, route ):
return self.galaxy_interactor.get( route )
# Required for ImporterGalaxyInterface interface - so we can recurisvely import
# nested workflows.
def import_workflow(self, workflow, **kwds):
workflow_str = json.dumps(workflow, indent=4)
data = {
'workflow': workflow_str,
}
data.update(**kwds)
upload_response = self._post( "workflows", data=data )
assert upload_response.status_code == 200, upload_response
return upload_response.json()
class LibraryPopulator( object ):
@@ -0,0 +1,48 @@
"""Integration tests for maximum workflow invocation duration configuration option."""
import time
from json import dumps
from base import integration_util
from base.populators import (
DatasetPopulator,
WorkflowPopulator,
)
class MaximumWorkflowInvocationDurationTestCase(integration_util.IntegrationTestCase):
"""Start a Pulsar job."""
framework_tool_and_types = True
def setUp( self ):
super( MaximumWorkflowInvocationDurationTestCase, self ).setUp()
self.dataset_populator = DatasetPopulator( self.galaxy_interactor )
self.workflow_populator = WorkflowPopulator( self.galaxy_interactor )
@classmethod
def handle_galaxy_config_kwds(cls, config):
config["maximum_workflow_invocation_duration"] = 20
def do_test(self):
workflow = self.workflow_populator.load_workflow_from_resource("test_workflow_pause")
workflow_id = self.workflow_populator.create_workflow(workflow)
history_id = self.dataset_populator.new_history()
hda1 = self.dataset_populator.new_dataset(history_id, content="1 2 3")
index_map = {
'0': dict(src="hda", id=hda1["id"])
}
request = {}
request["history"] = "hist_id=%s" % history_id
request[ "inputs" ] = dumps(index_map)
request[ "inputs_by" ] = 'step_index'
url = "workflows/%s/invocations" % (workflow_id)
invocation_response = self._post(url, data=request)
invocation_url = url + "/" + invocation_response.json()["id"]
time.sleep(5)
state = self._get(invocation_url).json()["state"]
assert state != "failed", state
time.sleep(35)
state = self._get(invocation_url).json()["state"]
assert state == "failed", state