PEP-8 and pylint fixes.

This commit is contained in:
Nicola Soranzo
2015-06-16 16:53:49 +01:00
parent 06cc164f9b
commit 3c40f32fa2
48 changed files with 795 additions and 624 deletions
+5 -5
View File
@@ -12,11 +12,7 @@ lib/galaxy/openid/__init__.py
lib/galaxy/queues.py
lib/galaxy/queue_worker.py
lib/galaxy/tags
lib/galaxy/tools/deps/{commands,containers,dependencies,dockerfiles,docker_util,__init__,requirements}.py
lib/galaxy/tools/filters
lib/galaxy/tools/parser
lib/galaxy/tools/toolbox
lib/galaxy/tools/{errors,loader_directory,test}.py
lib/galaxy/tools
lib/galaxy/util/{__init__,json,permutations,plugin_config,properties,simplegraph,sockets,sleeper,streamball,submodules}.py
lib/galaxy/web/{proxy,security}
lib/galaxy/web/base/{__init__,interactive_environments}.py
@@ -33,6 +29,7 @@ lib/galaxy/webapps/tool_shed/framework/__init__.py
lib/galaxy/webapps/tool_shed/framework/middleware/__init__.py
lib/galaxy/webapps/tool_shed/util/{__init__,shed_statistics}.py
lib/galaxy/work
lib/galaxy/workflow
lib/galaxy/version.py
lib/pulsar
lib/tool_shed/__init__.py
@@ -41,3 +38,6 @@ lib/tool_shed/galaxy_install/repository_dependencies
lib/tool_shed/galaxy_install/tools/{__init__,tool_panel_manager}.py
lib/tool_shed/repository_types/{__init__,registry,unrestricted}.py
lib/tool_shed/util/{__init__,repository_content_util,tool_util,web_util,workflow_util}.py
test/functional/tools
tools/data_source/upload.py
tools/evolution/codingSnps_filter.py
+68 -73
View File
@@ -8,15 +8,11 @@ import json
import logging
import os
import re
import shutil
import threading
import types
import urllib
import copy
import traceback
from datetime import datetime
from galaxy import eggs, util
from galaxy import eggs
eggs.require( "MarkupSafe" ) # MarkupSafe must load before mako
eggs.require( "Mako" )
@@ -40,12 +36,10 @@ from galaxy.tools.deps import build_dependency_manager
from galaxy.tools.parameters import params_to_incoming, check_param, params_from_strings, params_to_strings, visit_input_values
from galaxy.tools.parameters import output_collect
from galaxy.tools.parameters.basic import (BaseURLToolParameter,
DataToolParameter, DataCollectionToolParameter, HiddenToolParameter, LibraryDatasetToolParameter,
SelectToolParameter, ToolParameter, UnvalidatedValue,
IntegerToolParameter, FloatToolParameter)
DataToolParameter, DataCollectionToolParameter, HiddenToolParameter,
SelectToolParameter, ToolParameter, UnvalidatedValue)
from galaxy.tools.parameters.grouping import Conditional, ConditionalWhen, Repeat, Section, UploadDataset
from galaxy.tools.parameters.input_translation import ToolInputTranslator
from galaxy.tools.parameters.output import ToolOutputActionGroup
from galaxy.tools.parameters.validation import LateValidationError
from galaxy.tools.test import parse_tests
from galaxy.tools.parser import get_tool_source
@@ -55,7 +49,7 @@ from galaxy.util import rst_to_html, string_as_bool, string_to_object
from galaxy.tools.parameters.meta import expand_meta_parameters
from galaxy.util.bunch import Bunch
from galaxy.util.expressions import ExpressionContext
from galaxy.util.hash_util import hmac_new, is_hashable
from galaxy.util.hash_util import hmac_new
from galaxy.util.odict import odict
from galaxy.util.template import fill_template
from galaxy.web import url_for
@@ -86,20 +80,21 @@ class ToolErrorLog:
def __init__(self):
self.error_stack = []
self.max_errors = 100
def add_error(self, file, phase, exception):
self.error_stack.insert(0, {
"file" : file,
"time" : str(datetime.now()),
"phase" : phase,
"error" : str(exception)
"file": file,
"time": str(datetime.now()),
"phase": phase,
"error": str(exception)
} )
if len(self.error_stack) > self.max_errors:
self.error_stack.pop()
global_tool_errors = ToolErrorLog()
class ToolNotFoundException( Exception ):
pass
@@ -126,7 +121,7 @@ class ToolBox( AbstractToolBox ):
try:
tool_source = get_tool_source( config_file, getattr( self.app.config, "enable_beta_tool_formats", False ) )
except Exception, e:
#capture and log parsing errors
# capture and log parsing errors
global_tool_errors.add_error(config_file, "Tool XML parsing", e)
raise e
# Allow specifying a different tool subclass to instantiate
@@ -448,7 +443,7 @@ class Tool( object, Dictifiable ):
self.app = app
self.repository_id = repository_id
self._allow_code_files = allow_code_files
#setup initial attribute values
# setup initial attribute values
self.inputs = odict()
self.stdio_exit_codes = list()
self.stdio_regexes = list()
@@ -482,7 +477,7 @@ class Tool( object, Dictifiable ):
self.version = None
# Enable easy access to this tool's version lineage.
self.lineage_ids = []
#populate toolshed repository info, if available
# populate toolshed repository info, if available
self.populate_tool_shed_info()
# Parse XML element containing configuration
try:
@@ -534,7 +529,7 @@ class Tool( object, Dictifiable ):
return False
return True
return any( map( output_is_dynamic_collection, self.outputs.values() ) )
return any( map( output_is_dynamic_collection, self.outputs.values() ) )
def __get_job_tool_configuration(self, job_params=None):
"""Generalized method for getting this tool's job configuration.
@@ -1167,6 +1162,7 @@ class Tool( object, Dictifiable ):
# TODO: Anyway to capture tools that dynamically change their own
# outputs?
return True
def new_state( self, trans, all_pages=False, history=None ):
"""
Create a new `DefaultToolState` for this tool. It will be initialized
@@ -1183,6 +1179,7 @@ class Tool( object, Dictifiable ):
inputs = self.inputs_by_page[ 0 ]
self.fill_in_new_state( trans, inputs, state.inputs, history=history )
return state
def fill_in_new_state( self, trans, inputs, state, context=None, history=None ):
"""
Fill in a tool state dictionary with default values for all parameters
@@ -1191,6 +1188,7 @@ class Tool( object, Dictifiable ):
context = ExpressionContext( state, context )
for input in inputs.itervalues():
state[ input.name ] = input.get_initial_value( trans, context, history=history )
def get_param_html_map( self, trans, page=0, other_values={} ):
"""
Return a dictionary containing the HTML representation of each
@@ -1349,7 +1347,7 @@ class Tool( object, Dictifiable ):
params = self.__remove_meta_properties( params )
job, out_data = self.execute( trans, incoming=params, history=history, rerun_remap_job_id=rerun_remap_job_id, mapping_over_collection=mapping_over_collection )
except httpexceptions.HTTPFound, e:
#if it's a paste redirect exception, pass it up the stack
# if it's a paste redirect exception, pass it up the stack
raise e
except Exception, e:
log.exception('Exception caught while attempting tool execution:')
@@ -1365,19 +1363,19 @@ class Tool( object, Dictifiable ):
return False, message
def __handle_state_refresh( self, trans, state, errors ):
try:
self.find_fieldstorage( state.inputs )
except InterruptedUpload:
# If inputs contain a file it won't persist. Most likely this
# is an interrupted upload. We should probably find a more
# standard method of determining an incomplete POST.
return self.handle_interrupted( trans, state.inputs )
except:
pass
# Just a refresh, render the form with updated state and errors.
if not self.display_interface:
return self.__no_display_interface_response()
return self.default_template, dict( errors=errors, tool_state=state )
try:
self.find_fieldstorage( state.inputs )
except InterruptedUpload:
# If inputs contain a file it won't persist. Most likely this
# is an interrupted upload. We should probably find a more
# standard method of determining an incomplete POST.
return self.handle_interrupted( trans, state.inputs )
except:
pass
# Just a refresh, render the form with updated state and errors.
if not self.display_interface:
return self.__no_display_interface_response()
return self.default_template, dict( errors=errors, tool_state=state )
def __handle_page_advance( self, trans, state, errors ):
state.page += 1
@@ -1404,7 +1402,7 @@ class Tool( object, Dictifiable ):
def __check_param_values( self, trans, incoming, state, old_errors, process_state, history, source ):
# Process incoming data
if not( self.check_values ):
if not self.check_values:
# If `self.check_values` is false we don't do any checking or
# processing on input This is used to pass raw values
# through to/from external sites. FIXME: This should be handled
@@ -1433,9 +1431,9 @@ class Tool( object, Dictifiable ):
def find_fieldstorage( self, x ):
if isinstance( x, FieldStorage ):
raise InterruptedUpload( None )
elif type( x ) is types.DictType:
elif isinstance(x, dict):
[ self.find_fieldstorage( y ) for y in x.values() ]
elif type( x ) is types.ListType:
elif isinstance(x, list):
[ self.find_fieldstorage( y ) for y in x ]
def handle_interrupted( self, trans, inputs ):
@@ -1544,8 +1542,7 @@ class Tool( object, Dictifiable ):
history,
source,
prefix=group_prefix,
context=context,
)
context=context)
if group_errors:
errors[ input.name ] = group_errors
# Store the current case in a special value
@@ -1572,7 +1569,7 @@ class Tool( object, Dictifiable ):
any_group_errors = False
d_type = input.get_datatype( trans, context )
writable_files = d_type.writable_files
#remove extra files
# remove extra files
while len( group_state ) > len( writable_files ):
del group_state[-1]
@@ -1731,7 +1728,7 @@ class Tool( object, Dictifiable ):
# are meant to be much more transient than the rest
# of tool state.
continue
#load default initial value
# load default initial value
if not test_param_error:
test_param_error = str( e )
if trans is not None:
@@ -1798,7 +1795,7 @@ class Tool( object, Dictifiable ):
any_group_errors = False
d_type = input.get_datatype( trans, context )
writable_files = d_type.writable_files
#remove extra files
# remove extra files
while len( group_state ) > len( writable_files ):
del group_state[-1]
if group_old_errors:
@@ -2019,7 +2016,7 @@ class Tool( object, Dictifiable ):
if allow_workflow_parameters and isinstance( values[ input.name ], basestring ):
if WORKFLOW_PARAMETER_REGULAR_EXPRESSION.search( values[ input.name ] ):
ck_param = False
#this will fail when a parameter's type has changed to a non-compatible one: e.g. conditional group changed to dataset input
# this will fail when a parameter's type has changed to a non-compatible one: e.g. conditional group changed to dataset input
if ck_param:
input.value_from_basic( input.value_to_basic( values[ input.name ], trans.app ), trans.app, ignore_errors=False )
except:
@@ -2202,18 +2199,16 @@ class Tool( object, Dictifiable ):
if 'job_working_directory' in self.app.config.collect_outputs_from:
filenames.extend( glob.glob(os.path.join(job_working_directory, "child_%i_*" % outdata.id) ) )
for filename in filenames:
if not name in children:
if name not in children:
children[name] = {}
fields = os.path.basename(filename).split("_")
fields.pop(0)
parent_id = int(fields.pop(0))
designation = fields.pop(0)
visible = fields.pop(0).lower()
designation = fields[2]
visible = fields[3].lower()
if visible == "visible":
visible = True
else:
visible = False
ext = fields.pop(0).lower()
ext = fields[4].lower()
child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext,
parent_id=outdata.id,
designation=designation,
@@ -2297,7 +2292,7 @@ class Tool( object, Dictifiable ):
return tool_dict
def to_json (self, trans, kwd={}, is_workflow=False):
def to_json(self, trans, kwd={}, is_workflow=False):
"""
Recursively creates a tool dictionary containing repeats, dynamic options and updated states.
"""
@@ -2308,7 +2303,7 @@ class Tool( object, Dictifiable ):
# history id
history = None
try:
if not history_id is None:
if history_id is not None:
history = self.history_manager.get_owned( trans.security.decode_id( history_id ), trans.user, current_history=trans.history )
else:
history = trans.get_history()
@@ -2352,8 +2347,8 @@ class Tool( object, Dictifiable ):
tool_message = ''
if job:
try:
job_params = job.get_param_values( trans.app, ignore_errors = True )
job_messages = self.check_and_update_param_values( job_params, trans, update_values=False )
job_params = job.get_param_values( trans.app, ignore_errors=True )
self.check_and_update_param_values( job_params, trans, update_values=False )
self._map_source_to_history( trans, self.inputs, job_params, history )
tool_message = self._compare_tool_version(trans, job)
params_to_incoming( kwd, self.inputs, job_params, trans.app, to_html=False )
@@ -2363,7 +2358,7 @@ class Tool( object, Dictifiable ):
return { 'error': str( exception ) }
# create parameter object
params = galaxy.util.Params( kwd, sanitize = False )
params = galaxy.util.Params( kwd, sanitize=False )
# convert value to jsonifiable value
def jsonify(v):
@@ -2488,7 +2483,7 @@ class Tool( object, Dictifiable ):
group_state = state[input.name] = {}
populate_state( trans, input.cases[current_case].inputs, group_state, errors, incoming, prefix=group_prefix, context=context)
group_state['__current_case__'] = current_case
except Exception, e:
except Exception:
errors[test_param_key] = 'The selected case is unavailable/invalid.'
pass
group_state[input.test_param.name] = value
@@ -2526,7 +2521,7 @@ class Tool( object, Dictifiable ):
test_param = tool_dict['test_param']
test_param['default_value'] = jsonify(input.test_param.get_initial_value(trans, other_values, history=history))
test_param['value'] = jsonify(group_state.get(test_param['name'], test_param['default_value']))
for i in range (len ( tool_dict['cases'] ) ):
for i in range(len( tool_dict['cases'] ) ):
current_state = {}
if i == group_state.get('__current_case__', None):
current_state = group_state
@@ -2618,7 +2613,7 @@ class Tool( object, Dictifiable ):
tool_versions = []
tools = self.app.toolbox.get_loaded_tools_by_lineage(self.id)
for t in tools:
if not t.version in tool_versions:
if t.version not in tool_versions:
tool_versions.append(t.version)
# add information with underlying requirements and their versions
@@ -2656,7 +2651,7 @@ class Tool( object, Dictifiable ):
# return enriched tool model
return tool_model
def _get_job_remap ( self, job):
def _get_job_remap( self, job):
if job:
if job.state == job.states.ERROR:
try:
@@ -2707,7 +2702,7 @@ class Tool( object, Dictifiable ):
# Unpack unvalidated values to strings, they'll be validated when the
# form is submitted (this happens when re-running a job that was
# initially run by a workflow)
#This needs to be done recursively through grouping parameters
# This needs to be done recursively through grouping parameters
def mapping_callback( input, value, prefixed_name, prefixed_label ):
if isinstance( value, UnvalidatedValue ):
try:
@@ -2717,7 +2712,7 @@ class Tool( object, Dictifiable ):
log.debug( "Failed to use input.to_html_value to determine value of unvalidated parameter, defaulting to string: %s" % ( e ) )
return str( value )
if isinstance( input, DataToolParameter ):
if isinstance(value,list):
if isinstance(value, list):
values = []
for val in value:
new_val = map_to_history( val )
@@ -2746,7 +2741,7 @@ class Tool( object, Dictifiable ):
return { 'error': 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id }
if ( self.id != tool_id and self.old_id != tool_id ) or self.version != tool_version:
if self.id == tool_id:
if tool_version == None:
if tool_version is None:
# for some reason jobs don't always keep track of the tool version.
message = ''
else:
@@ -2764,12 +2759,12 @@ class Tool( object, Dictifiable ):
message += 'currently not available. You can re-run the job with this tool, which is a derivation of the original tool.'
except Exception, error:
trans.response.status = 500
return { 'error': str (error) }
return { 'error': str(error) }
# can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now
#if not self.is_workflow_compatible:
# trans.response.status = 500
# return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name }
# if not self.is_workflow_compatible:
# trans.response.status = 500
# return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name }
return message
def get_default_history_by_trans( self, trans, create=False ):
@@ -2831,9 +2826,9 @@ class OutputParameterJSONTool( Tool ):
json_params[ 'job_config' ] = dict( GALAXY_DATATYPES_CONF_FILE=param_dict.get( 'GALAXY_DATATYPES_CONF_FILE' ), GALAXY_ROOT_DIR=param_dict.get( 'GALAXY_ROOT_DIR' ), TOOL_PROVIDED_JOB_METADATA_FILE=galaxy.jobs.TOOL_PROVIDED_JOB_METADATA_FILE )
json_filename = None
for i, ( out_name, data ) in enumerate( out_data.iteritems() ):
#use wrapped dataset to access certain values
# use wrapped dataset to access certain values
wrapped_data = param_dict.get( out_name )
#allow multiple files to be created
# allow multiple files to be created
file_name = str( wrapped_data )
extra_files_path = str( wrapped_data.files_path )
data_dict = dict( out_data_name=out_name,
@@ -2883,9 +2878,9 @@ class DataSourceTool( OutputParameterJSONTool ):
json_params[ 'job_config' ] = dict( GALAXY_DATATYPES_CONF_FILE=param_dict.get( 'GALAXY_DATATYPES_CONF_FILE' ), GALAXY_ROOT_DIR=param_dict.get( 'GALAXY_ROOT_DIR' ), TOOL_PROVIDED_JOB_METADATA_FILE=galaxy.jobs.TOOL_PROVIDED_JOB_METADATA_FILE )
json_filename = None
for i, ( out_name, data ) in enumerate( out_data.iteritems() ):
#use wrapped dataset to access certain values
# use wrapped dataset to access certain values
wrapped_data = param_dict.get( out_name )
#allow multiple files to be created
# allow multiple files to be created
cur_base_param_name = 'GALAXY|%s|' % out_name
cur_name = param_dict.get( cur_base_param_name + 'name', name )
cur_dbkey = param_dict.get( cur_base_param_name + 'dkey', dbkey )
@@ -2991,12 +2986,12 @@ class DataManagerTool( OutputParameterJSONTool ):
def exec_after_process( self, app, inp_data, out_data, param_dict, job=None, **kwds ):
assert self.allow_user_access( job.user ), "You must be an admin to access this tool."
#run original exec_after_process
# run original exec_after_process
super( DataManagerTool, self ).exec_after_process( app, inp_data, out_data, param_dict, job=job, **kwds )
#process results of tool
# process results of tool
if job and job.state == job.states.ERROR:
return
#Job state may now be 'running' instead of previous 'error', but datasets are still set to e.g. error
# Job state may now be 'running' instead of previous 'error', but datasets are still set to e.g. error
for dataset in out_data.itervalues():
if dataset.state != dataset.states.OK:
return
@@ -3017,7 +3012,7 @@ class DataManagerTool( OutputParameterJSONTool ):
assert self.allow_user_access( user ), "You must be an admin to access this tool."
history = user.data_manager_histories
if not history:
#create
# create
if create:
history = _create_data_manager_history( user )
else:
+13 -14
View File
@@ -1,8 +1,7 @@
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
from galaxy import model
from galaxy.tools.parameters import DataToolParameter
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter
from galaxy.tools.parameters.wrapped import WrappedParameters
from galaxy.util import ExecutionTimer
from galaxy.util.json import dumps
@@ -51,7 +50,7 @@ class DefaultToolAction( object ):
data = converted_dataset
else:
# FIXME: merge with hda.get_converted_dataset() mode as it's nearly identical.
#run converter here
# run converter here
new_data = data.datatype.convert_dataset( trans, data, target_ext, return_output=True, visible=False ).values()[0]
new_data.hid = data.hid
new_data.name = data.name
@@ -85,12 +84,12 @@ class DefaultToolAction( object ):
if parent:
parent[input.name][i] = input_datasets[ prefix + input.name + str( i + 1 ) ]
for conversion_name, conversion_data in conversions:
#allow explicit conversion to be stored in job_parameter table
# allow explicit conversion to be stored in job_parameter table
parent[ conversion_name ][i] = conversion_data.id # a more robust way to determine JSONable value is desired
else:
param_values[input.name][i] = input_datasets[ prefix + input.name + str( i + 1 ) ]
for conversion_name, conversion_data in conversions:
#allow explicit conversion to be stored in job_parameter table
# allow explicit conversion to be stored in job_parameter table
param_values[ conversion_name ][i] = conversion_data.id # a more robust way to determine JSONable value is desired
else:
input_datasets[ prefix + input.name ] = process_dataset( value )
@@ -107,7 +106,7 @@ class DefaultToolAction( object ):
target_dict = param_values
target_dict[ input.name ] = input_datasets[ prefix + input.name ]
for conversion_name, conversion_data in conversions:
#allow explicit conversion to be stored in job_parameter table
# allow explicit conversion to be stored in job_parameter table
target_dict[ conversion_name ] = conversion_data.id # a more robust way to determine JSONable value is desired
elif isinstance( input, DataCollectionToolParameter ):
if not value:
@@ -121,7 +120,7 @@ class DefaultToolAction( object ):
# some point and figure out if implicitly converting a
# dataset collection makes senese.
#if i == 0:
# if i == 0:
# # Allow copying metadata to output, first item will be source.
# input_datasets[ prefix + input.name ] = data.dataset_instance
input_datasets[ prefix + input.name + str( i + 1 ) ] = data
@@ -229,8 +228,8 @@ class DefaultToolAction( object ):
if output.parent:
parent_to_child_pairs.append( ( output.parent, name ) )
child_dataset_names.add( name )
## What is the following hack for? Need to document under what
## conditions can the following occur? (james@bx.psu.edu)
# What is the following hack for? Need to document under what
# conditions can the following occur? (james@bx.psu.edu)
# HACK: the output data has already been created
# this happens i.e. as a result of the async controller
if name in incoming:
@@ -276,7 +275,7 @@ class DefaultToolAction( object ):
# Store output
out_data[ name ] = data
if output.actions:
#Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
# Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
output_action_params = dict( out_data )
output_action_params.update( incoming )
output.actions.apply_action( data, output_action_params )
@@ -386,7 +385,7 @@ class DefaultToolAction( object ):
for name, dataset in inp_data.iteritems():
if dataset:
if not trans.app.security_agent.can_access_dataset( current_user_roles, dataset.dataset ):
raise "User does not have permission to use a dataset (%s) provided for input." % data.id
raise Exception("User does not have permission to use a dataset (%s) provided for input." % data.id)
job.add_input_dataset( name, dataset )
else:
job.add_input_dataset( name, None )
@@ -434,7 +433,7 @@ class DefaultToolAction( object ):
trans.sa_session.add(jtid)
jtod.dataset.visible = False
trans.sa_session.add(jtod)
except Exception, e:
except Exception:
log.exception('Cannot remap rerun dependencies.')
trans.sa_session.flush()
# Some tools are not really executable, but jobs are still created for them ( for record keeping ).
@@ -554,7 +553,7 @@ def determine_output_format(output, parameter_context, input_datasets, random_in
except Exception:
pass
#process change_format tags
# process change_format tags
if output.change_format is not None:
new_format_set = False
for change_elem in output.change_format:
@@ -563,7 +562,7 @@ def determine_output_format(output, parameter_context, input_datasets, random_in
if check is not None:
try:
if '$' not in check:
#allow a simple name or more complex specifications
# allow a simple name or more complex specifications
check = '${%s}' % check
if str( fill_template( check, context=parameter_context ) ) == when_elem.get( 'value', None ):
ext = when_elem.get( 'format', ext )
+1
View File
@@ -3,6 +3,7 @@ from __init__ import DefaultToolAction
import logging
log = logging.getLogger( __name__ )
class DataManagerToolAction( DefaultToolAction ):
"""Tool action used for Data Manager Tools"""
+1
View File
@@ -3,6 +3,7 @@ from __init__ import DefaultToolAction
import logging
log = logging.getLogger( __name__ )
class DataSourceToolAction( DefaultToolAction ):
"""Tool action used for Data Source Tools"""
+1 -1
View File
@@ -2,7 +2,7 @@ import os
import tempfile
from galaxy.tools.actions import ToolAction
from galaxy.tools.imp_exp import JobExportHistoryArchiveWrapper, JobImportHistoryArchiveWrapper
from galaxy.tools.imp_exp import JobExportHistoryArchiveWrapper
from galaxy.util.odict import odict
import logging
+21 -22
View File
@@ -6,7 +6,6 @@ from galaxy.util.odict import odict
from galaxy.util.json import dumps
from galaxy.jobs.datasets import DatasetPath
log = logging.getLogger( __name__ )
@@ -25,7 +24,7 @@ class SetMetadataToolAction( ToolAction ):
return job, odict
def execute_via_app( self, tool, app, session_id, history_id, user=None,
incoming = {}, set_output_hid = False, overwrite = True,
incoming={}, set_output_hid=False, overwrite=True,
history=None, job_params=None ):
"""
Execute using application.
@@ -55,18 +54,18 @@ class SetMetadataToolAction( ToolAction ):
job.user_id = user.id
if job_params:
job.params = dumps( job_params )
start_job_state = job.state #should be job.states.NEW
start_job_state = job.state # should be job.states.NEW
try:
# For backward compatibility, some tools may not have versions yet.
job.tool_version = tool.version
except:
job.tool_version = "1.0.1"
job.state = job.states.WAITING #we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters
job.state = job.states.WAITING # we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters
job.set_handler(tool.get_job_handler( job_params ))
sa_session.add( job )
sa_session.flush() #ensure job.id is available
sa_session.flush() # ensure job.id is available
#add parameters to job_parameter table
# add parameters to job_parameter table
# Store original dataset state, so we can restore it. A separate table might be better (no chance of 'losing' the original state)?
incoming[ '__ORIGINAL_DATASET_STATE__' ] = dataset.state
input_paths = [DatasetPath( dataset.id, real_path=dataset.file_name, mutable=False )]
@@ -75,36 +74,36 @@ class SetMetadataToolAction( ToolAction ):
external_metadata_wrapper = JobExternalOutputMetadataWrapper( job )
cmd_line = external_metadata_wrapper.setup_external_metadata( dataset,
sa_session,
exec_dir = None,
tmp_dir = job_working_dir,
dataset_files_path = app.model.Dataset.file_path,
output_fnames = input_paths,
config_root = app.config.root,
config_file = app.config.config_file,
datatypes_config = app.datatypes_registry.integrated_datatypes_configs,
job_metadata = None,
include_command = False,
kwds = { 'overwrite' : overwrite } )
exec_dir=None,
tmp_dir=job_working_dir,
dataset_files_path=app.model.Dataset.file_path,
output_fnames=input_paths,
config_root=app.config.root,
config_file=app.config.config_file,
datatypes_config=app.datatypes_registry.integrated_datatypes_configs,
job_metadata=None,
include_command=False,
kwds={ 'overwrite' : overwrite } )
incoming[ '__SET_EXTERNAL_METADATA_COMMAND_LINE__' ] = cmd_line
for name, value in tool.params_to_strings( incoming, app ).iteritems():
job.add_parameter( name, value )
#add the dataset to job_to_input_dataset table
# add the dataset to job_to_input_dataset table
if type == 'hda':
job.add_input_dataset( dataset_name, dataset )
elif type == 'ldda':
job.add_input_library_dataset( dataset_name, dataset )
#Need a special state here to show that metadata is being set and also allow the job to run
# i.e. if state was set to 'running' the set metadata job would never run, as it would wait for input (the dataset to set metadata on) to be in a ready state
# Need a special state here to show that metadata is being set and also allow the job to run
# i.e. if state was set to 'running' the set metadata job would never run, as it would wait for input (the dataset to set metadata on) to be in a ready state
dataset._state = dataset.states.SETTING_METADATA
job.state = start_job_state #job inputs have been configured, restore initial job state
job.state = start_job_state # job inputs have been configured, restore initial job state
sa_session.flush()
# Queue the job for execution
app.job_queue.put( job.id, tool.id )
# FIXME: need to add event logging to app and log events there rather than trans.
#trans.log_event( "Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id )
# trans.log_event( "Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id )
#clear e.g. converted files
# clear e.g. converted files
dataset.datatype.before_setting_metadata( dataset )
return job, odict()
@@ -6,7 +6,6 @@ import subprocess
from cgi import FieldStorage
from galaxy import datatypes, util
from galaxy.util.odict import odict
from galaxy.datatypes import sniff
from galaxy.util.json import dumps
from galaxy.model.orm import eagerload_all
from galaxy.exceptions import ObjectInvalid
+20 -22
View File
@@ -139,7 +139,7 @@ class ToolDataTableManager( object ):
if not remove_elems:
remove_elems = []
full_path = os.path.abspath( shed_tool_data_table_config )
#FIXME: we should lock changing this file by other threads / head nodes
# FIXME: we should lock changing this file by other threads / head nodes
try:
tree = util.parse_xml( full_path )
root = tree.getroot()
@@ -148,10 +148,10 @@ class ToolDataTableManager( object ):
out_elems = []
log.debug( 'Could not parse existing tool data table config, assume no existing elements: %s', e )
for elem in remove_elems:
#handle multiple occurrences of remove elem in existing elems
# handle multiple occurrences of remove elem in existing elems
while elem in out_elems:
remove_elems.remove( elem )
#add new elems
# add new elems
out_elems.extend( new_elems )
with open( full_path, 'wb' ) as out:
out.write( '<?xml version="1.0"?>\n<tables>\n' )
@@ -273,7 +273,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
# Configure columns
self.parse_column_spec( config_element )
#store repo info if available:
# store repo info if available:
repo_elem = config_element.find( 'tool_shed_repository' )
if repo_elem is not None:
repo_info = dict( tool_shed=repo_elem.find( 'tool_shed' ).text, name=repo_elem.find( 'repository_name' ).text,
@@ -303,7 +303,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
log.debug( "Encountered a file element (%s) that does not contain a path value when loading tool data table '%s'.", util.xml_to_string( file_element ), self.name )
continue
#FIXME: splitting on and merging paths from a configuration file when loading is wonky
# FIXME: splitting on and merging paths from a configuration file when loading is wonky
# Data should exist on disk in the state needed, i.e. the xml configuration should
# point directly to the desired file to load. Munging of the tool_data_tables_conf.xml.sample
# can be done during installing / testing / metadata resetting with the creation of a proper
@@ -349,18 +349,18 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
def merge_tool_data_table( self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ):
assert self.columns == other_table.columns, "Merging tabular data tables with non matching columns is not allowed: %s:%s != %s:%s" % ( self.name, self.columns, other_table.name, other_table.columns )
#merge filename info
# merge filename info
for filename, info in other_table.filenames.iteritems():
if filename not in self.filenames:
self.filenames[ filename ] = info
#save info about table
# save info about table
self._merged_load_info.append( ( other_table.__class__, other_table._load_info ) )
# If we are merging in a data table that does not allow duplicates, enforce that upon the data table
if self.allow_duplicate_entries and not other_table.allow_duplicate_entries:
log.debug( 'While attempting to merge tool data table "%s", the other instance of the table specified that duplicate entries are not allowed, now deduplicating all previous entries.', self.name )
self.allow_duplicate_entries = False
self._deduplicate_data()
#add data entries and return current data table version
# add data entries and return current data table version
return self.add_entries( other_table.data, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd )
def handle_found_index_file( self, filename ):
@@ -469,7 +469,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
if not found_column:
rval.append( name )
elif name == 'value':
#the column named 'value' always has priority over other named columns
# the column named 'value' always has priority over other named columns
rval[ -1 ] = name
found_column = True
if not found_column:
@@ -513,7 +513,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
def get_filename_for_source( self, source, default=None ):
if source:
#if dict, assume is compatible info dict, otherwise call method
# if dict, assume is compatible info dict, otherwise call method
if isinstance( source, dict ):
source_repo_info = source
else:
@@ -529,7 +529,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
return filename
def _add_entry( self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ):
#accepts dict or list of columns
# accepts dict or list of columns
if isinstance( entry, dict ):
fields = []
for column_name in self.get_column_name_list():
@@ -557,11 +557,11 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
if persist and ( not is_error or persist_on_error ):
filename = self.get_filename_for_source( entry_source )
if filename is None:
#should we default to using any filename here instead?
# should we default to using any filename here instead?
log.error( "Unable to determine filename for persisting data table '%s' values: '%s'.", self.name, fields )
is_error = True
else:
#FIXME: Need to lock these files for editing
# FIXME: Need to lock these files for editing
log.debug( "Persisting changes to file: %s", filename )
try:
data_table_fh = open( filename, 'r+b' )
@@ -587,18 +587,16 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
self.filter_file_fields( filename, values )
else:
log.warn( "Cannot find index file '%s' for tool data table '%s'" % ( filename, self.name ) )
self.reload_from_files()
def filter_file_fields( self, loc_file, values ):
"""
Reads separated lines from file and print back only the lines that pass a filter.
"""
separator_char = (lambda c: '<TAB>' if c == '\t' else c)(self.separator)
with open(loc_file) as reader:
rval = ""
for i, line in enumerate( reader ):
for line in reader:
if line.lstrip().startswith( self.comment_char ):
rval += line
else:
@@ -607,16 +605,16 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
fields = line_s.split( self.separator )
if fields != values:
rval += line
with open(loc_file, 'wb') as writer:
writer.write(rval)
return rval
def _replace_field_separators( self, fields, separator=None, replace=None, comment_char=None ):
#make sure none of the fields contain separator
#make sure separator replace is different from comment_char,
#due to possible leading replace
# make sure none of the fields contain separator
# make sure separator replace is different from comment_char,
# due to possible leading replace
if separator is None:
separator = self.separator
if replace is None:
+22 -22
View File
@@ -10,7 +10,7 @@ from tool_shed.util import common_util
import tool_shed.util.shed_util_common as suc
import galaxy.queue_worker
#set up logger
# set up logger
import logging
log = logging.getLogger( __name__ )
@@ -38,7 +38,7 @@ class DataManagers( object ):
tree = util.parse_xml( xml_filename )
except Exception, e:
log.error( 'There was an error parsing your Data Manager config file "%s": %s' % ( xml_filename, e ) )
return #we are not able to load any data managers
return # we are not able to load any data managers
root = tree.getroot()
if root.tag != 'data_managers':
log.error( 'A data managers configuration must have a "data_managers" tag as the root. "%s" is present' % ( root.tag ) )
@@ -87,10 +87,10 @@ class DataManagers( object ):
data_manager = self.get_manager( manager_id, None )
if data_manager is not None:
del self.data_managers[ manager_id ]
#remove tool from toolbox
# remove tool from toolbox
if data_manager.tool:
self.app.toolbox.remove_tool_by_id( data_manager.tool.id )
#determine if any data_tables are no longer tracked
# determine if any data_tables are no longer tracked
for data_table_name in data_manager.data_tables.keys():
remove_data_table_tracking = True
for other_data_manager in self.data_managers.itervalues():
@@ -135,7 +135,7 @@ class DataManager( object ):
assert tool_elem is not None, "Error loading tool for data manager. Make sure that a tool_file attribute or a tool tag set has been defined:\n%s" % ( util.xml_to_string( elem ) )
path = tool_elem.get( "file", None )
tool_guid = tool_elem.get( "guid", None )
#need to determine repository info so that dependencies will work correctly
# need to determine repository info so that dependencies will work correctly
tool_shed_url = tool_elem.find( 'tool_shed' ).text
# Handle protocol changes.
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( self.data_managers.app, tool_shed_url )
@@ -156,7 +156,7 @@ class DataManager( object ):
installed_changeset_revision )
if tool_shed_repository is None:
log.warning( 'Could not determine tool shed repository from database. This should only ever happen when running tests.' )
#we'll set tool_path manually here from shed_conf_file
# we'll set tool_path manually here from shed_conf_file
tool_shed_repository_id = None
try:
tool_path = util.parse_xml( elem.get( 'shed_conf_file' ) ).getroot().get( 'tool_path', tool_path )
@@ -164,7 +164,7 @@ class DataManager( object ):
log.error( 'Error determining tool_path for Data Manager during testing: %s', e )
else:
tool_shed_repository_id = self.data_managers.app.security.encode_id( tool_shed_repository.id )
#use shed_conf_file to determine tool_path
# use shed_conf_file to determine tool_path
shed_conf_file = elem.get( "shed_conf_file", None )
if shed_conf_file:
shed_conf = self.data_managers.app.toolbox.get_shed_config_dict_by_filename( shed_conf_file, None )
@@ -183,7 +183,7 @@ class DataManager( object ):
data_table_name = data_table_elem.get( "name" )
assert data_table_name is not None, "A name is required for a data table entry"
if data_table_name not in self.data_tables:
self.data_tables[ data_table_name ] = odict()#{}
self.data_tables[ data_table_name ] = odict()
output_elem = data_table_elem.find( 'output' )
if output_elem is not None:
for column_elem in output_elem.findall( 'column' ):
@@ -217,7 +217,7 @@ class DataManager( object ):
for move_elem in column_elem.findall( 'move' ):
move_type = move_elem.get( 'type', 'directory' )
relativize_symlinks = move_elem.get( 'relativize_symlinks', False ) #TODO: should we instead always relativize links?
relativize_symlinks = move_elem.get( 'relativize_symlinks', False ) # TODO: should we instead always relativize links?
source_elem = move_elem.find( 'source' )
if source_elem is None:
source_base = None
@@ -244,7 +244,7 @@ class DataManager( object ):
@property
def id( self ):
return self.guid or self.declared_id #if we have a guid, we will use that as the data_manager id
return self.guid or self.declared_id # if we have a guid, we will use that as the data_manager id
def load_tool( self, tool_filename, guid=None, data_manager_id=None, tool_shed_repository_id=None ):
toolbox = self.data_managers.app.toolbox
@@ -259,12 +259,12 @@ class DataManager( object ):
def process_result( self, out_data ):
data_manager_dicts = {}
data_manager_dict = {}
#TODO: fix this merging below
# TODO: fix this merging below
for output_name, output_dataset in out_data.iteritems():
try:
output_dict = json.loads( open( output_dataset.file_name ).read() )
except Exception, e:
log.warning( 'Error reading DataManagerTool json for "%s": %s' % ( output_name, e ) )
log.warning( 'Error reading DataManagerTool json for "%s": %s' % ( output_name, e ) )
continue
data_manager_dicts[ output_name ] = output_dict
for key, value in output_dict.iteritems():
@@ -274,18 +274,18 @@ class DataManager( object ):
data_manager_dict.update( output_dict )
data_tables_dict = data_manager_dict.get( 'data_tables', {} )
for data_table_name, data_table_columns in self.data_tables.iteritems():
for data_table_name in self.data_tables.iterkeys():
data_table_values = data_tables_dict.pop( data_table_name, None )
if not data_table_values:
log.warning( 'No values for data table "%s" were returned by the data manager "%s".' % ( data_table_name, self.id ) )
continue #next data table
continue # next data table
data_table = self.data_managers.app.tool_data_tables.get( data_table_name, None )
if data_table is None:
log.error( 'The data manager "%s" returned an unknown data table "%s" with new entries "%s". These entries will not be created. Please confirm that an entry for "%s" exists in your "%s" file.' % ( self.id, data_table_name, data_table_values, data_table_name, 'tool_data_table_conf.xml' ) )
continue #next table name
continue # next table name
if not isinstance( data_table, SUPPORTED_DATA_TABLE_TYPES ):
log.error( 'The data manager "%s" returned an unsupported data table "%s" with type "%s" with new entries "%s". These entries will not be created. Please confirm that the data table is of a supported type (%s).' % ( self.id, data_table_name, type( data_table ), data_table_values, SUPPORTED_DATA_TABLE_TYPES ) )
continue #next table name
continue # next table name
output_ref_values = {}
if data_table_name in self.output_ref_by_data_table:
for data_table_column, output_ref in self.output_ref_by_data_table[ data_table_name ].iteritems():
@@ -296,10 +296,10 @@ class DataManager( object ):
if not isinstance( data_table_values, list ):
data_table_values = [ data_table_values ]
for data_table_row in data_table_values:
data_table_value = dict( **data_table_row ) #keep original values here
for name, value in data_table_row.iteritems(): #FIXME: need to loop through here based upon order listed in data_manager config
data_table_value = dict( **data_table_row ) # keep original values here
for name, value in data_table_row.iteritems(): # FIXME: need to loop through here based upon order listed in data_manager config
if name in output_ref_values:
moved = self.process_move( data_table_name, name, output_ref_values[ name ].extra_files_path, **data_table_value )
self.process_move( data_table_name, name, output_ref_values[ name ].extra_files_path, **data_table_value )
data_table_value[ name ] = self.process_value_translation( data_table_name, name, **data_table_value )
data_table.add_entry( data_table_value, persist=True, entry_source=self )
galaxy.queue_worker.send_control_task(self.data_managers.app, 'reload_tool_data_tables',
@@ -317,7 +317,7 @@ class DataManager( object ):
if not isinstance( data_table_values, list ):
data_table_values = [ data_table_values ]
for data_table_row in data_table_values:
data_table_value = dict( **data_table_row ) #keep original values here
data_table_value = dict( **data_table_row ) # keep original values here
for name, value in data_table_row.iteritems():
if name in path_column_names:
data_table_value[ name ] = os.path.abspath( os.path.join( self.data_managers.app.config.galaxy_data_manager_data_path, value ) )
@@ -350,13 +350,13 @@ class DataManager( object ):
target = os.path.join( target, fill_template( move_dict[ 'target_value' ], GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) )
if move_dict[ 'type' ] == 'file':
dirs, filename = os.path.split( target )
dirs = os.path.split( target )[0]
try:
os.makedirs( dirs )
except OSError, e:
if e.errno != errno.EEXIST:
raise e
#moving a directory and the target already exists, we move the contents instead
# moving a directory and the target already exists, we move the contents instead
util.move_merge( source, target )
if move_dict.get( 'relativize_symlinks', False ):
+1 -1
View File
@@ -438,7 +438,7 @@ def execute(cmds, env=None):
if env:
subprocess_kwds["env"] = env
p = subprocess.Popen(cmds, **subprocess_kwds)
#log = p.stdout.read()
# log = p.stdout.read()
global VERBOSE
stdout, stderr = p.communicate()
if p.returncode != 0:
-1
View File
@@ -37,4 +37,3 @@ def requirements_to_recipe_contexts(requirements, brew_context):
brew_context
)
return map(to_recipe_context, requirements_to_recipes(requirements))
@@ -11,11 +11,11 @@ class GalaxyPackageDependencyResolver(DependencyResolver, UsesToolDependencyDirM
resolver_type = "galaxy_packages"
def __init__(self, dependency_manager, **kwds):
## Galaxy tool shed requires explicit versions on XML elements,
## this in inconvient for testing or Galaxy instances not utilizing
## the tool shed so allow a fallback version of the Galaxy package
## resolver that will just grab 'default' version of exact version
## unavailable.
# Galaxy tool shed requires explicit versions on XML elements,
# this in inconvient for testing or Galaxy instances not utilizing
# the tool shed so allow a fallback version of the Galaxy package
# resolver that will just grab 'default' version of exact version
# unavailable.
self.versionless = str(kwds.get('versionless', "false")).lower() == "true"
self._init_base_path( dependency_manager, **kwds )
+1 -2
View File
@@ -70,7 +70,6 @@ class DirectoryModuleChecker(object):
self.directories = modulepath.split(pathsep)
if prefetch:
log.warn("Created module dependency resolver with prefetch enabled, but directory module checker does not support this.")
pass
def has_module(self, module, version):
has_module = False
@@ -107,7 +106,7 @@ class AvailModuleChecker(object):
for module_name, module_version in module_generator:
names_match = module == module_name
module_match = names_match and (version == None or module_version == version)
module_match = names_match and (version is None or module_version == version)
if module_match:
return True
return False
+30 -30
View File
@@ -164,11 +164,11 @@ class ToolEvaluator( object ):
tool=self.tool,
name=input.name )
elif isinstance( input, DataToolParameter ):
## FIXME: We're populating param_dict with conversions when
## wrapping values, this should happen as a separate
## step before wrapping (or call this wrapping step
## something more generic) (but iterating this same
## list twice would be wasteful)
# FIXME: We're populating param_dict with conversions when
# wrapping values, this should happen as a separate
# step before wrapping (or call this wrapping step
# something more generic) (but iterating this same
# list twice would be wasteful)
# Add explicit conversions by name to current parent
for conversion_name, conversion_extensions, conversion_datatypes in input.conversions:
# If we are at building cmdline step, then converters
@@ -205,7 +205,7 @@ class ToolEvaluator( object ):
if identifier_key in param_dict:
wrapper_kwds["identifier"] = param_dict[identifier_key]
if dataset:
#A None dataset does not have a filename
# A None dataset does not have a filename
real_path = dataset.file_name
if real_path in input_dataset_paths:
wrapper_kwds[ "dataset_path" ] = input_dataset_paths[ real_path ]
@@ -240,16 +240,16 @@ class ToolEvaluator( object ):
def __populate_input_dataset_wrappers(self, param_dict, input_datasets, input_dataset_paths):
# TODO: Update this method for dataset collections? Need to test. -John.
## FIXME: when self.check_values==True, input datasets are being wrapped
## twice (above and below, creating 2 separate
## DatasetFilenameWrapper objects - first is overwritten by
## second), is this necessary? - if we get rid of this way to
## access children, can we stop this redundancy, or is there
## another reason for this?
## - Only necessary when self.check_values is False (==external dataset
## tool?: can this be abstracted out as part of being a datasouce tool?)
## - But we still want (ALWAYS) to wrap input datasets (this should be
## checked to prevent overhead of creating a new object?)
# FIXME: when self.check_values==True, input datasets are being wrapped
# twice (above and below, creating 2 separate
# DatasetFilenameWrapper objects - first is overwritten by
# second), is this necessary? - if we get rid of this way to
# access children, can we stop this redundancy, or is there
# another reason for this?
# - Only necessary when self.check_values is False (==external dataset
# tool?: can this be abstracted out as part of being a datasouce tool?)
# - But we still want (ALWAYS) to wrap input datasets (this should be
# checked to prevent overhead of creating a new object?)
# Additionally, datasets go in the param dict. We wrap them such that
# if the bare variable name is used it returns the filename (for
# backwards compatibility). We also add any child datasets to the
@@ -281,9 +281,9 @@ class ToolEvaluator( object ):
for name, out_collection in output_collections.items():
if name not in tool.output_collections:
continue
#message_template = "Name [%s] not found in tool.output_collections %s"
#message = message_template % ( name, tool.output_collections )
#raise AssertionError( message )
# message_template = "Name [%s] not found in tool.output_collections %s"
# message = message_template % ( name, tool.output_collections )
# raise AssertionError( message )
wrapper_kwds = dict(
datatypes_registry=self.app.datatypes_registry,
@@ -371,7 +371,7 @@ class ToolEvaluator( object ):
if not self.tool.check_values and self.unstructured_path_rewriter:
# The tools weren't "wrapped" yet, but need to be in order to get
#the paths rewritten.
# the paths rewritten.
self.__walk_inputs( self.tool.inputs, param_dict, rewrite_unstructured_paths )
def __sanitize_param_dict( self, param_dict ):
@@ -379,7 +379,7 @@ class ToolEvaluator( object ):
Sanitize all values that will be substituted on the command line, with the exception of ToolParameterValueWrappers,
which already have their own specific sanitization rules and also exclude special-cased named values.
We will only examine the first level for values to skip; the wrapping function will recurse as necessary.
Note: this method follows the style of the similar populate calls, in that param_dict is modified in-place.
"""
# chromInfo is a filename, do not sanitize it.
@@ -404,22 +404,22 @@ class ToolEvaluator( object ):
try:
self.__build_config_files( )
except Exception, e:
#capture and log parsing errors
# capture and log parsing errors
global_tool_errors.add_error(self.tool.config_file, "Building Config Files", e)
raise e
raise e
try:
self.__build_param_file( )
except Exception, e:
#capture and log parsing errors
# capture and log parsing errors
global_tool_errors.add_error(self.tool.config_file, "Building Param File", e)
raise e
try:
self.__build_command_line( )
except Exception, e:
#capture and log parsing errors
# capture and log parsing errors
global_tool_errors.add_error(self.tool.config_file, "Building Command Line", e)
raise e
return self.command_line, self.extra_filenames
def __build_command_line( self ):
@@ -444,7 +444,7 @@ class ToolEvaluator( object ):
command_line = command_line.replace( "\n", " " ).replace( "\r", " " ).strip()
except Exception:
# Modify exception message to be more clear
#e.args = ( 'Error substituting into command line. Params: %r, Command: %s' % ( param_dict, self.command ), )
# e.args = ( 'Error substituting into command line. Params: %r, Command: %s' % ( param_dict, self.command ), )
raise
if interpreter:
# TODO: path munging for cluster/dataset server relocatability
@@ -469,7 +469,7 @@ class ToolEvaluator( object ):
else:
fd, config_filename = tempfile.mkstemp( dir=directory )
os.close( fd )
f = open( config_filename, "wt" )
f = open( config_filename, "w" )
f.write( fill_template( template_text, context=param_dict ) )
f.close()
# For running jobs as the actual user, ensure the config file is globally readable
@@ -488,10 +488,10 @@ class ToolEvaluator( object ):
if command and "$param_file" in command:
fd, param_filename = tempfile.mkstemp( dir=directory )
os.close( fd )
f = open( param_filename, "wt" )
f = open( param_filename, "w" )
for key, value in param_dict.items():
# parameters can be strings or lists of strings, coerce to list
if type(value) != type([]):
if not isinstance(value, list):
value = [ value ]
for elem in value:
f.write( '%s=%s\n' % (key, elem) )
+5
View File
@@ -5,17 +5,21 @@ FIXME: These are used by tool scripts, not the framework, and should not live
in this package.
"""
class UCSCLimitException( Exception ):
pass
class UCSCOutWrapper( object ):
"""File-like object that throws an exception if it encounters the UCSC limit error lines"""
def __init__( self, other ):
self.other = iter( other )
# Need one line of lookahead to be sure we are hitting the limit message
self.lookahead = None
def __iter__( self ):
return self
def next( self ):
if self.lookahead is None:
line = self.other.next()
@@ -29,5 +33,6 @@ class UCSCOutWrapper( object ):
else:
self.lookahead = next_line
return line
def readline(self):
return self.next()
+1 -3
View File
@@ -88,9 +88,7 @@ class ToolExecutionTracker( object ):
# collection replaced with a specific dataset. Need to replace this
# with the collection and wrap everything up so can evaluate output
# label.
params.update( self.collection_info.collections ) # Replace datasets
# with source collections
# for labelling outputs.
params.update( self.collection_info.collections ) # Replace datasets with source collections for labelling outputs.
collection_names = map( lambda c: "collection %d" % c.hid, collections )
on_text = on_text_for_names( collection_names )
@@ -11,7 +11,6 @@ import os
import sys
import tarfile
from galaxy import eggs
from galaxy.util.json import dumps, loads
-1
View File
@@ -24,4 +24,3 @@ def lint_citations(tool_xml, lint_ctx):
if valid_citations > 0:
lint_ctx.valid("Found %d likely valid citations.", valid_citations)
+6 -6
View File
@@ -66,8 +66,8 @@ def _macros_of_type(root, type, el_func):
macro_dict = {}
if macros_el is not None:
macro_els = macros_el.findall('macro')
macro_dict = dict([(macro_el.get("name"), el_func(macro_el)) \
for macro_el in macro_els \
macro_dict = dict([(macro_el.get("name"), el_func(macro_el))
for macro_el in macro_els
if macro_el.get('type') == type])
return macro_dict
@@ -80,11 +80,11 @@ def _expand_tokens(elements, tokens):
value = element.text
if value:
new_value = _expand_tokens_str(element.text, tokens)
if not (new_value is value):
if new_value is not value:
element.text = new_value
for key, value in element.attrib.iteritems():
new_value = _expand_tokens_str(value, tokens)
if not (new_value is value):
if new_value is not value:
element.attrib[key] = new_value
_expand_tokens(list(element), tokens)
@@ -211,10 +211,10 @@ def _xml_set_children(element, new_children):
def _xml_replace(query, targets, parent_map):
#parent_el = query.find('..') ## Something like this would be better with newer xml library
# 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.iter('.')): ## Something like this for newer implementation
for index, el in enumerate(list(parent_el)):
if el == query:
matching_index = index
+4 -4
View File
@@ -2,9 +2,9 @@
Classes encapsulating Galaxy tool parameters.
"""
from basic import *
from grouping import *
from galaxy.util.json import *
from basic import DataCollectionToolParameter, DataToolParameter, SelectToolParameter
from grouping import Conditional, Repeat, Section, UploadDataset
from galaxy.util.json import dumps, json_fix, loads
def visit_input_values( inputs, input_values, callback, name_prefix="", label_prefix="" ):
@@ -119,7 +119,7 @@ def params_to_incoming( incoming, inputs, input_values, app, name_prefix="", to_
"""
for input in inputs.itervalues():
if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
for i, d in enumerate( input_values[ input.name ] ):
for d in input_values[ input.name ]:
index = d['__index__']
new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
params_to_incoming( incoming, input.inputs, d, app, new_name_prefix, to_html=to_html)
+41 -44
View File
@@ -3,22 +3,18 @@ Basic tool parameters.
"""
import logging
import string
import sys
import os
import os.path
import urllib
from xml.etree.ElementTree import XML
from galaxy import config, datatypes, util
from galaxy import util
from galaxy.web import form_builder
from galaxy.util.bunch import Bunch
from galaxy.util import string_as_bool, sanitize_param, unicodify
from galaxy.util import listify
from galaxy.util.odict import odict
from galaxy.util.expressions import ExpressionContext
from sanitize import ToolParameterSanitizer
import validation
import dynamic_options
import galaxy.tools.parser
from ..parser import get_input_source as ensure_input_source
from ..parameters import history_query
@@ -209,11 +205,11 @@ class ToolParameter( object, Dictifiable ):
def to_dict( self, trans, view='collection', value_mapper=None, other_values={} ):
""" to_dict tool parameter. This can be overridden by subclasses. """
tool_dict = super( ToolParameter, self ).to_dict()
#TODO: wrapping html as it causes a lot of errors on subclasses - needs histories, etc.
# TODO: wrapping html as it causes a lot of errors on subclasses - needs histories, etc.
try:
tool_dict[ 'html' ] = urllib.quote( util.smart_str( self.get_html( trans ) ) )
except AssertionError, e:
pass #HACK for assert trans.history, 'requires a history'
except AssertionError:
pass # HACK for assert trans.history, 'requires a history'
tool_dict[ 'model_class' ] = self.__class__.__name__
tool_dict[ 'optional' ] = self.optional
@@ -296,6 +292,7 @@ class TextToolParameter( ToolParameter ):
d['size'] = self.size
return d
class IntegerToolParameter( TextToolParameter ):
"""
Parameter that takes an integer value.
@@ -594,8 +591,8 @@ class FTPFileToolParameter( ToolParameter ):
self.user_ftp_dir = ''
def get_initial_value( self, trans, context, history=None ):
if not trans is None:
if not trans.user is None:
if trans is not None:
if trans.user is not None:
self.user_ftp_dir = "%s/" % trans.user_ftp_dir
return None
@@ -652,6 +649,7 @@ class FTPFileToolParameter( ToolParameter ):
d['multiple'] = self.multiple
return d
class HiddenToolParameter( ToolParameter ):
"""
Parameter that takes one of two values.
@@ -698,7 +696,7 @@ class ColorToolParameter( ToolParameter ):
return form_builder.HiddenField( self.name, self.value )
def get_initial_value( self, trans, context, history=None ):
return self.value.lower();
return self.value.lower()
class BaseURLToolParameter( HiddenToolParameter ):
@@ -1188,7 +1186,7 @@ class ColumnListParameter( SelectToolParameter ):
removes the 'c' when entered into a workflow.
"""
if self.multiple:
#split on newline and ,
# split on newline and ,
if isinstance( value, list ) or isinstance( value, basestring ):
column_list = []
if not isinstance( value, list ):
@@ -1271,28 +1269,27 @@ class ColumnListParameter( SelectToolParameter ):
""" show column labels rather than c1..cn if use_header_names=True
"""
options = []
colnames = None
if self.usecolnames: # read first row - assume is a header with metadata useful for making good choices
if self.usecolnames: # read first row - assume is a header with metadata useful for making good choices
assert self.data_ref in other_values, "Value for associated DataToolParameter not found"
dataset = other_values[ self.data_ref ]
try:
head = open(dataset.get_file_name(),'r').readline()
head = open(dataset.get_file_name(), 'r').readline()
cnames = head.rstrip().split('\t')
column_list = [('%d' % (i+1),'c%d: %s' % (i+1,x)) for i,x in enumerate(cnames)]
if self.numerical: # If numerical was requested, filter columns based on metadata
column_list = [('%d' % (i + 1), 'c%d: %s' % (i + 1, x)) for i, x in enumerate(cnames)]
if self.numerical: # If numerical was requested, filter columns based on metadata
if len(dataset.metadata.column_types) >= len(cnames):
numerics = [i for i,x in enumerate(dataset.metadata.column_types) if x == 'int' or x == 'float']
numerics = [i for i, x in enumerate(dataset.metadata.column_types) if x == 'int' or x == 'float']
column_list = [column_list[i] for i in numerics]
except:
column_list = self.get_column_list( trans, other_values )
else:
column_list = self.get_column_list( trans, other_values )
column_list = self.get_column_list( trans, other_values )
if len( column_list ) > 0 and not self.force_select:
options.append( ('?', 'None', False) )
for col in column_list:
if col != 'None':
if type(col) == type(()) and len(col) == 2: # fiddled
options.append((col[1],col[0],False))
if isinstance(col, tuple) and len(col) == 2: # fiddled
options.append((col[1], col[0], False))
else:
options.append( ( 'Column: ' + col, col, False ) )
return options
@@ -1455,7 +1452,7 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
if elem.find( 'filter' ):
self.is_dynamic = True
for filter in elem.findall( 'filter' ):
#currently only filtering by metadata key matching input file is allowed
# currently only filtering by metadata key matching input file is allowed
if filter.get( 'type' ) == 'data_meta':
if filter.get( 'data_ref' ) not in self.filtered:
self.filtered[filter.get( 'data_ref' )] = {}
@@ -1585,7 +1582,7 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
options = get_options_list( val )
rval.extend( options )
if len( rval ) > 1:
if not( self.repeat ):
if not self.repeat:
assert self.multiple, "Multiple values provided but parameter is not expecting multiple values"
rval = self.separator.join( map( value_map, rval ) )
if self.tool is None or self.tool.options.sanitize:
@@ -1706,7 +1703,7 @@ class BaseDataToolParameter( ToolParameter ):
# A handle to the transaction (and thus app) will be given by the module.
datatypes_registry = trans.app.datatypes_registry
else:
#This occurs for things such as unit tests
# This occurs for things such as unit tests
import galaxy.datatypes.registry
datatypes_registry = galaxy.datatypes.registry.Registry()
datatypes_registry.load_datatypes()
@@ -1919,7 +1916,7 @@ class DataToolParameter( BaseDataToolParameter ):
most_recent_dataset = []
def dataset_collector( datasets ):
for i, data in enumerate( datasets ):
for data in datasets:
if data.visible and dataset_matcher.hda_accessible( data, check_security=False ):
match = dataset_matcher.valid_hda_match( data, check_security=False )
if not match or dataset_matcher.filter( match.hda ):
@@ -2099,9 +2096,9 @@ class DataToolParameter( BaseDataToolParameter ):
return allow
def _options_filter_attribute( self, value ):
#HACK to get around current hardcoded limitation of when a set of dynamic options is defined for a DataToolParameter
#it always causes available datasets to be filtered by dbkey
#this behavior needs to be entirely reworked (in a backwards compatible manner)
# HACK to get around current hardcoded limitation of when a set of dynamic options is defined for a DataToolParameter
# it always causes available datasets to be filtered by dbkey
# this behavior needs to be entirely reworked (in a backwards compatible manner)
options_filter_attribute = self.options_filter_attribute
if options_filter_attribute is None:
return value.get_dbkey()
@@ -2361,22 +2358,22 @@ class DataCollectionToolParameter( BaseDataToolParameter ):
# append directly matched collections
for hdca in self.match_collections( trans, history, dataset_matcher ):
d['options']['hdca'].append({
'id' : trans.security.encode_id( hdca.id ),
'hid' : hdca.hid,
'name' : hdca.name,
'src' : 'hdca'
})
'id': trans.security.encode_id( hdca.id ),
'hid': hdca.hid,
'name': hdca.name,
'src': 'hdca'
})
# append matching subcollections
for hdca in self.match_multirun_collections( trans, history, dataset_matcher ):
subcollection_type = self._history_query( trans ).collection_type_description.collection_type
d['options']['hdca'].append({
'id' : trans.security.encode_id( hdca.id ),
'hid' : hdca.hid,
'name' : hdca.name,
'src' : 'hdca',
'map_over_type' : subcollection_type
})
'id': trans.security.encode_id( hdca.id ),
'hid': hdca.hid,
'name': hdca.name,
'src': 'hdca',
'map_over_type': subcollection_type
})
# sort both lists
d['options']['hdca'] = sorted(d['options']['hdca'], key=lambda k: k['hid'], reverse=True)
@@ -2438,10 +2435,10 @@ class LibraryDatasetToolParameter( ToolParameter ):
lst = []
for item in value:
encoded_id = encoded_name = None
if isinstance (item, app.model.LibraryDatasetDatasetAssociation):
if isinstance(item, app.model.LibraryDatasetDatasetAssociation):
encoded_id = app.security.encode_id( item.id )
encoded_name = item.name
elif isinstance (item, dict):
elif isinstance(item, dict):
encoded_id = item.get('id')
encoded_name = item.get('name')
else:
@@ -2469,13 +2466,13 @@ class LibraryDatasetToolParameter( ToolParameter ):
value = [value]
lst = []
for item in value:
if isinstance (item, app.model.LibraryDatasetDatasetAssociation):
if isinstance(item, app.model.LibraryDatasetDatasetAssociation):
lst.append(item)
else:
encoded_id = None
if isinstance (item, dict):
if isinstance(item, dict):
encoded_id = item.get('id')
elif isinstance (item, basestring):
elif isinstance(item, basestring):
encoded_id = item
else:
lst = []
@@ -35,7 +35,7 @@ class DatasetMatcher( object ):
accessible to user.
"""
dataset = hda.dataset
state_valid = not dataset.state in INVALID_STATES
state_valid = dataset.state not in INVALID_STATES
return state_valid and ( not check_security or self.__can_access_dataset( dataset ) )
def valid_hda_match( self, hda, check_implicit_conversions=True, check_security=False ):
+58 -28
View File
@@ -3,14 +3,17 @@ Support for generating the options for a SelectToolParameter dynamically (based
on the values of other parameters or other aspects of the current state)
"""
import operator, sys, os, logging
import basic, validation
import logging
import os
import basic
import validation
from galaxy.util import string_as_bool
from galaxy.model import User
import galaxy.tools
log = logging.getLogger(__name__)
class Filter( object ):
"""
A filter takes the current options list and modifies it.
@@ -21,16 +24,20 @@ class Filter( object ):
type = elem.get( 'type', None )
assert type is not None, "Required 'type' attribute missing from filter"
return filter_types[type.strip()]( d_option, elem )
def __init__( self, d_option, elem ):
self.dynamic_option = d_option
self.elem = elem
def get_dependency_name( self ):
"""Returns the name of any depedencies, otherwise None"""
return None
def filter_options( self, options, trans, other_values ):
"""Returns a list of options after the filter is applied"""
raise TypeError( "Abstract Method" )
class StaticValueFilter( Filter ):
"""
Filters a list of options on a column by a static value.
@@ -52,6 +59,7 @@ class StaticValueFilter( Filter ):
assert column is not None, "Required 'column' attribute missing from filter, when loading from file"
self.column = d_option.column_spec_to_index( column )
self.keep = string_as_bool( elem.get( "keep", 'True' ) )
def filter_options( self, options, trans, other_values ):
rval = []
filter_value = self.value
@@ -64,6 +72,7 @@ class StaticValueFilter( Filter ):
rval.append( fields )
return rval
class DataMetaFilter( Filter ):
"""
Filters a list of options on a column by a dataset metadata value.
@@ -99,8 +108,10 @@ class DataMetaFilter( Filter ):
self.column = d_option.column_spec_to_index( self.column )
self.multiple = string_as_bool( elem.get( "multiple", "False" ) )
self.separator = elem.get( "separator", "," )
def get_dependency_name( self ):
return self.ref_name
def filter_options( self, options, trans, other_values ):
def compare_meta_value( file_value, dataset_value ):
if isinstance( dataset_value, list ):
@@ -116,10 +127,10 @@ class DataMetaFilter( Filter ):
return file_value == dataset_value
assert self.ref_name in other_values or ( trans is not None and trans.workflow_building_mode), "Required dependency '%s' not found in incoming values" % self.ref_name
ref = other_values.get( self.ref_name, None )
if not isinstance( ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ) and not ( isinstance( ref, galaxy.tools.wrappers.DatasetFilenameWrapper ) ):
return [] #not a valid dataset
if not isinstance( ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ) and not isinstance( ref, galaxy.tools.wrappers.DatasetFilenameWrapper ):
return [] # not a valid dataset
meta_value = ref.metadata.get( self.key, None )
if meta_value is None: #assert meta_value is not None, "Required metadata value '%s' not found in referenced dataset" % self.key
if meta_value is None: # assert meta_value is not None, "Required metadata value '%s' not found in referenced dataset" % self.key
return [ ( disp_name, basic.UnvalidatedValue( optval ), selected ) for disp_name, optval, selected in options ]
if self.column is not None:
@@ -142,6 +153,7 @@ class DataMetaFilter( Filter ):
options.append( ( value, value, False ) )
return options
class ParamValueFilter( Filter ):
"""
Filters a list of options on a column by the value of another input.
@@ -173,15 +185,18 @@ class ParamValueFilter( Filter ):
self.ref_attribute = self.ref_attribute.split( '.' )
else:
self.ref_attribute = []
def get_dependency_name( self ):
return self.ref_name
def filter_options( self, options, trans, other_values ):
if trans is not None and trans.workflow_building_mode: return []
if trans is not None and trans.workflow_building_mode:
return []
assert self.ref_name in other_values, "Required dependency '%s' not found in incoming values" % self.ref_name
ref = other_values.get( self.ref_name, None )
for ref_attribute in self.ref_attribute:
if not hasattr( ref, ref_attribute ):
return [] #ref does not have attribute, so we cannot filter, return empty list
return [] # ref does not have attribute, so we cannot filter, return empty list
ref = getattr( ref, ref_attribute )
ref = str( ref )
rval = []
@@ -190,6 +205,7 @@ class ParamValueFilter( Filter ):
rval.append( fields )
return rval
class UniqueValueFilter( Filter ):
"""
Filters a list of options to be unique by a column value.
@@ -204,8 +220,10 @@ class UniqueValueFilter( Filter ):
column = elem.get( "column", None )
assert column is not None, "Required 'column' attribute missing from filter"
self.column = d_option.column_spec_to_index( column )
def get_dependency_name( self ):
return self.dynamic_option.dataset_ref_name
def filter_options( self, options, trans, other_values ):
rval = []
skip_list = []
@@ -215,6 +233,7 @@ class UniqueValueFilter( Filter ):
skip_list.append( fields[self.column] )
return rval
class MultipleSplitterFilter( Filter ):
"""
Turns a single line of options into multiple lines, by splitting a column and creating a line for each item.
@@ -232,14 +251,16 @@ class MultipleSplitterFilter( Filter ):
columns = elem.get( "column", None )
assert columns is not None, "Required 'columns' attribute missing from filter"
self.columns = [ d_option.column_spec_to_index( column ) for column in columns.split( "," ) ]
def filter_options( self, options, trans, other_values ):
rval = []
for fields in options:
for column in self.columns:
for field in fields[column].split( self.separator ):
rval.append( fields[0:column] + [field] + fields[column+1:] )
rval.append( fields[0:column] + [field] + fields[column + 1:] )
return rval
class AttributeValueSplitterFilter( Filter ):
"""
Filters a list of attribute-value pairs to be unique attribute names.
@@ -258,7 +279,8 @@ class AttributeValueSplitterFilter( Filter ):
self.name_val_separator = elem.get( "name_val_separator", None )
self.columns = elem.get( "column", None )
assert self.columns is not None, "Required 'columns' attribute missing from filter"
self.columns = [ int ( column ) for column in self.columns.split( "," ) ]
self.columns = [ int( column ) for column in self.columns.split( "," ) ]
def filter_options( self, options, trans, other_values ):
attr_names = []
rval = []
@@ -267,7 +289,7 @@ class AttributeValueSplitterFilter( Filter ):
for pair in fields[column].split( self.pair_separator ):
ary = pair.split( self.name_val_separator )
if len( ary ) == 2:
name, value = ary
name = ary[0]
if name not in attr_names:
rval.append( fields[0:column] + [name] + fields[column:] )
attr_names.append( name )
@@ -296,10 +318,11 @@ class AdditionalValueFilter( Filter ):
self.index = elem.get( "index", None )
if self.index is not None:
self.index = int( self.index )
def filter_options( self, options, trans, other_values ):
rval = list( options )
add_value = []
for i in range( self.dynamic_option.largest_index + 1 ):
for _ in range( self.dynamic_option.largest_index + 1 ):
add_value.append( "" )
value_col = self.dynamic_option.columns.get( 'value', 0 )
name_col = self.dynamic_option.columns.get( 'name', value_col )
@@ -312,6 +335,7 @@ class AdditionalValueFilter( Filter ):
rval.append( add_value )
return rval
class RemoveValueFilter( Filter ):
"""
Removes a value from an options list.
@@ -337,9 +361,12 @@ class RemoveValueFilter( Filter ):
assert self.value is not None or ( ( self.ref_name is not None or self.meta_ref is not None )and self.metadata_key is not None ), ValueError( "Required 'value' or 'ref' and 'key' attributes missing from filter" )
self.multiple = string_as_bool( elem.get( "multiple", "False" ) )
self.separator = elem.get( "separator", "," )
def filter_options( self, options, trans, other_values ):
if trans is not None and trans.workflow_building_mode: return options
if trans is not None and trans.workflow_building_mode:
return options
assert self.value is not None or ( self.ref_name is not None and self.ref_name in other_values ) or (self.meta_ref is not None and self.meta_ref in other_values ) or ( trans is not None and trans.workflow_building_mode), Exception( "Required dependency '%s' or '%s' not found in incoming values" % ( self.ref_name, self.meta_ref ) )
def compare_value( option_value, filter_value ):
if isinstance( filter_value, list ):
if self.multiple:
@@ -358,11 +385,12 @@ class RemoveValueFilter( Filter ):
value = other_values.get( self.ref_name )
else:
data_ref = other_values.get( self.meta_ref )
if not isinstance( data_ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ) and not ( isinstance( data_ref, galaxy.tools.wrappers.DatasetFilenameWrapper ) ):
return options #cannot modify options
if not isinstance( data_ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ) and not isinstance( data_ref, galaxy.tools.wrappers.DatasetFilenameWrapper ):
return options # cannot modify options
value = data_ref.metadata.get( self.metadata_key, None )
return [ ( disp_name, optval, selected ) for disp_name, optval, selected in options if not compare_value( optval, value ) ]
class SortByColumnFilter( Filter ):
"""
Sorts an options list by a column
@@ -377,9 +405,10 @@ class SortByColumnFilter( Filter ):
column = elem.get( "column", None )
assert column is not None, "Required 'column' attribute missing from filter"
self.column = d_option.column_spec_to_index( column )
def filter_options( self, options, trans, other_values ):
rval = []
for i, fields in enumerate( options ):
for fields in options:
for j in range( 0, len( rval ) ):
if fields[self.column] < rval[j][self.column]:
rval.insert( j, fields )
@@ -389,20 +418,21 @@ class SortByColumnFilter( Filter ):
return rval
filter_types = dict( data_meta = DataMetaFilter,
param_value = ParamValueFilter,
static_value = StaticValueFilter,
unique_value = UniqueValueFilter,
multiple_splitter = MultipleSplitterFilter,
attribute_value_splitter = AttributeValueSplitterFilter,
add_value = AdditionalValueFilter,
remove_value = RemoveValueFilter,
sort_by = SortByColumnFilter )
filter_types = dict( data_meta=DataMetaFilter,
param_value=ParamValueFilter,
static_value=StaticValueFilter,
unique_value=UniqueValueFilter,
multiple_splitter=MultipleSplitterFilter,
attribute_value_splitter=AttributeValueSplitterFilter,
add_value=AdditionalValueFilter,
remove_value=RemoveValueFilter,
sort_by=SortByColumnFilter )
class DynamicOptions( object ):
"""Handles dynamically generated SelectToolParameter options"""
def __init__( self, elem, tool_param ):
def load_from_parameter( from_parameter, transform_lines = None ):
def load_from_parameter( from_parameter, transform_lines=None ):
obj = self.tool_param
for field in from_parameter.split( '.' ):
obj = getattr( obj, field )
@@ -513,7 +543,7 @@ class DynamicOptions( object ):
except AttributeError:
name = "a configuration file"
# Perhaps this should be an error, but even a warning is useful.
log.warn( "Inconsistent number of fields (%i vs %i) in %s using separator %r, check line: %r" % \
log.warn( "Inconsistent number of fields (%i vs %i) in %s using separator %r, check line: %r" %
( field_count, len( fields ), name, self.separator, line ) )
rval.append( fields )
return rval
@@ -536,10 +566,10 @@ class DynamicOptions( object ):
if self.dataset_ref_name:
dataset = other_values.get( self.dataset_ref_name, None )
assert dataset is not None, "Required dataset '%s' missing from input" % self.dataset_ref_name
if not dataset: return [] #no valid dataset in history
if not dataset:
return [] # no valid dataset in history
# Ensure parsing dynamic options does not consume more than a megabyte worth memory.
path = dataset.file_name
file_size = os.path.getsize( path )
if os.path.getsize( path ) < 1048576:
options = self.parse_file_fields( open( path ) )
else:
+87 -45
View File
@@ -17,6 +17,7 @@ from galaxy.util.bunch import Bunch
from galaxy.util.expressions import ExpressionContext
from galaxy.model.item_attrs import Dictifiable
class Group( object, Dictifiable ):
dict_collection_visible_keys = ( 'name', 'type' )
@@ -41,6 +42,7 @@ class Group( object, Dictifiable ):
into the preferred value form.
"""
return value
def get_initial_value( self, trans, context, history=None ):
"""
Return the initial state/value for this group
@@ -52,11 +54,12 @@ class Group( object, Dictifiable ):
group_dict = super( Group, self ).to_dict( view=view, value_mapper=value_mapper )
return group_dict
class Repeat( Group ):
dict_collection_visible_keys = ( 'name', 'type', 'title', 'help', 'default', 'min', 'max' )
type = "repeat"
def __init__( self ):
Group.__init__( self )
self.title = None
@@ -65,11 +68,14 @@ class Repeat( Group ):
self.default = 0
self.min = None
self.max = None
@property
def title_plural( self ):
return inflector.pluralize( self.title )
def label( self ):
return "Repeat (%s)" % self.title
def value_to_basic( self, value, app ):
rval = []
for d in value:
@@ -81,6 +87,7 @@ class Repeat( Group ):
rval_dict[ input.name ] = input.value_to_basic( d[input.name], app )
rval.append( rval_dict )
return rval
def value_from_basic( self, value, app, ignore_errors=False ):
rval = []
try:
@@ -103,14 +110,16 @@ class Repeat( Group ):
if not ignore_errors:
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
for i, d in enumerate( value ):
for input in self.inputs.itervalues():
new_prefix = prefix + "%s_%d|" % ( self.name, i )
if isinstance( input, ToolParameter ):
callback( new_prefix, input, d[input.name], parent = d )
callback( new_prefix, input, d[input.name], parent=d )
else:
input.visit_inputs( new_prefix, d[input.name], callback )
def get_initial_value( self, trans, context, history=None ):
rval = []
for i in range( self.default ):
@@ -129,27 +138,32 @@ class Repeat( Group ):
repeat_dict[ "inputs" ] = map( input_to_dict, self.inputs.values() )
return repeat_dict
class Section( Group ):
dict_collection_visible_keys = ( 'name', 'type', 'title', 'help', 'expanded')
type = "section"
def __init__( self ):
Group.__init__( self )
self.title = None
self.inputs = None
self.help = None
self.expanded = False
@property
def title_plural( self ):
return inflector.pluralize( self.title )
def label( self ):
return "Section (%s)" % self.title
def value_to_basic( self, value, app ):
rval = {}
for input in self.inputs.itervalues():
rval[ input.name ] = input.value_to_basic( value[input.name], app )
return rval
def value_from_basic( self, value, app, ignore_errors=False ):
rval = {}
try:
@@ -160,64 +174,79 @@ class Section( Group ):
if not ignore_errors:
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
for input in self.inputs.itervalues():
if isinstance( input, ToolParameter ):
callback( prefix, input, value[input.name], parent = value )
callback( prefix, input, value[input.name], parent=value )
else:
input.visit_inputs( prefix, value[input.name], callback )
def get_initial_value( self, trans, context, history=None ):
rval = {}
child_context = ExpressionContext( rval, context )
for child_input in self.inputs.itervalues():
rval[ child_input.name ] = child_input.get_initial_value( trans, child_context, history=history )
return rval
def to_dict( self, trans, view='collection', value_mapper=None ):
section_dict = super( Section, self ).to_dict( trans, view=view, value_mapper=value_mapper )
def input_to_dict( input ):
return input.to_dict( trans, view=view, value_mapper=value_mapper )
section_dict[ "inputs" ] = map( input_to_dict, self.inputs.values() )
return section_dict
class UploadDataset( Group ):
type = "upload_dataset"
def __init__( self ):
Group.__init__( self )
self.title = None
self.inputs = None
self.file_type_name = 'file_type'
self.default_file_type = 'txt'
self.file_type_to_ext = { 'auto':self.default_file_type }
self.file_type_to_ext = { 'auto': self.default_file_type }
self.metadata_ref = 'files_metadata'
def get_composite_dataset_name( self, context ):
#FIXME: HACK
#Special case of using 'base_name' metadata for use as Dataset name needs to be done in a General Fashion, as defined within a particular Datatype.
#We get two different types of contexts here, one straight from submitted parameters, the other after being parsed into tool inputs
def get_composite_dataset_name( self, context ):
# FIXME: HACK
# Special case of using 'base_name' metadata for use as Dataset name needs to be done in a General Fashion, as defined within a particular Datatype.
# We get two different types of contexts here, one straight from submitted parameters, the other after being parsed into tool inputs
dataset_name = context.get('files_metadata|base_name', None )
if dataset_name is None:
dataset_name = context.get('files_metadata', {} ).get( 'base_name', None )
if dataset_name is None:
dataset_name = 'Uploaded Composite Dataset (%s)' % self.get_file_type( context )
return dataset_name
def get_file_base_name( self, context ):
fd = context.get('files_metadata|base_name','Galaxy_Composite_file')
fd = context.get('files_metadata|base_name', 'Galaxy_Composite_file')
return fd
def get_file_type( self, context ):
return context.get( self.file_type_name, self.default_file_type )
def get_datatype_ext( self, trans, context ):
ext = self.get_file_type( context )
if ext in self.file_type_to_ext:
ext = self.file_type_to_ext[ext] #when using autodetect, we will use composite info from 'text', i.e. only the main file
ext = self.file_type_to_ext[ext] # when using autodetect, we will use composite info from 'text', i.e. only the main file
return ext
def get_datatype( self, trans, context ):
ext = self.get_datatype_ext( trans, context )
return trans.app.datatypes_registry.get_datatype_by_extension( ext )
@property
def title_plural( self ):
return inflector.pluralize(self.title)
def group_title( self, context ):
return "%s (%s)" % ( self.title, context.get( self.file_type_name, self.default_file_type ) )
def title_by_index( self, trans, index, context ):
d_type = self.get_datatype( trans, context )
for i, ( composite_name, composite_file ) in enumerate( d_type.writable_files.iteritems() ):
@@ -229,6 +258,7 @@ class UploadDataset( Group ):
rval = "%s [optional]" % rval
return rval
return None
def value_to_basic( self, value, app ):
rval = []
for d in value:
@@ -240,6 +270,7 @@ class UploadDataset( Group ):
rval_dict[ input.name ] = input.value_to_basic( d[input.name], app )
rval.append( rval_dict )
return rval
def value_from_basic( self, value, app, ignore_errors=False ):
rval = []
for i, d in enumerate( value ):
@@ -249,34 +280,38 @@ class UploadDataset( Group ):
rval_dict['__index__'] = d.get( '__index__', i )
# Restore child inputs
for input in self.inputs.itervalues():
if ignore_errors and input.name not in d: #this wasn't tested
if ignore_errors and input.name not in d: # this wasn't tested
rval_dict[ input.name ] = input.get_initial_value( None, d )
else:
rval_dict[ input.name ] = input.value_from_basic( d[input.name], app, ignore_errors )
rval.append( rval_dict )
return rval
def visit_inputs( self, prefix, value, callback ):
for i, d in enumerate( value ):
for input in self.inputs.itervalues():
new_prefix = prefix + "%s_%d|" % ( self.name, i )
if isinstance( input, ToolParameter ):
callback( new_prefix, input, d[input.name], parent = d )
callback( new_prefix, input, d[input.name], parent=d )
else:
input.visit_inputs( new_prefix, d[input.name], callback )
def get_initial_value( self, trans, context, history=None ):
d_type = self.get_datatype( trans, context )
rval = []
for i, ( composite_name, composite_file ) in enumerate( d_type.writable_files.iteritems() ):
rval_dict = {}
rval_dict['__index__'] = i # create __index__
rval_dict['__index__'] = i # create __index__
for input in self.inputs.itervalues():
rval_dict[ input.name ] = input.get_initial_value( trans, context, history=history ) #input.value_to_basic( d[input.name], app )
rval_dict[ input.name ] = input.get_initial_value( trans, context, history=history ) # input.value_to_basic( d[input.name], app )
rval.append( rval_dict )
return rval
def get_uploaded_datasets( self, trans, context, override_name = None, override_info = None ):
def get_data_file_filename( data_file, override_name = None, override_info = None ):
def get_uploaded_datasets( self, trans, context, override_name=None, override_info=None ):
def get_data_file_filename( data_file, override_name=None, override_info=None ):
dataset_name = override_name
dataset_info = override_info
def get_file_name( file_name ):
file_name = file_name.split( '\\' )[-1]
file_name = file_name.split( '/' )[-1]
@@ -288,13 +323,13 @@ class UploadDataset( Group ):
if not dataset_info:
dataset_info = 'uploaded file'
return Bunch( type='file', path=data_file['local_filename'], name=dataset_name )
#return 'file', data_file['local_filename'], get_file_name( data_file.filename ), dataset_name, dataset_info
# return 'file', data_file['local_filename'], get_file_name( data_file.filename ), dataset_name, dataset_info
except:
# The uploaded file should've been persisted by the upload tool action
return Bunch( type=None, path=None, name=None )
#return None, None, None, None, None
def get_url_paste_urls_or_filename( group_incoming, override_name = None, override_info = None ):
filenames = []
# return None, None, None, None, None
def get_url_paste_urls_or_filename( group_incoming, override_name=None, override_info=None ):
url_paste_file = group_incoming.get( 'url_paste', None )
if url_paste_file is not None:
url_paste = open( url_paste_file, 'r' ).read( 1024 )
@@ -304,7 +339,7 @@ class UploadDataset( Group ):
line = line.strip()
if line:
if not line.lower().startswith( 'http://' ) and not line.lower().startswith( 'ftp://' ) and not line.lower().startswith( 'https://' ):
continue # non-url line, ignore
continue # non-url line, ignore
dataset_name = override_name
if not dataset_name:
dataset_name = line
@@ -312,15 +347,16 @@ class UploadDataset( Group ):
if not dataset_info:
dataset_info = 'uploaded url'
yield Bunch( type='url', path=line, name=dataset_name )
#yield ( 'url', line, precreated_name, dataset_name, dataset_info )
# yield ( 'url', line, precreated_name, dataset_name, dataset_info )
else:
dataset_name = dataset_info = precreated_name = 'Pasted Entry' #we need to differentiate between various url pastes here
dataset_name = dataset_info = precreated_name = 'Pasted Entry' # we need to differentiate between various url pastes here
if override_name:
dataset_name = override_name
if override_info:
dataset_info = override_info
yield Bunch( type='file', path=url_paste_file, name=precreated_name )
#yield ( 'file', url_paste_file, precreated_name, dataset_name, dataset_info )
# yield ( 'file', url_paste_file, precreated_name, dataset_name, dataset_info )
def get_one_filename( context ):
data_file = context['file_data']
url_paste = context['url_paste']
@@ -335,19 +371,19 @@ class UploadDataset( Group ):
space_to_tab = False
if context.get( 'space_to_tab', None ) not in [ "None", None, False ]:
space_to_tab = True
file_bunch = get_data_file_filename( data_file, override_name = name, override_info = info )
file_bunch = get_data_file_filename( data_file, override_name=name, override_info=info )
if file_bunch.path:
if url_paste is not None and url_paste.strip():
warnings.append( "All file contents specified in the paste box were ignored." )
if ftp_files:
warnings.append( "All FTP uploaded file selections were ignored." )
elif url_paste is not None and url_paste.strip(): #we need to use url_paste
for file_bunch in get_url_paste_urls_or_filename( context, override_name = name, override_info = info ):
elif url_paste is not None and url_paste.strip(): # we need to use url_paste
for file_bunch in get_url_paste_urls_or_filename( context, override_name=name, override_info=info ):
if file_bunch.path:
break
if file_bunch.path and ftp_files is not None:
warnings.append( "All FTP uploaded file selections were ignored." )
elif ftp_files is not None and trans.user is not None: # look for files uploaded via FTP
elif ftp_files is not None and trans.user is not None: # look for files uploaded via FTP
user_ftp_dir = trans.user_ftp_dir
for ( dirpath, dirnames, filenames ) in os.walk( user_ftp_dir ):
for filename in filenames:
@@ -357,7 +393,7 @@ class UploadDataset( Group ):
if not os.path.islink( os.path.join( dirpath, filename ) ):
ftp_data_file = { 'local_filename' : os.path.abspath( os.path.join( user_ftp_dir, path ) ),
'filename' : os.path.basename( path ) }
file_bunch = get_data_file_filename( ftp_data_file, override_name = name, override_info = info )
file_bunch = get_data_file_filename( ftp_data_file, override_name=name, override_info=info )
if file_bunch.path:
break
if file_bunch.path:
@@ -368,10 +404,10 @@ class UploadDataset( Group ):
file_bunch.space_to_tab = space_to_tab
file_bunch.uuid = uuid
return file_bunch, warnings
def get_filenames( context ):
rval = []
data_file = context['file_data']
url_paste = context['url_paste']
ftp_files = context['ftp_files']
uuid = context.get( 'uuid', None ) or None # Turn '' to None
name = context.get( 'NAME', None )
@@ -382,14 +418,13 @@ class UploadDataset( Group ):
space_to_tab = False
if context.get( 'space_to_tab', None ) not in [ "None", None, False ]:
space_to_tab = True
warnings = []
file_bunch = get_data_file_filename( data_file, override_name = name, override_info = info )
file_bunch = get_data_file_filename( data_file, override_name=name, override_info=info )
file_bunch.uuid = uuid
if file_bunch.path:
file_bunch.to_posix_lines = to_posix_lines
file_bunch.space_to_tab = space_to_tab
rval.append( file_bunch )
for file_bunch in get_url_paste_urls_or_filename( context, override_name = name, override_info = info ):
for file_bunch in get_url_paste_urls_or_filename( context, override_name=name, override_info=info ):
if file_bunch.path:
file_bunch.uuid = uuid
file_bunch.to_posix_lines = to_posix_lines
@@ -426,7 +461,7 @@ class UploadDataset( Group ):
# TODO: warning to the user (could happen if file is already imported)
ftp_data_file = { 'local_filename' : os.path.abspath( os.path.join( user_ftp_dir, ftp_file ) ),
'filename' : os.path.basename( ftp_file ) }
file_bunch = get_data_file_filename( ftp_data_file, override_name = name, override_info = info )
file_bunch = get_data_file_filename( ftp_data_file, override_name=name, override_info=info )
if file_bunch.path:
file_bunch.to_posix_lines = to_posix_lines
file_bunch.space_to_tab = space_to_tab
@@ -437,13 +472,13 @@ class UploadDataset( Group ):
dbkey = context.get( 'dbkey', None )
writable_files = d_type.writable_files
writable_files_offset = 0
groups_incoming = [ None for filename in writable_files ]
groups_incoming = [ None for _ in writable_files ]
for group_incoming in context.get( self.name, [] ):
i = int( group_incoming['__index__'] )
groups_incoming[ i ] = group_incoming
if d_type.composite_type is not None:
#handle uploading of composite datatypes
#Only one Dataset can be created
# handle uploading of composite datatypes
# Only one Dataset can be created
dataset = Bunch()
dataset.type = 'composite'
dataset.file_type = file_type
@@ -453,7 +488,7 @@ class UploadDataset( Group ):
dataset.metadata = {}
dataset.composite_files = {}
dataset.uuid = None
#load metadata
# load metadata
files_metadata = context.get( self.metadata_ref, {} )
metadata_name_substition_default_dict = dict( [ ( composite_file.substitute_name_with_metadata, d_type.metadata_spec[ composite_file.substitute_name_with_metadata ].default ) for composite_file in d_type.composite_files.values() if composite_file.substitute_name_with_metadata ] )
for meta_name, meta_spec in d_type.metadata_spec.iteritems():
@@ -461,11 +496,11 @@ class UploadDataset( Group ):
if meta_name in files_metadata:
meta_value = files_metadata[ meta_name ]
if meta_name in metadata_name_substition_default_dict:
meta_value = sanitize_for_filename( meta_value, default = metadata_name_substition_default_dict[ meta_name ] )
meta_value = sanitize_for_filename( meta_value, default=metadata_name_substition_default_dict[ meta_name ] )
dataset.metadata[ meta_name ] = meta_value
dataset.precreated_name = dataset.name = self.get_composite_dataset_name( context )
if dataset.datatype.composite_type == 'auto_primary_file':
#replace sniff here with just creating an empty file
# replace sniff here with just creating an empty file
temp_name, is_multi_byte = sniff.stream_to_file( StringIO.StringIO( d_type.generate_primary_file( dataset ) ), prefix='upload_auto_primary_file' )
dataset.primary_file = temp_name
dataset.to_posix_lines = True
@@ -477,7 +512,7 @@ class UploadDataset( Group ):
dataset.to_posix_lines = file_bunch.to_posix_lines
dataset.space_to_tab = file_bunch.space_to_tab
dataset.warnings.extend( warnings )
if dataset.primary_file is None:#remove this before finish, this should create an empty dataset
if dataset.primary_file is None: # remove this before finish, this should create an empty dataset
raise Exception( 'No primary dataset file was available for composite upload' )
keys = [ value.name for value in writable_files.values() ]
for i, group_incoming in enumerate( groups_incoming[ writable_files_offset : ] ):
@@ -506,17 +541,21 @@ class UploadDataset( Group ):
rval.append( dataset )
return rval
class Conditional( Group ):
type = "conditional"
def __init__( self ):
Group.__init__( self )
self.test_param = None
self.cases = []
self.value_ref = None
self.value_ref_in_group = True #When our test_param is not part of the conditional Group, this is False
self.value_ref_in_group = True # When our test_param is not part of the conditional Group, this is False
@property
def label( self ):
return "Conditional (%s)" % self.name
def get_current_case( self, value, trans ):
# Convert value to user representation
if isinstance( value, bool ):
@@ -528,6 +567,7 @@ class Conditional( Group ):
if str_value == case.value:
return index
raise ValueError( "No case matched value:", self.name, str_value )
def value_to_basic( self, value, app ):
rval = dict()
current_case = rval['__current_case__'] = value['__current_case__']
@@ -535,6 +575,7 @@ class Conditional( Group ):
for input in self.cases[current_case].inputs.itervalues():
rval[ input.name ] = input.value_to_basic( value[ input.name ], app )
return rval
def value_from_basic( self, value, app, ignore_errors=False ):
rval = dict()
try:
@@ -560,14 +601,15 @@ class Conditional( Group ):
if not ignore_errors:
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
current_case = value['__current_case__']
new_prefix = prefix + "%s|" % ( self.name )
for input in self.cases[current_case].inputs.itervalues():
if isinstance( input, ToolParameter ):
callback( prefix, input, value[input.name], parent = value )
callback( prefix, input, value[input.name], parent=value )
else:
input.visit_inputs( prefix, value[input.name], callback )
def get_initial_value( self, trans, context, history=None ):
# State for a conditional is a plain dictionary.
rval = {}
@@ -7,6 +7,7 @@ from galaxy.util.bunch import Bunch
log = logging.getLogger( __name__ )
class ToolInputTranslator( object ):
"""
Handles Tool input translation.
@@ -53,7 +54,7 @@ class ToolInputTranslator( object ):
rval = ToolInputTranslator()
for req_param in elem.findall( "request_param" ):
# req_param tags must look like <request_param galaxy_name="dbkey" remote_name="GENOME" missing="" />
#trans_list = []
# trans_list = []
remote_name = req_param.get( "remote_name" )
galaxy_name = req_param.get( "galaxy_name" )
missing = req_param.get( "missing" )
@@ -79,9 +80,9 @@ class ToolInputTranslator( object ):
value_missing = value_elem.get( 'missing' )
if None not in [ value_name, value_missing ]:
append_dict[ value_name ] = value_missing
append_param = Bunch( separator = separator, first_separator = first_separator, join_str = join_str, append_dict = append_dict )
append_param = Bunch( separator=separator, first_separator=first_separator, join_str=join_str, append_dict=append_dict )
rval.param_trans_dict[ remote_name ] = Bunch( galaxy_name = galaxy_name, missing = missing, value_trans = value_trans, append_param = append_param )
rval.param_trans_dict[ remote_name ] = Bunch( galaxy_name=galaxy_name, missing=missing, value_trans=value_trans, append_param=append_param )
return rval
@@ -93,8 +94,8 @@ class ToolInputTranslator( object ):
update params in-place
"""
for remote_name, translator in self.param_trans_dict.iteritems():
galaxy_name = translator.galaxy_name #NB: if a param by name galaxy_name is provided, it is always thrown away unless galaxy_name == remote_name
value = params.get( remote_name, translator.missing ) #get value from input params, or use default value specified in tool config
galaxy_name = translator.galaxy_name # NB: if a param by name galaxy_name is provided, it is always thrown away unless galaxy_name == remote_name
value = params.get( remote_name, translator.missing ) # get value from input params, or use default value specified in tool config
if translator.value_trans and value in translator.value_trans:
value = translator.value_trans[ value ]
if translator.append_param:
@@ -229,7 +229,7 @@ def collect_primary_datasets( tool, output, job_working_directory, input_ext ):
app.object_store.update_from_file( outdata.dataset, file_name=filename, create=True )
primary_output_assigned = True
continue
if not name in primary_datasets:
if name not in primary_datasets:
primary_datasets[ name ] = {}
visible = fields_match.visible
ext = fields_match.ext
+14 -13
View File
@@ -8,6 +8,7 @@ import galaxy.util
log = logging.getLogger( __name__ )
class ToolParameterSanitizer( object ):
"""
Handles tool parameter specific sanitizing.
@@ -41,15 +42,15 @@ class ToolParameterSanitizer( object ):
True
"""
VALID_PRESET = { 'default':( string.letters + string.digits +" -=_.()/+*^,:?!" ), 'none':'' }
MAPPING_PRESET = { 'default':galaxy.util.mapped_chars, 'none':{} }
VALID_PRESET = { 'default': ( string.letters + string.digits + " -=_.()/+*^,:?!" ), 'none': '' }
MAPPING_PRESET = { 'default': galaxy.util.mapped_chars, 'none': {} }
DEFAULT_INVALID_CHAR = 'X'
#class methods
# class methods
@classmethod
def from_element( cls, elem ):
"""Loads the proper filter by the type attribute of elem"""
#TODO: Add ability to generically specify a method to use for sanitizing input via specification in tool XML
# TODO: Add ability to generically specify a method to use for sanitizing input via specification in tool XML
rval = ToolParameterSanitizer()
rval._invalid_char = elem.get( 'invalid_char', cls.DEFAULT_INVALID_CHAR )
rval.sanitize = galaxy.util.string_as_bool( elem.get( 'sanitize', 'True' ) )
@@ -59,11 +60,11 @@ class ToolParameterSanitizer( object ):
preset = rval.get_valid_by_name( action_elem.get( 'preset', 'none' ) )
valid_value = [ val for val in action_elem.get( 'value', [] ) ]
if action_elem.tag.lower() == 'add':
for val in ( preset + valid_value ):
for val in preset + valid_value:
if val not in rval._valid_chars:
rval._valid_chars.append( val )
elif action_elem.tag.lower() == 'remove':
for val in ( preset + valid_value ):
for val in preset + valid_value:
while val in rval._valid_chars:
rval._valid_chars.remove( val )
else:
@@ -116,13 +117,13 @@ class ToolParameterSanitizer( object ):
else:
log.debug( 'Invalid preset name specified: %s' % split_name )
return rval
#end class methods
# end class methods
def __init__( self ):
self._valid_chars = [] #List of valid characters
self._mapped_chars = {} #Replace a char with a any number of characters
self._invalid_char = self.DEFAULT_INVALID_CHAR #Replace invalid characters with this character
self.sanitize = True #Simply pass back the passed in value
self._valid_chars = [] # List of valid characters
self._mapped_chars = {} # Replace a char with a any number of characters
self._invalid_char = self.DEFAULT_INVALID_CHAR # Replace invalid characters with this character
self.sanitize = True # Simply pass back the passed in value
def restore_text( self, text ):
"""Restores sanitized text"""
@@ -138,7 +139,7 @@ class ToolParameterSanitizer( object ):
elif isinstance( value, list ):
return map( self.restore_text, value )
else:
raise Exception, 'Unknown parameter type (%s:%s)' % ( type( value ), value )
raise Exception('Unknown parameter type (%s:%s)' % ( type( value ), value ))
return value
def sanitize_text( self, text ):
@@ -164,4 +165,4 @@ class ToolParameterSanitizer( object ):
elif isinstance( value, list ):
return map( self.sanitize_text, value )
else:
raise Exception, 'Unknown parameter type (%s:%s)' % ( type( value ), value )
raise Exception('Unknown parameter type (%s:%s)' % ( type( value ), value ))
+56 -15
View File
@@ -2,16 +2,18 @@
Classes related to parameter validation.
"""
import os, re, logging
from xml.etree.ElementTree import XML
import logging
import re
from galaxy import model
log = logging.getLogger( __name__ )
class LateValidationError( Exception ):
def __init__( self, message ):
self.message = message
class Validator( object ):
"""
A validator checks that a value meets some conditions OR raises ValueError
@@ -21,14 +23,17 @@ class Validator( object ):
type = elem.get( 'type', None )
assert type is not None, "Required 'type' attribute missing from validator"
return validator_types[type].from_element( param, elem )
def validate( self, value, history=None ):
raise TypeError( "Abstract Method" )
class RegexValidator( Validator ):
"""
Validator that evaluates a regular expression
>>> from galaxy.tools.parameters import ToolParameter
>>> from xml.etree.ElementTree import XML
>>> from galaxy.tools.parameters.basic import ToolParameter
>>> p = ToolParameter.build( None, XML( '''
... <param name="blah" type="text" size="10" value="10">
... <validator type="regex" message="Not gonna happen">[Ff]oo</validator>
@@ -44,20 +49,24 @@ class RegexValidator( Validator ):
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message' ), elem.text )
def __init__( self, message, expression ):
self.message = message
# Compile later. RE objects used to not be thread safe. Not sure about
# the sre module.
self.expression = expression
def validate( self, value, history=None ):
if re.match( self.expression, value ) is None:
raise ValueError( self.message )
class ExpressionValidator( Validator ):
"""
Validator that evaluates a python expression using the value
>>> from galaxy.tools.parameters import ToolParameter
>>> from xml.etree.ElementTree import XML
>>> from galaxy.tools.parameters.basic import ToolParameter
>>> p = ToolParameter.build( None, XML( '''
... <param name="blah" type="text" size="10" value="10">
... <validator type="expression" message="Not gonna happen">value.lower() == "foo"</validator>
@@ -73,11 +82,13 @@ class ExpressionValidator( Validator ):
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message' ), elem.text, elem.get( 'substitute_value_in_message' ) )
def __init__( self, message, expression, substitute_value_in_message ):
self.message = message
self.substitute_value_in_message = substitute_value_in_message
# Save compiled expression, code objects are thread safe (right?)
self.expression = compile( expression, '<string>', 'eval' )
def validate( self, value, history=None ):
if not( eval( self.expression, dict( value=value ) ) ):
message = self.message
@@ -85,11 +96,13 @@ class ExpressionValidator( Validator ):
message = message % value
raise ValueError( message )
class InRangeValidator( Validator ):
"""
Validator that ensures a number is in a specific range
>>> from galaxy.tools.parameters import ToolParameter
>>> from xml.etree.ElementTree import XML
>>> from galaxy.tools.parameters.basic import ToolParameter
>>> p = ToolParameter.build( None, XML( '''
... <param name="blah" type="integer" size="10" value="10">
... <validator type="in_range" message="Not gonna happen" min="10" max="20"/>
@@ -106,6 +119,7 @@ class InRangeValidator( Validator ):
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ), elem.get( 'min' ), elem.get( 'max' ) )
def __init__( self, message, range_min, range_max ):
self.min = float( range_min if range_min is not None else '-inf' )
self.max = float( range_max if range_max is not None else 'inf' )
@@ -114,15 +128,18 @@ class InRangeValidator( Validator ):
self_min_str = str( self.min ).rstrip( '0' ).rstrip( '.' )
self_max_str = str( self.max ).rstrip( '0' ).rstrip( '.' )
self.message = message or "Value must be between %s and %s" % ( self_min_str, self_max_str )
def validate( self, value, history=None ):
if not( self.min <= float( value ) <= self.max ):
raise ValueError( self.message )
class LengthValidator( Validator ):
"""
Validator that ensures the length of the provided string (value) is in a specific range
>>> from galaxy.tools.parameters import ToolParameter
>>> from xml.etree.ElementTree import XML
>>> from galaxy.tools.parameters.basic import ToolParameter
>>> p = ToolParameter.build( None, XML( '''
... <param name="blah" type="text" size="10" value="foobar">
... <validator type="length" min="2" max="8"/>
@@ -142,6 +159,7 @@ class LengthValidator( Validator ):
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ), elem.get( 'min', None ), elem.get( 'max', None ) )
def __init__( self, message, length_min, length_max ):
self.message = message
if length_min is not None:
@@ -150,47 +168,55 @@ class LengthValidator( Validator ):
length_max = int( length_max )
self.min = length_min
self.max = length_max
def validate( self, value, history=None ):
if self.min is not None and len( value ) < self.min:
raise ValueError( self.message or ( "Must have length of at least %d" % self.min ) )
if self.max is not None and len( value ) > self.max:
raise ValueError( self.message or ( "Must have length no more than %d" % self.max ) )
class DatasetOkValidator( Validator ):
"""
Validator that checks if a dataset is in an 'ok' state
"""
def __init__( self, message=None ):
self.message = message
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ) )
def validate( self, value, history=None ):
if value and value.state != model.Dataset.states.OK:
if self.message is None:
self.message = "The selected dataset is still being generated, select another dataset or wait until it is completed"
raise ValueError( self.message )
class MetadataValidator( Validator ):
"""
Validator that checks for missing metadata
"""
def __init__( self, message = None, check = "", skip = "" ):
def __init__( self, message=None, check="", skip="" ):
self.message = message
self.check = check.split( "," )
self.skip = skip.split( "," )
@classmethod
def from_element( cls, param, elem ):
return cls( message=elem.get( 'message', None ), check=elem.get( 'check', "" ), skip=elem.get( 'skip', "" ) )
def validate( self, value, history=None ):
if value:
if not isinstance( value, model.DatasetInstance ):
raise ValueError( 'A non-dataset value was provided.' )
if value.missing_meta( check = self.check, skip = self.skip ):
if value.missing_meta( check=self.check, skip=self.skip ):
if self.message is None:
self.message = "Metadata missing, click the pencil icon in the history item to edit / save the metadata attributes"
raise ValueError( self.message )
class UnspecifiedBuildValidator( Validator ):
"""
Validator that checks for dbkey not equal to '?'
@@ -200,11 +226,13 @@ class UnspecifiedBuildValidator( Validator ):
self.message = "Unspecified genome build, click the pencil icon in the history item to set the genome build"
else:
self.message = message
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ) )
def validate( self, value, history=None ):
#if value is None, we cannot validate
# if value is None, we cannot validate
if value:
dbkey = value.metadata.dbkey
if isinstance( dbkey, list ):
@@ -212,32 +240,39 @@ class UnspecifiedBuildValidator( Validator ):
if dbkey == '?':
raise ValueError( self.message )
class NoOptionsValidator( Validator ):
"""Validator that checks for empty select list"""
def __init__( self, message=None ):
self.message = message
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ) )
def validate( self, value, history=None ):
if value is None:
if self.message is None:
self.message = "No options available for selection"
raise ValueError( self.message )
class EmptyTextfieldValidator( Validator ):
"""Validator that checks for empty text field"""
def __init__( self, message=None ):
self.message = message
@classmethod
def from_element( cls, param, elem ):
return cls( elem.get( 'message', None ) )
def validate( self, value, history=None ):
if value == '':
if self.message is None:
self.message = "Field requires a value"
raise ValueError( self.message )
class MetadataInFileColumnValidator( Validator ):
"""
Validator that checks if the value for a dataset's metadata item exists in a file.
@@ -256,6 +291,7 @@ class MetadataInFileColumnValidator( Validator ):
if line_startswith:
line_startswith = line_startswith.strip()
return cls( filename, metadata_name, metadata_column, message, line_startswith )
def __init__( self, filename, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None ):
self.metadata_name = metadata_name
self.message = message
@@ -265,13 +301,16 @@ class MetadataInFileColumnValidator( Validator ):
fields = line.split( '\t' )
if metadata_column < len( fields ):
self.valid_values.append( fields[metadata_column].strip() )
def validate( self, value, history = None ):
if not value: return
def validate( self, value, history=None ):
if not value:
return
if hasattr( value, "metadata" ):
if value.metadata.spec[self.metadata_name].param.to_string( value.metadata.get( self.metadata_name ) ) in self.valid_values:
return
raise ValueError( self.message )
class MetadataInDataTableColumnValidator( Validator ):
"""
Validator that checks if the value for a dataset's metadata item exists in a file.
@@ -313,8 +352,9 @@ class MetadataInDataTableColumnValidator( Validator ):
if self._metadata_column < len( fields ):
self.valid_values.append( fields[ self._metadata_column ] )
def validate( self, value, history = None ):
if not value: return
def validate( self, value, history=None ):
if not value:
return
if hasattr( value, "metadata" ):
if not self._tool_data_table.is_current_version( self._data_table_content_version ):
log.debug( 'MetadataInDataTableColumnValidator values are out of sync with data table (%s), updating validator.', self._tool_data_table.name )
@@ -336,8 +376,9 @@ validator_types = dict( expression=ExpressionValidator,
dataset_metadata_in_data_table=MetadataInDataTableColumnValidator,
dataset_ok_validator=DatasetOkValidator )
def get_suite():
"""Get unittest suite for this module"""
import doctest, sys
import doctest
import sys
return doctest.DocTestSuite( sys.modules[__name__] )
+7 -9
View File
@@ -1,5 +1,3 @@
import galaxy.tools
from galaxy.tools.parameters.basic import (
DataToolParameter,
DataCollectionToolParameter,
@@ -59,15 +57,15 @@ class WrappedParameters( object ):
elif isinstance( input, DataToolParameter ) and input.multiple:
input_values[ input.name ] = \
DatasetListWrapper( input_values[ input.name ],
datatypes_registry=trans.app.datatypes_registry,
tool=tool,
name=input.name )
datatypes_registry=trans.app.datatypes_registry,
tool=tool,
name=input.name )
elif isinstance( input, DataToolParameter ):
input_values[ input.name ] = \
DatasetFilenameWrapper( input_values[ input.name ],
datatypes_registry=trans.app.datatypes_registry,
tool=tool,
name=input.name )
datatypes_registry=trans.app.datatypes_registry,
tool=tool,
name=input.name )
elif isinstance( input, SelectToolParameter ):
input_values[ input.name ] = SelectToolParameterWrapper( input, input_values[ input.name ], tool.app, other_values=incoming )
elif isinstance( input, DataCollectionToolParameter ):
@@ -111,4 +109,4 @@ def make_list_copy( from_list ):
return new_list
__all__ = [ WrappedParameters, make_dict_copy ]
__all__ = [ 'WrappedParameters', 'make_dict_copy' ]
+1 -1
View File
@@ -236,7 +236,7 @@ class XmlToolSource(ToolSource):
output.tool = tool
output.from_work_dir = data_elem.get("from_work_dir", None)
output.hidden = string_as_bool( data_elem.get("hidden", "") )
output.actions = galaxy.tools.ToolOutputActionGroup( output, data_elem.find( 'actions' ) )
output.actions = galaxy.tools.parameters.output.ToolOutputActionGroup( output, data_elem.find( 'actions' ) )
output.dataset_collectors = output_collect.dataset_collectors_from_elem( data_elem )
return output
+1 -1
View File
@@ -87,7 +87,7 @@ class YamlToolSource(ToolSource):
output.tool = tool
output.from_work_dir = output_dict.get("from_work_dir", None)
output.hidden = output_dict.get("hidden", "")
output.actions = galaxy.tools.ToolOutputActionGroup( output, None )
output.actions = galaxy.tools.parameters.output.ToolOutputActionGroup( output, None )
discover_datasets_dicts = output_dict.get( "discover_datasets", [] )
if isinstance( discover_datasets_dicts, dict ):
discover_datasets_dicts = [ discover_datasets_dicts ]
+1
View File
@@ -18,6 +18,7 @@ schema = Schema( id=STORED,
import logging
log = logging.getLogger( __name__ )
class ToolBoxSearch( object ):
"""
Support searching tools in a toolbox. This implementation uses
+5 -2
View File
@@ -1,7 +1,6 @@
"""Utility functions for galaxyops"""
import sys
from bx.bitset import *
from bx.intervals.io import *
def warn( msg ):
# TODO: since everything printed to stderr results in job.state = error, we
@@ -9,6 +8,7 @@ def warn( msg ):
print >> sys.stderr, msg
sys.exit( 1 )
def fail( msg ):
print >> sys.stderr, msg
sys.exit( 1 )
@@ -16,6 +16,7 @@ def fail( msg ):
# Default chrom, start, end, strand cols for a bed file
BED_DEFAULT_COLS = 0, 1, 2, 5
def parse_cols_arg( cols ):
"""Parse a columns command line argument into a four-tuple"""
if cols:
@@ -28,10 +29,12 @@ def parse_cols_arg( cols ):
else:
return BED_DEFAULT_COLS
def default_printer( stream, exc, obj ):
print >> stream, "%d: %s" % ( obj.linenum, obj.current_line )
print >> stream, "\tError: %s" % ( str(exc) )
def skipped( reader, filedesc="" ):
first_line, line_contents, problem = reader.skipped_lines[0]
return 'Skipped %d invalid lines%s, 1st line #%d: "%s", problem: %s' % ( reader.skipped, filedesc, first_line, line_contents, problem )
+196 -138
View File
@@ -2,13 +2,17 @@
"""
Provides wrappers and utilities for working with MAF files and alignments.
"""
#Dan Blankenberg
import pkg_resources; pkg_resources.require( "bx-python" )
# Dan Blankenberg
import logging
import os
import string
import sys
import tempfile
import pkg_resources
pkg_resources.require( "bx-python" )
import bx.align.maf
import bx.intervals
import bx.interval_index_file
import sys, os, string, tempfile
import logging
from errno import EMFILE
import resource
from copy import deepcopy
@@ -21,6 +25,7 @@ log = logging.getLogger(__name__)
GAP_CHARS = [ '-' ]
SRC_SPLIT_CHAR = '.'
def src_split( src ):
fields = src.split( SRC_SPLIT_CHAR, 1 )
spec = fields.pop( 0 )
@@ -30,11 +35,13 @@ def src_split( src ):
chrom = spec
return spec, chrom
def src_merge( spec, chrom, contig = None ):
def src_merge( spec, chrom, contig=None ):
if None in [ spec, chrom ]:
spec = chrom = spec or chrom
return bx.align.maf.src_merge( spec, chrom, contig )
def get_species_in_block( block ):
species = []
for c in block.components:
@@ -43,14 +50,15 @@ def get_species_in_block( block ):
species.append( spec )
return species
def tool_fail( msg = "Unknown Error" ):
def tool_fail( msg="Unknown Error" ):
print >> sys.stderr, "Fatal Error: %s" % msg
sys.exit()
class TempFileHandler( object ):
'''
Handles creating, opening, closing, and deleting of Temp files, with a
Handles creating, opening, closing, and deleting of Temp files, with a
maximum number of files open at one time.
'''
@@ -75,7 +83,7 @@ class TempFileHandler( object ):
index = len( self.files )
temp_kwds = dict( self.kwds )
temp_kwds.update( kwds )
# Being able to use delete=True here, would simplify a bit,
# Being able to use delete=True here, would simplify a bit,
# but we support python2.4 in these tools
while True:
try:
@@ -121,17 +129,17 @@ class TempFileHandler( object ):
self.files[ index ].flush()
def __del__( self ):
for i in xrange( len( self.files ) ):
for i in xrange( len( self.files ) ):
self.close( i, delete=True )
#an object corresponding to a reference layered alignment
# an object corresponding to a reference layered alignment
class RegionAlignment( object ):
DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" )
MAX_SEQUENCE_SIZE = sys.maxint #Maximum length of sequence allowed
MAX_SEQUENCE_SIZE = sys.maxint # Maximum length of sequence allowed
def __init__( self, size, species = [], temp_file_handler = None ):
def __init__( self, size, species=[], temp_file_handler=None ):
assert size <= self.MAX_SEQUENCE_SIZE, "Maximum length allowed for an individual sequence has been exceeded (%i > %i)." % ( size, self.MAX_SEQUENCE_SIZE )
self.size = size
if not temp_file_handler:
@@ -143,49 +151,57 @@ class RegionAlignment( object ):
for spec in species:
self.add_species( spec )
#add a species to the alignment
# add a species to the alignment
def add_species( self, species ):
#make temporary sequence files
# make temporary sequence files
file_index, fh = self.temp_file_handler.get_open_tempfile()
self.sequences[species] = file_index
fh.write( "-" * self.size )
#returns the names for species found in alignment, skipping names as requested
def get_species_names( self, skip = [] ):
if not isinstance( skip, list ): skip = [skip]
# returns the names for species found in alignment, skipping names as requested
def get_species_names( self, skip=[] ):
if not isinstance( skip, list ):
skip = [skip]
names = self.sequences.keys()
for name in skip:
try: names.remove( name )
except: pass
try:
names.remove( name )
except:
pass
return names
#returns the sequence for a species
# returns the sequence for a species
def get_sequence( self, species ):
file_index, fh = self.temp_file_handler.get_open_tempfile( self.sequences[species] )
fh.seek( 0 )
return fh.read()
#returns the reverse complement of the sequence for a species
# returns the reverse complement of the sequence for a species
def get_sequence_reverse_complement( self, species ):
complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )]
complement.reverse()
return "".join( complement )
#sets a position for a species
# sets a position for a species
def set_position( self, index, species, base ):
if len( base ) != 1: raise Exception( "A genomic position can only have a length of 1." )
if len( base ) != 1:
raise Exception( "A genomic position can only have a length of 1." )
return self.set_range( index, species, base )
#sets a range for a species
# sets a range for a species
def set_range( self, index, species, bases ):
if index >= self.size or index < 0: raise Exception( "Your index (%i) is out of range (0 - %i)." % ( index, self.size - 1 ) )
if len( bases ) == 0: raise Exception( "A set of genomic positions can only have a positive length." )
if species not in self.sequences.keys(): self.add_species( species )
if index >= self.size or index < 0:
raise Exception( "Your index (%i) is out of range (0 - %i)." % ( index, self.size - 1 ) )
if len( bases ) == 0:
raise Exception( "A set of genomic positions can only have a positive length." )
if species not in self.sequences.keys():
self.add_species( species )
file_index, fh = self.temp_file_handler.get_open_tempfile( self.sequences[species] )
fh.seek( index )
fh.write( bases )
#Flush temp file of specified species, or all species
def flush( self, species = None ):
# Flush temp file of specified species, or all species
def flush( self, species=None ):
if species is None:
species = self.sequences.keys()
elif not isinstance( species, list ):
@@ -193,18 +209,20 @@ class RegionAlignment( object ):
for spec in species:
self.temp_file_handler.flush( self.sequences[spec] )
class GenomicRegionAlignment( RegionAlignment ):
def __init__( self, start, end, species = [], temp_file_handler = None ):
def __init__( self, start, end, species=[], temp_file_handler=None ):
RegionAlignment.__init__( self, end - start, species, temp_file_handler=temp_file_handler )
self.start = start
self.end = end
class SplicedAlignment( object ):
DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" )
def __init__( self, exon_starts, exon_ends, species = [], temp_file_handler = None ):
def __init__( self, exon_starts, exon_ends, species=[], temp_file_handler=None ):
if not isinstance( exon_starts, list ):
exon_starts = [exon_starts]
if not isinstance( exon_ends, list ):
@@ -217,17 +235,18 @@ class SplicedAlignment( object ):
for i in range( len( exon_starts ) ):
self.exons.append( GenomicRegionAlignment( exon_starts[i], exon_ends[i], species, temp_file_handler=temp_file_handler ) )
#returns the names for species found in alignment, skipping names as requested
def get_species_names( self, skip = [] ):
if not isinstance( skip, list ): skip = [skip]
# returns the names for species found in alignment, skipping names as requested
def get_species_names( self, skip=[] ):
if not isinstance( skip, list ):
skip = [skip]
names = []
for exon in self.exons:
for name in exon.get_species_names( skip = skip ):
for name in exon.get_species_names( skip=skip ):
if name not in names:
names.append( name )
return names
#returns the sequence for a species
# returns the sequence for a species
def get_sequence( self, species ):
index, fh = self.temp_file_handler.get_open_tempfile()
for exon in self.exons:
@@ -244,45 +263,50 @@ class SplicedAlignment( object ):
self.temp_file_handler.close( index, delete=True )
return rval
#returns the reverse complement of the sequence for a species
# returns the reverse complement of the sequence for a species
def get_sequence_reverse_complement( self, species ):
complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )]
complement.reverse()
return "".join( complement )
#Start and end of coding region
# Start and end of coding region
@property
def start( self ):
return self.exons[0].start
@property
def end( self ):
return self.exons[-1].end
#Open a MAF index using a UID
# Open a MAF index using a UID
def maf_index_by_uid( maf_uid, index_location_file ):
for line in open( index_location_file ):
try:
#read each line, if not enough fields, go to next line
if line[0:1] == "#" : continue
# read each line, if not enough fields, go to next line
if line[0:1] == "#":
continue
fields = line.split('\t')
if maf_uid == fields[1]:
try:
maf_files = fields[4].replace( "\n", "" ).replace( "\r", "" ).split( "," )
return bx.align.maf.MultiIndexed( maf_files, keep_open = True, parse_e_rows = False )
return bx.align.maf.MultiIndexed( maf_files, keep_open=True, parse_e_rows=False )
except Exception, e:
raise Exception( 'MAF UID (%s) found, but configuration appears to be malformed: %s' % ( maf_uid, e ) )
except:
pass
return None
#return ( index, temp_index_filename ) for user maf, if available, or build one and return it, return None when no tempfile is created
def open_or_build_maf_index( maf_file, index_filename, species = None ):
try:
return ( bx.align.maf.Indexed( maf_file, index_filename = index_filename, keep_open = True, parse_e_rows = False ), None )
except:
return build_maf_index( maf_file, species = species )
def build_maf_index_species_chromosomes( filename, index_species = None ):
# return ( index, temp_index_filename ) for user maf, if available, or build one and return it, return None when no tempfile is created
def open_or_build_maf_index( maf_file, index_filename, species=None ):
try:
return ( bx.align.maf.Indexed( maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False ), None )
except:
return build_maf_index( maf_file, species=species )
def build_maf_index_species_chromosomes( filename, index_species=None ):
species = []
species_chromosomes = {}
indexes = bx.interval_index_file.Indexes()
@@ -312,51 +336,55 @@ def build_maf_index_species_chromosomes( filename, index_species = None ):
forward_strand_start = int( forward_strand_start )
forward_strand_end = int( forward_strand_end )
except ValueError:
continue #start and end are not integers, can't add component to index, goto next component
#this likely only occurs when parse_e_rows is True?
#could a species exist as only e rows? should the
continue # start and end are not integers, can't add component to index, goto next component
# this likely only occurs when parse_e_rows is True?
# could a species exist as only e rows? should the
if forward_strand_end > forward_strand_start:
#require positive length; i.e. certain lines have start = end = 0 and cannot be indexed
# require positive length; i.e. certain lines have start = end = 0 and cannot be indexed
indexes.add( c.src, forward_strand_start, forward_strand_end, pos, max=c.src_size )
except Exception, e:
#most likely a bad MAF
# most likely a bad MAF
log.debug( 'Building MAF index on %s failed: %s' % ( filename, e ) )
return ( None, [], {}, 0 )
return ( indexes, species, species_chromosomes, blocks )
#builds and returns ( index, index_filename ) for specified maf_file
def build_maf_index( maf_file, species = None ):
# builds and returns ( index, index_filename ) for specified maf_file
def build_maf_index( maf_file, species=None ):
indexes, found_species, species_chromosomes, blocks = build_maf_index_species_chromosomes( maf_file, species )
if indexes is not None:
fd, index_filename = tempfile.mkstemp()
out = os.fdopen( fd, 'w' )
indexes.write( out )
out.close()
return ( bx.align.maf.Indexed( maf_file, index_filename = index_filename, keep_open = True, parse_e_rows = False ), index_filename )
return ( bx.align.maf.Indexed( maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False ), index_filename )
return ( None, None )
def component_overlaps_region( c, region ):
if c is None: return False
if c is None:
return False
start, end = c.get_forward_strand_start(), c.get_forward_strand_end()
if region.start >= end or region.end <= start:
return False
return True
def chop_block_by_region( block, src, region, species = None, mincols = 0 ):
def chop_block_by_region( block, src, region, species=None, mincols=0 ):
# This chopping method was designed to maintain consistency with how start/end padding gaps have been working in Galaxy thus far:
# behavior as seen when forcing blocks to be '+' relative to src sequence (ref) and using block.slice_by_component( ref, slice_start, slice_end )
# whether-or-not this is the 'correct' behavior is questionable, but this will at least maintain consistency
# comments welcome
slice_start = block.text_size #max for the min()
slice_end = 0 #min for the max()
old_score = block.score #save old score for later use
slice_start = block.text_size # max for the min()
slice_end = 0 # min for the max()
old_score = block.score # save old score for later use
# We no longer assume only one occurance of src per block, so we need to check them all
for c in iter_components_by_src( block, src ):
if component_overlaps_region( c, region ):
if c.text is not None:
rev_strand = False
if c.strand == "-":
#We want our coord_to_col coordinates to be returned from positive stranded component
# We want our coord_to_col coordinates to be returned from positive stranded component
rev_strand = True
c = c.reverse_complement()
start = max( region.start, c.start )
@@ -364,7 +392,7 @@ def chop_block_by_region( block, src, region, species = None, mincols = 0 ):
start = c.coord_to_col( start )
end = c.coord_to_col( end )
if rev_strand:
#need to orient slice coordinates to the original block direction
# need to orient slice coordinates to the original block direction
slice_len = end - start
end = len( c.text ) - start
start = end - slice_len
@@ -382,39 +410,45 @@ def chop_block_by_region( block, src, region, species = None, mincols = 0 ):
return block
return None
def orient_block_by_region( block, src, region, force_strand = None ):
#loop through components matching src,
#make sure each of these components overlap region
#cache strand for each of overlaping regions
#if force_strand / region.strand not in strand cache, reverse complement
### we could have 2 sequences with same src, overlapping region, on different strands, this would cause no reverse_complementing
def orient_block_by_region( block, src, region, force_strand=None ):
# loop through components matching src,
# make sure each of these components overlap region
# cache strand for each of overlaping regions
# if force_strand / region.strand not in strand cache, reverse complement
# we could have 2 sequences with same src, overlapping region, on different strands, this would cause no reverse_complementing
strands = [ c.strand for c in iter_components_by_src( block, src ) if component_overlaps_region( c, region ) ]
if strands and ( force_strand is None and region.strand not in strands ) or ( force_strand is not None and force_strand not in strands ):
block = block.reverse_complement()
return block
def get_oriented_chopped_blocks_for_region( index, src, region, species = None, mincols = 0, force_strand = None ):
def get_oriented_chopped_blocks_for_region( index, src, region, species=None, mincols=0, force_strand=None ):
for block, idx, offset in get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols, force_strand ):
yield block
def get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species = None, mincols = 0, force_strand = None ):
def get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species=None, mincols=0, force_strand=None ):
for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ):
yield orient_block_by_region( block, src, region, force_strand ), idx, offset
#split a block with multiple occurances of src into one block per src
# split a block with multiple occurances of src into one block per src
def iter_blocks_split_by_src( block, src ):
for src_c in iter_components_by_src( block, src ):
new_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) )
new_block.text_size = block.text_size
for c in block.components:
if c == src_c or c.src != src:
new_block.add_component( deepcopy( c ) ) #components have reference to alignment, dont want to loose reference to original alignment block in original components
new_block.add_component( deepcopy( c ) ) # components have reference to alignment, dont want to loose reference to original alignment block in original components
yield new_block
#split a block into multiple blocks with all combinations of a species appearing only once per block
def iter_blocks_split_by_species( block, species = None ):
# split a block into multiple blocks with all combinations of a species appearing only once per block
def iter_blocks_split_by_species( block, species=None ):
def __split_components_by_species( components_by_species, new_block ):
if components_by_species:
#more species with components to add to this block
# more species with components to add to this block
components_by_species = deepcopy( components_by_species )
spec_comps = components_by_species.pop( 0 )
for c in spec_comps:
@@ -423,10 +457,10 @@ def iter_blocks_split_by_species( block, species = None ):
for value in __split_components_by_species( components_by_species, newer_block ):
yield value
else:
#no more components to add, yield this block
# no more components to add, yield this block
yield new_block
#divide components by species
# divide components by species
spec_dict = {}
if not species:
species = []
@@ -442,41 +476,47 @@ def iter_blocks_split_by_species( block, species = None ):
for c in iter_components_by_src_start( block, spec ):
spec_dict[ spec ].append( c )
empty_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) ) #should we copy attributes?
empty_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) ) # should we copy attributes?
empty_block.text_size = block.text_size
#call recursive function to split into each combo of spec/blocks
# call recursive function to split into each combo of spec/blocks
for value in __split_components_by_species( spec_dict.values(), empty_block ):
sort_block_components_by_block( value, block ) #restore original component order
sort_block_components_by_block( value, block ) # restore original component order
yield value
#generator yielding only chopped and valid blocks for a specified region
def get_chopped_blocks_for_region( index, src, region, species = None, mincols = 0 ):
# generator yielding only chopped and valid blocks for a specified region
def get_chopped_blocks_for_region( index, src, region, species=None, mincols=0 ):
for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ):
yield block
def get_chopped_blocks_with_index_offset_for_region( index, src, region, species = None, mincols = 0 ):
def get_chopped_blocks_with_index_offset_for_region( index, src, region, species=None, mincols=0 ):
for block, idx, offset in index.get_as_iterator_with_index_and_offset( src, region.start, region.end ):
block = chop_block_by_region( block, src, region, species, mincols )
if block is not None:
yield block, idx, offset
#returns a filled region alignment for specified regions
def get_region_alignment( index, primary_species, chrom, start, end, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True, temp_file_handler = None ):
if species is not None: alignment = RegionAlignment( end - start, species, temp_file_handler=temp_file_handler )
else: alignment = RegionAlignment( end - start, primary_species, temp_file_handler=temp_file_handler )
# returns a filled region alignment for specified regions
def get_region_alignment( index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None ):
if species is not None:
alignment = RegionAlignment( end - start, species, temp_file_handler=temp_file_handler )
else:
alignment = RegionAlignment( end - start, primary_species, temp_file_handler=temp_file_handler )
return fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand, species, mincols, overwrite_with_gaps )
#reduces a block to only positions exisiting in the src provided
# reduces a block to only positions exisiting in the src provided
def reduce_block_by_primary_genome( block, species, chromosome, region_start ):
#returns ( startIndex, {species:texts}
#where texts' contents are reduced to only positions existing in the primary genome
# returns ( startIndex, {species:texts}
# where texts' contents are reduced to only positions existing in the primary genome
src = "%s.%s" % ( species, chromosome )
ref = block.get_component_by_src( src )
start_offset = ref.start - region_start
species_texts = {}
for c in block.components:
species_texts[ c.src.split( '.' )[0] ] = list( c.text )
#remove locations which are gaps in the primary species, starting from the downstream end
# remove locations which are gaps in the primary species, starting from the downstream end
for i in range( len( species_texts[ species ] ) - 1, -1, -1 ):
if species_texts[ species ][i] == '-':
for text in species_texts.values():
@@ -485,14 +525,15 @@ def reduce_block_by_primary_genome( block, species, chromosome, region_start ):
species_texts[spec] = ''.join( text )
return ( start_offset, species_texts )
#fills a region alignment
def fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True ):
# fills a region alignment
def fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True ):
region = bx.intervals.Interval( start, end )
region.chrom = chrom
region.strand = strand
primary_src = "%s.%s" % ( primary_species, chrom )
#Order blocks overlaping this position by score, lowest first
# Order blocks overlaping this position by score, lowest first
blocks = []
for block, idx, offset in index.get_as_iterator_with_index_and_offset( primary_src, start, end ):
score = float( block.score )
@@ -503,21 +544,21 @@ def fill_region_alignment( alignment, index, primary_species, chrom, start, end,
else:
blocks.append( ( score, idx, offset ) )
#gap_chars_tuple = tuple( GAP_CHARS )
# gap_chars_tuple = tuple( GAP_CHARS )
gap_chars_str = ''.join( GAP_CHARS )
#Loop through ordered blocks and layer by increasing score
# Loop through ordered blocks and layer by increasing score
for block_dict in blocks:
for block in iter_blocks_split_by_species( block_dict[1].get_at_offset( block_dict[2] ) ): #need to handle each occurance of sequence in block seperately
for block in iter_blocks_split_by_species( block_dict[1].get_at_offset( block_dict[2] ) ): # need to handle each occurance of sequence in block seperately
if component_overlaps_region( block.get_component_by_src( primary_src ), region ):
block = chop_block_by_region( block, primary_src, region, species, mincols ) #chop block
block = orient_block_by_region( block, primary_src, region ) #orient block
block = chop_block_by_region( block, primary_src, region, species, mincols ) # chop block
block = orient_block_by_region( block, primary_src, region ) # orient block
start_offset, species_texts = reduce_block_by_primary_genome( block, primary_species, chrom, start )
for spec, text in species_texts.items():
#we should trim gaps from both sides, since these are not positions in this species genome (sequence)
# we should trim gaps from both sides, since these are not positions in this species genome (sequence)
text = text.rstrip( gap_chars_str )
gap_offset = 0
while True in [ text.startswith( gap_char ) for gap_char in GAP_CHARS ]: #python2.4 doesn't accept a tuple for .startswith()
#while text.startswith( gap_chars_tuple ):
# while text.startswith( gap_chars_tuple ):
while True in [ text.startswith( gap_char ) for gap_char in GAP_CHARS ]: # python2.4 doesn't accept a tuple for .startswith()
gap_offset += 1
text = text[1:]
if not text:
@@ -531,48 +572,51 @@ def fill_region_alignment( alignment, index, primary_species, chrom, start, end,
alignment.set_position( start_offset + gap_offset + i, spec, char )
return alignment
#returns a filled spliced region alignment for specified region with start and end lists
def get_spliced_region_alignment( index, primary_species, chrom, starts, ends, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True, temp_file_handler = None ):
#create spliced alignment object
if species is not None: alignment = SplicedAlignment( starts, ends, species, temp_file_handler=temp_file_handler )
else: alignment = SplicedAlignment( starts, ends, [primary_species], temp_file_handler=temp_file_handler )
# returns a filled spliced region alignment for specified region with start and end lists
def get_spliced_region_alignment( index, primary_species, chrom, starts, ends, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None ):
# create spliced alignment object
if species is not None:
alignment = SplicedAlignment( starts, ends, species, temp_file_handler=temp_file_handler )
else:
alignment = SplicedAlignment( starts, ends, [primary_species], temp_file_handler=temp_file_handler )
for exon in alignment.exons:
fill_region_alignment( exon, index, primary_species, chrom, exon.start, exon.end, strand, species, mincols, overwrite_with_gaps )
return alignment
#loop through string array, only return non-commented lines
def line_enumerator( lines, comment_start = '#' ):
# loop through string array, only return non-commented lines
def line_enumerator( lines, comment_start='#' ):
i = 0
for line in lines:
if not line.startswith( comment_start ):
i += 1
yield ( i, line )
#read a GeneBed file, return list of starts, ends, raw fields
# read a GeneBed file, return list of starts, ends, raw fields
def get_starts_ends_fields_from_gene_bed( line ):
#Starts and ends for exons
# Starts and ends for exons
starts = []
ends = []
fields = line.split()
#Requires atleast 12 BED columns
# Requires atleast 12 BED columns
if len(fields) < 12:
raise Exception( "Not a proper 12 column BED line (%s)." % line )
chrom = fields[0]
tx_start = int( fields[1] )
tx_end = int( fields[2] )
name = fields[3]
strand = fields[5]
if strand != '-': strand='+' #Default strand is +
tx_start = int( fields[1] )
strand = fields[5]
if strand != '-':
strand = '+' # Default strand is +
cds_start = int( fields[6] )
cds_end = int( fields[7] )
cds_end = int( fields[7] )
#Calculate and store starts and ends of coding exons
# Calculate and store starts and ends of coding exons
region_start, region_end = cds_start, cds_end
exon_starts = map( int, fields[11].rstrip( ',\n' ).split( ',' ) )
exon_starts = map( ( lambda x: x + tx_start ), exon_starts )
exon_ends = map( int, fields[10].rstrip( ',' ).split( ',' ) )
exon_ends = map( ( lambda x, y: x + y ), exon_starts, exon_ends );
exon_ends = map( ( lambda x, y: x + y ), exon_starts, exon_ends )
for start, end in zip( exon_starts, exon_ends ):
start = max( start, region_start )
end = min( end, region_end )
@@ -581,27 +625,33 @@ def get_starts_ends_fields_from_gene_bed( line ):
ends.append( end )
return ( starts, ends, fields )
def iter_components_by_src( block, src ):
for c in block.components:
if c.src == src:
yield c
def get_components_by_src( block, src ):
return [ value for value in iter_components_by_src( block, src ) ]
def iter_components_by_src_start( block, src ):
for c in block.components:
if c.src.startswith( src ):
yield c
def get_components_by_src_start( block, src ):
return [ value for value in iter_components_by_src_start( block, src ) ]
def sort_block_components_by_block( block1, block2 ):
#orders the components in block1 by the index of the component in block2
#block1 must be a subset of block2
#occurs in-place
return block1.components.sort( cmp = lambda x, y: block2.components.index( x ) - block2.components.index( y ) )
# orders the components in block1 by the index of the component in block2
# block1 must be a subset of block2
# occurs in-place
return block1.components.sort( cmp=lambda x, y: block2.components.index( x ) - block2.components.index( y ) )
def get_species_in_maf( maf_filename ):
species = []
@@ -611,20 +661,25 @@ def get_species_in_maf( maf_filename ):
species.append( spec )
return species
def parse_species_option( species ):
if species:
species = species.split( ',' )
if 'None' not in species:
return species
return None #provided species was '', None, or had 'None' in it
return None # provided species was '', None, or had 'None' in it
def remove_temp_index_file( index_filename ):
try: os.unlink( index_filename )
except: pass
try:
os.unlink( index_filename )
except:
pass
#Below are methods to deal with FASTA files
# Below are methods to deal with FASTA files
def get_fasta_header( component, attributes = {}, suffix = None ):
def get_fasta_header( component, attributes={}, suffix=None ):
header = ">%s(%s):%i-%i|" % ( component.src, component.strand, component.get_forward_strand_start(), component.get_forward_strand_end() )
for key, value in attributes.iteritems():
header = "%s%s=%s|" % ( header, key, value )
@@ -634,8 +689,10 @@ def get_fasta_header( component, attributes = {}, suffix = None ):
header = "%s%s" % ( header, src_split( component.src )[ 0 ] )
return header
def get_attributes_from_fasta_header( header ):
if not header: return {}
if not header:
return {}
attributes = {}
header = header.lstrip( '>' )
header = header.strip()
@@ -655,7 +712,7 @@ def get_attributes_from_fasta_header( header ):
attributes['start'] = int( region[0] )
attributes['end'] = int( region[1] )
except:
#fields 0 is not a region coordinate
# fields 0 is not a region coordinate
pass
if len( fields ) > 2:
for i in xrange( 1, len( fields ) - 1 ):
@@ -666,17 +723,19 @@ def get_attributes_from_fasta_header( header ):
attributes['__suffix__'] = fields[-1]
return attributes
def iter_fasta_alignment( filename ):
class fastaComponent:
def __init__( self, species, text = "" ):
def __init__( self, species, text="" ):
self.species = species
self.text = text
def extend( self, text ):
self.text = self.text + text.replace( '\n', '' ).replace( '\r', '' ).strip()
#yields a list of fastaComponents for a FASTA file
# yields a list of fastaComponents for a FASTA file
f = open( filename, 'rb' )
components = []
#cur_component = None
# cur_component = None
while True:
line = f.readline()
if not line:
@@ -693,4 +752,3 @@ def iter_fasta_alignment( filename ):
components.append( fastaComponent( attributes['species'] ) )
elif components:
components[-1].extend( line )
+4 -3
View File
@@ -50,7 +50,7 @@ class RawObjectWrapper( ToolParameterValueWrapper ):
try:
return "%s:%s" % (self.obj.__module__, self.obj.__class__.__name__)
except:
#Most likely None, which lacks __module__.
# Most likely None, which lacks __module__.
return str( self.obj )
def __getattr__( self, key ):
@@ -159,7 +159,7 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
# again
setattr( self, name, rval )
else:
#escape string value of non-defined metadata value
# escape string value of non-defined metadata value
rval = wrap_with_safe_string( rval )
return rval
@@ -187,7 +187,7 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
ext = 'data'
self.dataset = wrap_with_safe_string( NoneDataset( datatypes_registry=datatypes_registry, ext=ext ), no_wrap_classes=ToolParameterValueWrapper )
else:
# Tool wrappers should not normally be accessing .dataset directly,
# Tool wrappers should not normally be accessing .dataset directly,
# so we will wrap it and keep the original around for file paths
# Should we name this .value to maintain consistency with most other ToolParameterValueWrapper?
self.unsanitized = dataset
@@ -276,6 +276,7 @@ class DatasetListWrapper( list, ToolParameterValueWrapper, HasDatasets ):
return self._dataset_wrapper( dataset, dataset_paths, **kwargs )
list.__init__( self, map( to_wrapper, datasets ) )
def __str__( self ):
return ','.join( map( str, self ) )
+1 -1
View File
@@ -279,7 +279,7 @@ class WorkflowSummary( object ):
return hdca
def __original_hda( self, hda ):
#if this hda was copied from another, we need to find the job that created the origial hda
# if this hda was copied from another, we need to find the job that created the origial hda
job_hda = hda
while job_hda.copied_from_history_dataset_association:
job_hda = job_hda.copied_from_history_dataset_association
+11 -13
View File
@@ -5,8 +5,6 @@ Modules used in building workflows
import logging
import re
from galaxy import eggs
from xml.etree.ElementTree import Element
import galaxy.tools
@@ -17,8 +15,8 @@ from galaxy.dataset_collections import matching
from galaxy.web.framework import formbuilder
from galaxy.jobs.actions.post import ActionBox
from galaxy.model import PostJobAction
from galaxy.tools.parameters import params_to_incoming, check_param, DataToolParameter, DummyDataset, RuntimeValue, visit_input_values
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters import check_param, visit_input_values
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter, DummyDataset, RuntimeValue
from galaxy.tools.parameters.wrapped import make_dict_copy
from galaxy.tools.execute import execute
from galaxy.util.bunch import Bunch
@@ -41,7 +39,7 @@ class WorkflowModule( object ):
def __init__( self, trans ):
self.trans = trans
## ---- Creating modules from various representations ---------------------
# ---- Creating modules from various representations ---------------------
@classmethod
def new( Class, trans, tool_id=None ):
@@ -62,12 +60,12 @@ class WorkflowModule( object ):
def from_workflow_step( Class, trans, step ):
return Class( trans )
## ---- Saving in various forms ------------------------------------------
# ---- Saving in various forms ------------------------------------------
def save_to_step( self, step ):
step.type = self.type
## ---- General attributes -----------------------------------------------
# ---- General attributes -----------------------------------------------
def get_type( self ):
return self.type
@@ -81,7 +79,7 @@ class WorkflowModule( object ):
def get_tooltip( self, static_path='' ):
return None
## ---- Configuration time -----------------------------------------------
# ---- Configuration time -----------------------------------------------
def get_state( self ):
""" Return a serializable representation of the persistable state of
@@ -132,7 +130,7 @@ class WorkflowModule( object ):
# Replaced connected inputs with DummyDataset values.
pass
## ---- Run time ---------------------------------------------------------
# ---- Run time ---------------------------------------------------------
def get_runtime_inputs( self ):
""" Used internally by modules and when displaying inputs in workflow
@@ -653,7 +651,7 @@ class ToolModule( WorkflowModule ):
input_type="dataset_collection",
collection_type=input.collection_type,
extensions=input.extensions,
) )
) )
visit_input_values( self.tool.inputs, self.state.inputs, callback )
return data_inputs
@@ -669,13 +667,13 @@ class ToolModule( WorkflowModule ):
formats = [ 'input' ] # TODO: fix
elif tool_output.format_source is not None:
formats = [ 'input' ] # default to special name "input" which remove restrictions on connections
if data_inputs == None:
if data_inputs is None:
data_inputs = self.get_data_inputs()
# find the input parameter referenced by format_source
for di in data_inputs:
# input names come prefixed with conditional and repeat names separated by '|'
# remove prefixes when comparing with format_source
if di['name'] != None and di['name'].split('|')[-1] == tool_output.format_source:
if di['name'] is not None and di['name'].split('|')[-1] == tool_output.format_source:
formats = di['extensions']
else:
formats = [ tool_output.format ]
@@ -1048,7 +1046,7 @@ class WorkflowModuleInjector(object):
input_connections_by_name = {}
for conn in step.input_connections:
input_name = conn.input_name
if not input_name in input_connections_by_name:
if input_name not 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
+4 -4
View File
@@ -233,10 +233,10 @@ def build_workflow_run_config( trans, workflow, payload ):
trans.security.decode_id(input_id))
assert trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), content.dataset )
elif input_source == 'uuid':
dataset = trans.sa_session.query(app.model.Dataset).filter(app.model.Dataset.uuid==input_id).first()
dataset = trans.sa_session.query(app.model.Dataset).filter(app.model.Dataset.uuid == input_id).first()
if dataset is None:
#this will need to be changed later. If federation code is avalible, then a missing UUID
#could be found amoung fereration partners
# this will need to be changed later. If federation code is avalible, then a missing UUID
# could be found amoung fereration partners
message = "Input cannot find UUID: %s." % input_id
raise exceptions.RequestParameterInvalidException( message )
assert trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), dataset )
@@ -330,7 +330,7 @@ def workflow_request_to_run_config( work_request_context, workflow_invocation ):
if parameter.name == "copy_inputs_to_history":
copy_inputs_to_history = (parameter.value == "true")
#for parameter in workflow_invocation.step_parameters:
# for parameter in workflow_invocation.step_parameters:
# step_id = parameter.workflow_step_id
# if step_id not in param_map:
# param_map[ step_id ] = {}
+1 -1
View File
@@ -30,7 +30,7 @@ def order_workflow_steps( steps ):
"""
position_data_available = True
for step in steps:
if not step.position or not 'left' in step.position or not 'top' in step.position:
if not step.position or 'left' not in step.position or 'top' not in step.position:
position_data_available = False
if position_data_available:
steps.sort(cmp=lambda s1, s2: cmp( math.sqrt(s1.position['left'] ** 2 + s1.position['top'] ** 2), math.sqrt(s2.position['left'] ** 2 + s2.position['top'] ** 2)))
@@ -1,6 +1,10 @@
#!/usr/bin/env python
import os, sys, logging, string, textwrap
import logging
import os
import string
import sys
import textwrap
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] )
@@ -10,28 +14,29 @@ log = logging.getLogger()
log.setLevel( 10 )
log.addHandler( logging.StreamHandler( sys.stdout ) )
from galaxy import eggs
import pkg_resources
pkg_resources.require( "SQLAlchemy >= 0.4" )
import time, ConfigParser, shutil
import time
import ConfigParser
from datetime import datetime, timedelta
from time import strftime
from optparse import OptionParser
from galaxy.tools import parameters
from tool_shed.util.common_util import url_join
import galaxy.webapps.tool_shed.config as tool_shed_config
import galaxy.webapps.tool_shed.model.mapping
import sqlalchemy as sa
from galaxy.model.orm import and_, not_, distinct
from sqlalchemy import and_, distinct, not_
from galaxy.util import send_mail as galaxy_send_mail
assert sys.version_info[:2] >= ( 2, 4 )
def build_citable_url( host, repository ):
return url_join( host, 'view', repository.user.username, repository.name )
def main():
'''
Script to deprecate any repositories that are older than n days, and have been empty since creation.
@@ -42,7 +47,7 @@ def main():
parser.add_option( "-v", "--verbose", action="store_true", dest="verbose", help="verbose mode, print the name of each repository", default=False )
( options, args ) = parser.parse_args()
ini_file = args[0]
config_parser = ConfigParser.ConfigParser( {'here':os.getcwd()} )
config_parser = ConfigParser.ConfigParser( {'here': os.getcwd()} )
config_parser.read( ini_file )
config_dict = {}
for key, value in config_parser.items( "app:main" ):
@@ -60,6 +65,7 @@ def main():
deprecate_repositories( app, cutoff_time, days=options.days, info_only=options.info_only, verbose=options.verbose )
def send_mail_to_owner( app, name, owner, email, repositories_deprecated, days=14 ):
'''
Sends an email to the owner of the provided repository.
@@ -77,8 +83,8 @@ def send_mail_to_owner( app, name, owner, email, repositories_deprecated, days=1
return
subject = "Regarding your tool shed repositories at %s" % url
message_body_template = 'The tool shed automated repository checker has discovered that one or more of your repositories hosted ' + \
'at this tool shed url ${url} have remained empty for over ${days} days, so they have been marked as deprecated. If you have plans ' + \
'for these repositories, you can mark them as un-deprecated at any time.'
'at this tool shed url ${url} have remained empty for over ${days} days, so they have been marked as deprecated. If you have plans ' + \
'for these repositories, you can mark them as un-deprecated at any time.'
message_template = string.Template( message_body_template )
body = '\n'.join( textwrap.wrap( message_template.safe_substitute( days=days, url=url ), width=95 ) )
body += '\n\n'
@@ -92,12 +98,11 @@ def send_mail_to_owner( app, name, owner, email, repositories_deprecated, days=1
print "# An error occurred attempting to send email: %s" % str( e )
return False
def deprecate_repositories( app, cutoff_time, days=14, info_only=False, verbose=False ):
# This method will get a list of repositories that were created on or before cutoff_time, but have never
# had any metadata records associated with them. Then it will iterate through that list and deprecate the
# repositories, sending an email to each repository owner.
dataset_count = 0
disk_space = 0
start = time.time()
repository_ids_to_not_check = []
# Get a unique list of repository ids from the repository_metadata table. Any repository ID found in this table is not
@@ -110,11 +115,11 @@ def deprecate_repositories( app, cutoff_time, days=14, info_only=False, verbose=
# Get the repositories that are A) not present in the above list, and b) older than the specified time.
# This will yield a list of repositories that have been created more than n days ago, but never populated.
repository_query = sa.select( [ app.model.Repository.table.c.id ],
whereclause = and_( app.model.Repository.table.c.create_time < cutoff_time,
app.model.Repository.table.c.deprecated == False,
app.model.Repository.table.c.deleted == False,
not_( app.model.Repository.table.c.id.in_( repository_ids_to_not_check ) ) ),
from_obj = [ app.model.Repository.table ] )
whereclause=and_( app.model.Repository.table.c.create_time < cutoff_time,
app.model.Repository.table.c.deprecated == False,
app.model.Repository.table.c.deleted == False,
not_( app.model.Repository.table.c.id.in_( repository_ids_to_not_check ) ) ),
from_obj=[ app.model.Repository.table ] )
query_result = repository_query.execute()
repositories = []
repositories_by_owner = {}
@@ -122,8 +127,7 @@ def deprecate_repositories( app, cutoff_time, days=14, info_only=False, verbose=
# Iterate through the list of repository ids for empty repositories and deprecate them unless info_only is set.
for repository_id in repository_ids:
repository = app.sa_session.query( app.model.Repository ) \
.filter( app.model.Repository.table.c.id == repository_id ) \
.one()
.filter( app.model.Repository.table.c.id == repository_id ).one()
owner = repository.user
if info_only:
print '# Repository %s owned by %s would have been deprecated, but info_only was set.' % ( repository.name, repository.user.username )
@@ -147,6 +151,7 @@ def deprecate_repositories( app, cutoff_time, days=14, info_only=False, verbose=
print "# Elapsed time: ", stop - start
print "####################################################################################"
class DeprecateRepositoriesApplication( object ):
"""Encapsulates the state of a Universe application"""
def __init__( self, config ):
@@ -155,6 +160,7 @@ class DeprecateRepositoriesApplication( object ):
# Setup the database engine and ORM
self.model = galaxy.webapps.tool_shed.model.mapping.init( config.file_path, config.database_connection, engine_options={}, create_tables=False )
self.config = config
@property
def sa_session( self ):
"""
@@ -163,7 +169,9 @@ class DeprecateRepositoriesApplication( object ):
to allow migration toward a more SQLAlchemy 0.4 style of use.
"""
return self.model.context.current
def shutdown( self ):
pass
if __name__ == "__main__": main()
if __name__ == "__main__":
main()
+3 -3
View File
@@ -78,7 +78,7 @@ class RepoToolModule( ToolModule ):
data_inputs = []
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, galaxy.tools.parameters.DataToolParameter ):
if isinstance( input, galaxy.tools.parameters.basic.DataToolParameter ):
data_inputs.append( dict( name=prefixed_name,
label=prefixed_label,
extensions=input.extensions ) )
@@ -264,7 +264,7 @@ def get_workflow_from_dict( trans, workflow_dict, tools_metadata, repository_id,
# will be ( tool_id, tool_name, tool_version ).
missing_tool_tups = []
# First pass to build step objects and populate basic values
for key, step_dict in workflow_dict[ 'steps' ].iteritems():
for step_dict in workflow_dict[ 'steps' ].itervalues():
# Create the model class for the step
step = trans.model.WorkflowStep()
step.name = step_dict[ 'name' ]
@@ -291,7 +291,7 @@ def get_workflow_from_dict( trans, workflow_dict, tools_metadata, repository_id,
step.annotations.append( new_step_annotation )
# Unpack and add post-job actions.
post_job_actions = step_dict.get( 'post_job_actions', {} )
for name, pja_dict in post_job_actions.items():
for pja_dict in post_job_actions.values():
trans.model.PostJobAction( pja_dict[ 'action_type' ],
step,
pja_dict[ 'output_name' ],
@@ -2,8 +2,7 @@
<%namespace file="/display_common.mako" import="render_message" />
<%!
from galaxy.tools.parameters import DataToolParameter, RuntimeValue
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter, RuntimeValue
from galaxy.web import form_builder
%>
@@ -17,8 +17,7 @@
${ h.dumps(self.form_config) }
%else:
<%
from galaxy.tools.parameters import DataToolParameter, RuntimeValue
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter, RuntimeValue
from galaxy.util.expressions import ExpressionContext
%>
@@ -143,4 +142,4 @@
</form>
%endif
%endif
+1 -2
View File
@@ -301,8 +301,7 @@
</%def>
<%
from galaxy.tools.parameters import DataToolParameter, RuntimeValue
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter, RuntimeValue
from galaxy.jobs.actions.post import ActionBox
import re
import colorsys
-6
View File
@@ -1,9 +1,3 @@
from galaxy.tools.parameters import DataToolParameter
def validate_input( trans, error_map, param_values, page_param_map ):
"""
Validates the user input, before execution.
+53 -39
View File
@@ -1,21 +1,25 @@
#!/usr/bin/env python
#Processes uploads from the user.
# Processes uploads from the user.
# WARNING: Changes in this tool (particularly as related to parsing) may need
# to be reflected in galaxy.web.controllers.tool_runner and galaxy.tools
import urllib, sys, os, gzip, tempfile, shutil, re, gzip, zipfile, codecs, binascii
from galaxy import eggs
# need to import model before sniff to resolve a circular import dependency
import galaxy.model
from galaxy.datatypes.checkers import *
from galaxy.datatypes import sniff
from galaxy.datatypes.binary import *
from galaxy.datatypes.images import Pdf
from galaxy.datatypes.registry import Registry
import codecs
import gzip
import os
import shutil
import sys
import tempfile
import urllib
import zipfile
from galaxy import util
from galaxy.datatypes.util.image_util import *
from galaxy.util.json import *
from galaxy.datatypes import sniff
from galaxy.datatypes.binary import Binary
from galaxy.datatypes.checkers import check_binary, check_bz2, check_gzip, check_html, check_image, check_zip
from galaxy.datatypes.registry import Registry
from galaxy.datatypes.util.image_util import get_image_ext
from galaxy.util.json import dumps, loads
try:
import Image as PIL
@@ -32,14 +36,17 @@ except:
assert sys.version_info[:2] >= ( 2, 4 )
def stop_err( msg, ret=1 ):
sys.stderr.write( msg )
sys.exit( ret )
def file_err( msg, dataset, json_file ):
json_file.write( dumps( dict( type = 'dataset',
ext = 'data',
dataset_id = dataset.dataset_id,
stderr = msg ) ) + "\n" )
json_file.write( dumps( dict( type='dataset',
ext='data',
dataset_id=dataset.dataset_id,
stderr=msg ) ) + "\n" )
# never remove a server-side upload
if dataset.type in ( 'server_dir', 'path_paste' ):
return
@@ -47,23 +54,29 @@ def file_err( msg, dataset, json_file ):
os.remove( dataset.path )
except:
pass
def safe_dict(d):
"""
Recursively clone json structure with UTF-8 dictionary keys
http://mellowmachines.com/blog/2009/06/exploding-dictionary-with-unicode-keys-as-python-arguments/
"""
if isinstance(d, dict):
return dict([(k.encode('utf-8'), safe_dict(v)) for k,v in d.iteritems()])
return dict([(k.encode('utf-8'), safe_dict(v)) for k, v in d.iteritems()])
elif isinstance(d, list):
return [safe_dict(x) for x in d]
else:
return d
def parse_outputs( args ):
rval = {}
for arg in args:
id, files_path, path = arg.split( ':', 2 )
rval[int( id )] = ( path, files_path )
return rval
def add_file( dataset, registry, json_file, output_path ):
data_type = None
line_count = None
@@ -80,7 +93,7 @@ def add_file( dataset, registry, json_file, output_path ):
if dataset.type == 'url':
try:
page = urllib.urlopen( dataset.path ) #page will be .close()ed by sniff methods
page = urllib.urlopen( dataset.path ) # page will be .close()ed by sniff methods
temp_name, dataset.is_multi_byte = sniff.stream_to_file( page, prefix='url_paste', source_encoding=util.get_charset_from_http_headers( page.headers ) )
except Exception, e:
file_err( 'Unable to fetch %s\n%s' % ( dataset.path, str( e ) ), dataset, json_file )
@@ -133,7 +146,7 @@ def add_file( dataset, registry, json_file, output_path ):
elif is_gzipped and is_valid:
if link_data_only == 'copy_files':
# We need to uncompress the temp_name file, but BAM files must remain compressed in the BGZF format
CHUNK_SIZE = 2**20 # 1Mb
CHUNK_SIZE = 2 ** 20 # 1Mb
fd, uncompressed = tempfile.mkstemp( prefix='data_id_%s_upload_gunzip_' % dataset.dataset_id, dir=os.path.dirname( output_path ), text=False )
gzipped_file = gzip.GzipFile( dataset.path, 'rb' )
while 1:
@@ -166,7 +179,7 @@ def add_file( dataset, registry, json_file, output_path ):
elif is_bzipped and is_valid:
if link_data_only == 'copy_files':
# We need to uncompress the temp_name file
CHUNK_SIZE = 2**20 # 1Mb
CHUNK_SIZE = 2 ** 20 # 1Mb
fd, uncompressed = tempfile.mkstemp( prefix='data_id_%s_upload_bunzip2_' % dataset.dataset_id, dir=os.path.dirname( output_path ), text=False )
bzipped_file = bz2.BZ2File( dataset.path, 'rb' )
while 1:
@@ -195,7 +208,7 @@ def add_file( dataset, registry, json_file, output_path ):
is_zipped = check_zip( dataset.path )
if is_zipped:
if link_data_only == 'copy_files':
CHUNK_SIZE = 2**20 # 1Mb
CHUNK_SIZE = 2 ** 20 # 1Mb
uncompressed = None
uncompressed_name = None
unzipped = False
@@ -255,7 +268,7 @@ def add_file( dataset, registry, json_file, output_path ):
if check_binary( dataset.path ) or Binary.is_ext_unsniffable(dataset.file_type):
# We have a binary dataset, but it is not Bam, Sff or Pdf
data_type = 'binary'
#binary_ok = False
# binary_ok = False
parts = dataset.name.split( "." )
if len( parts ) > 1:
ext = parts[-1].strip().lower()
@@ -318,12 +331,12 @@ def add_file( dataset, registry, json_file, output_path ):
shutil.move( dataset.path, output_path )
# Write the job info
stdout = stdout or 'uploaded %s file' % data_type
info = dict( type = 'dataset',
dataset_id = dataset.dataset_id,
ext = ext,
stdout = stdout,
name = dataset.name,
line_count = line_count )
info = dict( type='dataset',
dataset_id=dataset.dataset_id,
ext=ext,
stdout=stdout,
name=dataset.name,
line_count=line_count )
if dataset.get('uuid', None) is not None:
info['uuid'] = dataset.get('uuid')
json_file.write( dumps( info ) + "\n" )
@@ -332,6 +345,7 @@ def add_file( dataset, registry, json_file, output_path ):
# Groom the dataset content if necessary
datatype.groom_dataset_content( output_path )
def add_composite_file( dataset, registry, json_file, output_path, files_path ):
if dataset.composite_files:
os.mkdir( files_path )
@@ -342,15 +356,15 @@ def add_composite_file( dataset, registry, json_file, output_path, files_path ):
break
elif dataset.composite_file_paths[value.name] is not None:
dp = dataset.composite_file_paths[value.name][ 'path' ]
isurl = dp.find('://') <> -1 # todo fixme
isurl = dp.find('://') != -1 # todo fixme
if isurl:
try:
temp_name, dataset.is_multi_byte = sniff.stream_to_file( urllib.urlopen( dp ), prefix='url_paste' )
except Exception, e:
file_err( 'Unable to fetch %s\n%s' % ( dp, str( e ) ), dataset, json_file )
return
dataset.path = temp_name
dp = temp_name
try:
temp_name, dataset.is_multi_byte = sniff.stream_to_file( urllib.urlopen( dp ), prefix='url_paste' )
except Exception, e:
file_err( 'Unable to fetch %s\n%s' % ( dp, str( e ) ), dataset, json_file )
return
dataset.path = temp_name
dp = temp_name
if not value.is_binary:
tmpdir = output_adjacent_tmpdir( output_path )
tmp_prefix = 'data_id_%s_convert_' % dataset.dataset_id
@@ -362,9 +376,9 @@ def add_composite_file( dataset, registry, json_file, output_path, files_path ):
# Move the dataset to its "real" path
shutil.move( dataset.primary_file, output_path )
# Write the job info
info = dict( type = 'dataset',
dataset_id = dataset.dataset_id,
stdout = 'uploaded %s file' % dataset.file_type )
info = dict( type='dataset',
dataset_id=dataset.dataset_id,
stdout='uploaded %s file' % dataset.file_type )
json_file.write( dumps( info ) + "\n" )
+8 -12
View File
@@ -1,16 +1,14 @@
#!/usr/bin/env python
# runs after the job (and after the default post-filter)
import os
from galaxy import eggs
from galaxy import jobs
from galaxy.tools.parameters import DataToolParameter
from galaxy.tools.parameters.basic import DataToolParameter
# Older py compatibility
try:
set()
except:
from sets import Set as set
def validate_input( trans, error_map, param_values, page_param_map ):
dbkeys = set()
data_param_names = set()
@@ -18,22 +16,20 @@ def validate_input( trans, error_map, param_values, page_param_map ):
for name, param in page_param_map.iteritems():
if isinstance( param, DataToolParameter ):
# for each dataset parameter
if param_values.get(name, None) != None:
if param_values.get(name, None) is not None:
dbkeys.add( param_values[name].dbkey )
data_params += 1
# check meta data
try:
param = param_values[name]
startCol = int( param.metadata.startCol )
endCol = int( param.metadata.endCol )
chromCol = int( param.metadata.chromCol )
int( param.metadata.startCol )
int( param.metadata.endCol )
int( param.metadata.chromCol )
if param.metadata.strandCol is not None:
strandCol = int ( param.metadata.strandCol )
else:
strandCol = 0
int( param.metadata.strandCol )
except:
error_msg = "The attributes of this dataset are not properly set. " + \
"Click the pencil icon in the history item to set the chrom, start, end and strand columns."
"Click the pencil icon in the history item to set the chrom, start, end and strand columns."
error_map[name] = error_msg
data_param_names.add( name )
if len( dbkeys ) > 1: