1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/3e00d002e7ba/ Changeset: 3e00d002e7ba User: jmchilton Date: 2014-08-27 16:49:15 Summary: Reduce workflow step state and module population duplication. Move all operations prior to workflow invocation (and a few other places state and modules are used) relating to populating the non-persistent attributes of `WorkflowStep`s (e.g. module, state, upgrade_messages, and input_connections_by_name) into a single function on a new class. This should fix a couple of small errors as well - input_connections_by_name was being mis-calculated in a few places (though potentially never used down those roads) and some error handling code in the workflow editor template was expecting step.module to be populated with something (even if None) and it was not always. Affected #: 4 files diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -26,7 +26,7 @@ from galaxy.web import error, url_for from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField from galaxy.web.form_builder import build_select_field, HistoryField, PasswordField, WorkflowField, WorkflowMappingField -from galaxy.workflow.modules import module_factory +from galaxy.workflow.modules import module_factory, WorkflowModuleInjector, MissingToolException from galaxy.model.orm import eagerload, eagerload_all, desc from galaxy.security.validate_user_input import validate_publicname from galaxy.util.sanitize_html import sanitize_html @@ -1625,30 +1625,13 @@ def get_stored_workflow_steps( self, trans, stored_workflow ): """ Restores states for a stored workflow's steps. """ + module_injector = WorkflowModuleInjector( trans ) for step in stored_workflow.latest_workflow.steps: - step.upgrade_messages = {} - if step.type == 'tool' or step.type is None: - # Restore the tool state for the step - module = module_factory.from_workflow_step( trans, step ) - if module: - #Check if tool was upgraded - step.upgrade_messages = module.check_and_update_state() - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - module.add_dummy_datasets( connections=step.input_connections ) - # Store state with the step - step.module = module - step.state = module.state - else: - step.upgrade_messages = "Unknown Tool ID" - step.module = None - step.state = None - else: - ## Non-tool specific stuff? - step.module = module_factory.from_workflow_step( trans, step ) - step.state = step.module.get_runtime_state() - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) + try: + module_injector.inject( step ) + except MissingToolException: + # Now upgrade_messages is a string instead of a dict, why? + step.upgrade_messages = "Unknown Tool ID" def _import_shared_workflow( self, trans, stored): """ """ diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/webapps/galaxy/controllers/workflow.py --- a/lib/galaxy/webapps/galaxy/controllers/workflow.py +++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py @@ -24,6 +24,8 @@ from galaxy.web.framework import form from galaxy.web.framework.helpers import grids, time_ago from galaxy.web.framework.helpers import to_unicode +from galaxy.workflow.modules import WorkflowModuleInjector +from galaxy.workflow.modules import MissingToolException from galaxy.workflow.modules import module_factory, is_tool_module_type from galaxy.workflow.run import invoke from galaxy.workflow.run import WorkflowRunConfig @@ -1269,6 +1271,7 @@ error("That history does not exist.") try: # use a try/finally block to restore the user's current history default_target_history = trans.get_history() + module_injector = WorkflowModuleInjector( trans ) if kwargs: # If kwargs were provided, the states for each step should have # been POSTed @@ -1276,45 +1279,15 @@ invocations = [] for (kwargs, multi_input_keys) in _expand_multiple_inputs(kwargs): for step in workflow.steps: - step.upgrade_messages = {} - # Connections by input name - input_connections_by_name = {} - for conn in step.input_connections: - input_name = conn.input_name - if not input_name in input_connections_by_name: - input_connections_by_name[input_name] = [] - input_connections_by_name[input_name].append(conn) - step.input_connections_by_name = input_connections_by_name # Extract just the arguments for this step by prefix p = "%s|" % step.id l = len(p) step_args = dict( ( k[l:], v ) for ( k, v ) in kwargs.iteritems() if k.startswith( p ) ) - step_errors = None - if step.type == 'tool' or step.type is None: - module = module_factory.from_workflow_step( trans, step ) - # Fix any missing parameters - step.upgrade_messages = module.check_and_update_state() - if step.upgrade_messages: - has_upgrade_messages = True - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - module.add_dummy_datasets( connections=step.input_connections ) - # Get the tool - tool = module.tool - # Get the state - step.state = state = module.state - # Get old errors - old_errors = state.inputs.pop( "__errors__", {} ) - # Update the state - step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args, - update_only=True, old_errors=old_errors ) - else: - # Fix this for multiple inputs - module = step.module = module_factory.from_workflow_step( trans, step ) - state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) ) - step_errors = module.update_runtime_state( trans, state, step_args ) + step_errors = module_injector.inject( step, step_args ) + if step.upgrade_messages: + has_upgrade_messages = True if step_errors: - errors[step.id] = state.inputs["__errors__"] = step_errors + errors[step.id] = step.state.inputs["__errors__"] = step_errors if 'run_workflow' in kwargs and not errors: new_history = None if 'new_history' in kwargs: @@ -1361,36 +1334,20 @@ # Prepare each step missing_tools = [] for step in workflow.steps: - step.upgrade_messages = {} - # Contruct modules + try: + module_injector.inject( step ) + except MissingToolException: + if step.tool_id not in missing_tools: + missing_tools.append(step.tool_id) + continue + if step.upgrade_messages: + has_upgrade_messages = True if step.type == 'tool' or step.type is None: - # Restore the tool state for the step - step.module = module_factory.from_workflow_step( trans, step ) - if not step.module: - if step.tool_id not in missing_tools: - missing_tools.append(step.tool_id) - continue - step.upgrade_messages = step.module.check_and_update_state() - if step.upgrade_messages: - has_upgrade_messages = True - if step.type == 'tool' and step.module.version_changes: + if step.module.version_changes: step_version_changes.extend(step.module.version_changes) - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - step.module.add_dummy_datasets( connections=step.input_connections ) - # Store state with the step - step.state = step.module.state # Error dict if step.tool_errors: - # has_errors is never used. - # has_errors = True errors[step.id] = step.tool_errors - else: - ## Non-tool specific stuff? - step.module = module_factory.from_workflow_step( trans, step ) - step.state = step.module.get_runtime_state() - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) if missing_tools: stored.annotation = self.get_item_annotation_str( trans.sa_session, trans.user, stored ) return trans.fill_template( @@ -1464,32 +1421,17 @@ trans.sa_session.add(m) # Prepare each step trans.sa_session.flush() + module_injector = WorkflowModuleInjector( trans ) for step in workflow.steps: step.upgrade_messages = {} # Contruct modules + module_injector.inject( step ) + if step.upgrade_messages: + has_upgrade_messages = True if step.type == 'tool' or step.type is None: - # Restore the tool state for the step - step.module = module_factory.from_workflow_step( trans, step ) - # Fix any missing parameters - step.upgrade_messages = step.module.check_and_update_state() - if step.upgrade_messages: - has_upgrade_messages = True - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - step.module.add_dummy_datasets( connections=step.input_connections ) - # Store state with the step - step.state = step.module.state # Error dict if step.tool_errors: - # has_errors is never used - # has_errors = True errors[step.id] = step.tool_errors - else: - ## Non-tool specific stuff? - step.module = module_factory.from_workflow_step( trans, step ) - step.state = step.module.get_runtime_state() - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) # Render the form return trans.fill_template( "workflow/tag_outputs.mako", diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/workflow/modules.py --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -559,3 +559,81 @@ tool=ToolModule, ) module_factory = WorkflowModuleFactory( module_types ) + + +class MissingToolException( Exception ): + """ WorkflowModuleInjector will raise this if the tool corresponding to the + module is missing. """ + + +class WorkflowModuleInjector(object): + """ Injects workflow step objects from the ORM with appropriate module and + module generated/influenced state. """ + + def __init__( self, trans ): + self.trans = trans + + def inject( self, step, step_args=None ): + """ Pre-condition: `step` is an ORM object coming from the database, if + supplied `step_args` is the representation of the inputs for that step + supplied via web form. + + Post-condition: The supplied `step` has new non-persistent attributes + useful during workflow invocation. These include 'upgrade_messages', + 'state', 'input_connections_by_name', and 'module'. + + If step_args is provided from a web form this is applied to generate + 'state' else it is just obtained from the database. + """ + trans = self.trans + + step_errors = None + + step.upgrade_messages = {} + + # Make connection information available on each step by input name. + input_connections_by_name = {} + for conn in step.input_connections: + input_name = conn.input_name + if not input_name in input_connections_by_name: + input_connections_by_name[input_name] = [] + input_connections_by_name[input_name].append(conn) + step.input_connections_by_name = input_connections_by_name + + # Populate module. + module = step.module = module_factory.from_workflow_step( trans, step ) + + # Calculating step errors and state depends on whether step is a tool step or not. + if step.type == 'tool' or step.type is None: + if not module: + step.module = None + step.state = None + raise MissingToolException() + + # Fix any missing parameters + step.upgrade_messages = module.check_and_update_state() + + # Any connected input needs to have value DummyDataset (these + # are not persisted so we need to do it every time) + module.add_dummy_datasets( connections=step.input_connections ) + + state = module.state + step.state = state + if step_args is not None: + # Get the tool + tool = module.tool + # Get old errors + old_errors = state.inputs.pop( "__errors__", {} ) + # Update the state + step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args, + update_only=True, old_errors=old_errors ) + + else: + if step_args: + # Fix this for multiple inputs + state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) ) + step_errors = module.update_runtime_state( trans, state, step_args ) + else: + step.state = step.module.get_runtime_state() + + return step_errors diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/workflow/run.py --- a/lib/galaxy/workflow/run.py +++ b/lib/galaxy/workflow/run.py @@ -229,24 +229,10 @@ def _populate_state( self ): # Build the state for each step + module_injector = modules.WorkflowModuleInjector( self.trans ) for step in self.workflow.steps: - step_errors = None - input_connections_by_name = {} - for conn in step.input_connections: - input_name = conn.input_name - if not input_name in input_connections_by_name: - input_connections_by_name[input_name] = [] - input_connections_by_name[input_name].append(conn) - step.input_connections_by_name = input_connections_by_name - + step_errors = module_injector.inject( step ) if step.type == 'tool' or step.type is None: - step.module = modules.module_factory.from_workflow_step( self.trans, step ) - # Check for missing parameters - step.upgrade_messages = step.module.check_and_update_state() - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - step.module.add_dummy_datasets( connections=step.input_connections ) - step.state = step.module.state _update_step_parameters( step, self.param_map ) if step.tool_errors: message = "Workflow cannot be run because of validation errors in some steps: %s" % step_errors @@ -254,9 +240,6 @@ if step.upgrade_messages: message = "Workflow cannot be run because of step upgrade messages: %s" % step.upgrade_messages raise exceptions.MessageException( message ) - else: - step.module = modules.module_factory.from_workflow_step( self.trans, step ) - step.state = step.module.get_runtime_state() def _update_step_parameters(step, normalized_param_map): Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.