1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/659fc6822f0a/ Changeset: 659fc6822f0a User: natefoo Date: 2015-01-14 03:34:26+00:00 Summary: Merge stable to default Affected #: 10 files diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 .hgtags --- a/.hgtags +++ b/.hgtags @@ -8,16 +8,18 @@ 5e605ed6069fe4c5ca9875e95e91b2713499e8ca release_2014.02.10 9e53251b0b7e93b9563008a2b112f2e815a04bbc release_2014.04.14 7e257c7b10badb65772b1528cb61d58175a42e47 release_2014.06.02 -7a4d321c0e38fa263ea83d29a35a608c3181fcba latest_2014.06.02 -9661b9d5d5b330483ae3ad2236410e0efaa7c500 latest_2014.04.14 -6b0bd93038a843b1585155f0d63f0eea2459c70b latest_2013.01.13 -3e62060b14b9afc46f8e0ec02e1a4500d77db9e1 latest_2013.02.08 -425009b3ff4d8b67d2812253b221f3c4f4a8d1e3 latest_2013.04.01 -9713d86392ef985ffcdc39ff0c8ddf51a1f9ce47 latest_2013.06.03 -9ed84cd208e07e8985ec917cb025fcbbb09edcfb latest_2013.08.12 -81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04 -2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10 +4145417a6e1c13f82a3de365aadef0fb3ed7ab14 latest_2014.06.02 +8f9dcac033694e4cabcf5daae5cca1cfefbe967f latest_2014.04.14 +9c323aad4ffdd65a3deb06a4a36f6b2c5115a60f latest_2013.01.13 +b986c184be88947b5d1d90be7f36cfd2627dd938 latest_2013.02.08 +dec9431d66b837a208e2f060d90afd913c721227 latest_2013.04.01 +19e56e66b0b344c6e2afa4541f6988e4fdb9af29 latest_2013.06.03 +cee903b8b3eee9145627ee89742555dac581791e latest_2013.08.12 +7d5aa19a166cba9039e15f338a1e3fc924c43d3a latest_2013.11.04 +0c000cc2f9c05bf4c1c2bc3a10215014fd64e696 latest_2014.02.10 ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11 -548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11 +8150024c0e6fc5aef3033cf8aaa574896f6b5d0d latest_2014.08.11 2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06 -5834b1066462dd219f67c1c3cbd77c78e7cf3a6c latest_2014.10.06 +c437b28348a9345db8433e5b4f0e05ec8fb6c38a latest_2014.10.06 +2e8dd2949dd3eee0f56f9a3a5ebf1b2baca24aee release_2015.01.13 +2e8dd2949dd3eee0f56f9a3a5ebf1b2baca24aee latest_2015.01.13 diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/datatypes/metadata.py --- a/lib/galaxy/datatypes/metadata.py +++ b/lib/galaxy/datatypes/metadata.py @@ -20,6 +20,7 @@ import galaxy.model from galaxy.util import listify +from galaxy.util.object_wrapper import sanitize_lists_to_string from galaxy.util import stringify_dictionary_keys from galaxy.util import string_as_bool from galaxy.util import in_directory @@ -232,6 +233,9 @@ def to_string( self, value ): return str( value ) + def to_safe_string( self, value ): + return sanitize_lists_to_string( self.to_string( value ) ) + def make_copy( self, value, target_context = None, source_context = None ): return copy.deepcopy( value ) @@ -480,6 +484,10 @@ def to_string( self, value ): return json.dumps( value ) + def to_safe_string( self, value ): + # We do not sanitize json dicts + return json.safe_dumps( value ) + class PythonObjectParameter( MetadataParameter ): @@ -510,6 +518,10 @@ return str( self.spec.no_value ) return value.file_name + def to_safe_string( self, value ): + # We do not sanitize file names + return self.to_string( value ) + def get_html_field( self, value=None, context=None, other_values=None, **kwd ): context = context or {} other_values = other_values or {} diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/tools/evaluation.py --- a/lib/galaxy/tools/evaluation.py +++ b/lib/galaxy/tools/evaluation.py @@ -2,10 +2,12 @@ import tempfile from galaxy import model +from galaxy.util.object_wrapper import wrap_with_safe_string from galaxy.util.bunch import Bunch from galaxy.util.none_like import NoneDataset from galaxy.util.template import fill_template from galaxy.tools.wrappers import ( + ToolParameterValueWrapper, DatasetFilenameWrapper, DatasetListWrapper, DatasetCollectionWrapper, @@ -114,6 +116,9 @@ self.__populate_input_dataset_wrappers(param_dict, input_datasets, input_dataset_paths) self.__populate_output_dataset_wrappers(param_dict, output_datasets, output_paths, job_working_directory) self.__populate_unstructured_path_rewrites(param_dict) + # Call param dict sanitizer, before non-job params are added, as we don't want to sanitize filenames. + self.__sanitize_param_dict( param_dict ) + # Parameters added after this line are not sanitized self.__populate_non_job_params(param_dict) # Return the dictionary of parameters @@ -334,6 +339,24 @@ #the paths rewritten. self.__walk_inputs( self.tool.inputs, param_dict, rewrite_unstructured_paths ) + def __sanitize_param_dict( self, param_dict ): + """ + 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. + skip = [ 'chromInfo' ] + if not self.tool or not self.tool.options or self.tool.options.sanitize: + for key, value in param_dict.items(): + if key not in skip: + # Remove key so that new wrapped object will occupy key slot + del param_dict[key] + # And replace with new wrapped key + param_dict[ wrap_with_safe_string( key, no_wrap_classes=ToolParameterValueWrapper ) ] = wrap_with_safe_string( value, no_wrap_classes=ToolParameterValueWrapper ) + def build( self ): """ Build runtime description of job to execute, evaluate command and diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/tools/wrappers.py --- a/lib/galaxy/tools/wrappers.py +++ b/lib/galaxy/tools/wrappers.py @@ -2,6 +2,7 @@ from galaxy import exceptions from galaxy.util.none_like import NoneDataset from galaxy.util import odict +from galaxy.util.object_wrapper import wrap_with_safe_string from logging import getLogger log = getLogger( __name__ ) @@ -162,10 +163,13 @@ if name in self.metadata.spec: if rval is None: rval = self.metadata.spec[name].no_value - rval = self.metadata.spec[name].param.to_string( rval ) + rval = self.metadata.spec[ name ].param.to_safe_string( rval ) # Store this value, so we don't need to recalculate if needed # again setattr( self, name, rval ) + else: + #escape string value of non-defined metadata value + rval = wrap_with_safe_string( rval ) return rval def __nonzero__( self ): @@ -190,9 +194,13 @@ ext = tool.inputs[name].extensions[0] except: ext = 'data' - self.dataset = NoneDataset( datatypes_registry=datatypes_registry, ext=ext ) + self.dataset = wrap_with_safe_string( NoneDataset( datatypes_registry=datatypes_registry, ext=ext ), no_wrap_classes=ToolParameterValueWrapper ) else: - self.dataset = dataset + # 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 + self.dataset = wrap_with_safe_string( dataset, no_wrap_classes=ToolParameterValueWrapper ) self.metadata = self.MetadataWrapper( dataset.metadata ) self.datatypes_registry = datatypes_registry self.false_path = getattr( dataset_path, "false_path", None ) @@ -210,7 +218,7 @@ if self.false_path is not None: return self.false_path else: - return self.dataset.file_name + return self.unsanitized.file_name def __getattr__( self, key ): if self.false_path is not None and key == 'file_name': @@ -230,7 +238,7 @@ # object store to find the static location of this # directory. try: - return self.dataset.extra_files_path + return self.unsanitized.extra_files_path except exceptions.ObjectNotFound: # NestedObjectstore raises an error here # instead of just returning a non-existent diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/util/__init__.py --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -360,46 +360,57 @@ '#': '__pd__'} -def restore_text(text): +def restore_text( text, character_map=mapped_chars ): """Restores sanitized text""" if not text: return text - for key, value in mapped_chars.items(): + for key, value in character_map.items(): text = text.replace(value, key) return text -def sanitize_text(text): +def sanitize_text( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): """ Restricts the characters that are allowed in text; accepts both strings - and lists of strings. + and lists of strings; non-string entities will be cast to strings. """ - if isinstance( text, basestring ): - return _sanitize_text_helper(text) - elif isinstance( text, list ): - return [ _sanitize_text_helper(t) for t in text ] + if isinstance( text, list ): + return map( lambda x: sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ), text ) + if not isinstance( text, basestring ): + text = smart_str( text ) + return _sanitize_text_helper( text, valid_characters=valid_characters, character_map=character_map ) - -def _sanitize_text_helper(text): +def _sanitize_text_helper( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): """Restricts the characters that are allowed in a string""" out = [] for c in text: - if c in valid_chars: + if c in valid_characters: out.append(c) - elif c in mapped_chars: - out.append(mapped_chars[c]) + elif c in character_map: + out.append( character_map[c] ) else: - out.append('X') # makes debugging easier + out.append( invalid_character ) # makes debugging easier return ''.join(out) -def sanitize_param(value): +def sanitize_lists_to_string( values, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): + if isinstance( values, list ): + rval = [] + for value in values: + rval.append( sanitize_lists_to_string( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) ) + values = ",".join( rval ) + else: + values = sanitize_text( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) + return values + + +def sanitize_param( value, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): """Clean incoming parameters (strings or lists)""" if isinstance( value, basestring ): - return sanitize_text(value) + return sanitize_text( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) elif isinstance( value, list ): - return map(sanitize_text, value) + return map( lambda x: sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ), value ) else: raise Exception('Unknown parameter type (%s)' % ( type( value ) )) diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/util/dbkeys.py --- a/lib/galaxy/util/dbkeys.py +++ b/lib/galaxy/util/dbkeys.py @@ -4,6 +4,7 @@ #dbkeys read from disk using builds.txt from galaxy.util import read_dbnames from galaxy.util.json import loads +from galaxy.util.object_wrapper import sanitize_lists_to_string import os.path @@ -84,6 +85,7 @@ # use configured server len path if not chrom_info: # Default to built-in build. - chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % dbkey ) + # Since we are using an unverified dbkey, we will sanitize the dbkey before use + chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % sanitize_lists_to_string( dbkey ) ) chrom_info = os.path.abspath( chrom_info ) return ( chrom_info, db_dataset ) diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/util/object_wrapper.py --- /dev/null +++ b/lib/galaxy/util/object_wrapper.py @@ -0,0 +1,436 @@ +""" +Classes for wrapping Objects and Sanitizing string output. +""" + +import inspect +import copy_reg +import logging +import string +from numbers import Number +from types import ( NoneType, NotImplementedType, EllipsisType, FunctionType, MethodType, GeneratorType, CodeType, + BuiltinFunctionType, BuiltinMethodType, ModuleType, XRangeType, SliceType, TracebackType, FrameType, + BufferType, DictProxyType, GetSetDescriptorType, MemberDescriptorType ) +from UserDict import UserDict + +from galaxy.util import sanitize_lists_to_string as _sanitize_lists_to_string + +log = logging.getLogger( __name__ ) + +# Define different behaviors for different types, see also: https://docs.python.org/2/library/types.html + +# Known Callable types +__CALLABLE_TYPES__ = ( FunctionType, MethodType, GeneratorType, CodeType, BuiltinFunctionType, BuiltinMethodType, ) + +# Always wrap these types without attempting to subclass +__WRAP_NO_SUBCLASS__ = ( ModuleType, XRangeType, SliceType, BufferType, TracebackType, FrameType, DictProxyType, + GetSetDescriptorType, MemberDescriptorType ) + __CALLABLE_TYPES__ + +# Don't wrap or sanitize. +__DONT_SANITIZE_TYPES__ = ( Number, bool, NoneType, NotImplementedType, EllipsisType, bytearray, ) + +# Don't wrap, but do sanitize. +__DONT_WRAP_TYPES__ = tuple() #( basestring, ) so that we can get the unsanitized string, we will now wrap basestring instances + +# Wrap contents, but not the container +__WRAP_SEQUENCES__ = ( tuple, list, ) +__WRAP_SETS__ = ( set, frozenset, ) +__WRAP_MAPPINGS__ = ( dict, UserDict, ) + + +# Define the set of characters that are not sanitized, and define a set of mappings for those that are. +# characters that are valid +VALID_CHARACTERS = set( string.letters + string.digits + " -=_.()/+*^,:?!@" ) + +# characters that are allowed but need to be escaped +CHARACTER_MAP = { '>': '__gt__', + '<': '__lt__', + "'": '__sq__', + '"': '__dq__', + '[': '__ob__', + ']': '__cb__', + '{': '__oc__', + '}': '__cc__', + '\n': '__cn__', + '\r': '__cr__', + '\t': '__tc__', + '#': '__pd__'} + +INVALID_CHARACTER = "X" + +def sanitize_lists_to_string( values, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP, invalid_character=INVALID_CHARACTER ): + return _sanitize_lists_to_string( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) + + +def wrap_with_safe_string( value, no_wrap_classes = None ): + """ + Recursively wrap values that should be wrapped. + """ + + def __do_wrap( value ): + if isinstance( value, SafeStringWrapper ): + # Only ever wrap one-layer + return value + if callable( value ): + safe_class = CallableSafeStringWrapper + else: + safe_class = SafeStringWrapper + if isinstance( value, no_wrap_classes ): + return value + if isinstance( value, __DONT_WRAP_TYPES__ ): + return sanitize_lists_to_string( value, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) + if isinstance( value, __WRAP_NO_SUBCLASS__ ): + return safe_class( value, safe_string_wrapper_function = __do_wrap ) + for this_type in __WRAP_SEQUENCES__ + __WRAP_SETS__: + if isinstance( value, this_type ): + return this_type( map( __do_wrap, value ) ) + for this_type in __WRAP_MAPPINGS__: + if isinstance( value, this_type ): + # Wrap both key and value + return this_type( map( lambda x: ( __do_wrap( x[0] ), __do_wrap( x[1] ) ), value.items() ) ) + # Create a dynamic class that joins SafeStringWrapper with the object being wrapped. + # This allows e.g. isinstance to continue to work. + try: + wrapped_class_name = value.__name__ + wrapped_class = value + except: + wrapped_class_name = value.__class__.__name__ + wrapped_class = value.__class__ + value_mod = inspect.getmodule( value ) + if value_mod: + wrapped_class_name = "%s.%s" % ( value_mod.__name__, wrapped_class_name ) + wrapped_class_name = "SafeStringWrapper(%s:%s)" % ( wrapped_class_name, ",".join( sorted( map( str, no_wrap_classes ) ) ) ) + do_wrap_func_name = "__do_wrap_%s" % ( wrapped_class_name ) + do_wrap_func = __do_wrap + global_dict = globals() + if wrapped_class_name in global_dict: + # Check to see if we have created a wrapper for this class yet, if so, reuse + wrapped_class = global_dict.get( wrapped_class_name ) + do_wrap_func = global_dict.get( do_wrap_func_name, __do_wrap ) + else: + try: + wrapped_class = type( wrapped_class_name, ( safe_class, wrapped_class, ), {} ) + except TypeError, e: + # Fail-safe for when a class cannot be dynamically subclassed. + log.warning( "Unable to create dynamic subclass for %s, %s: %s", type( value), value, e ) + wrapped_class = type( wrapped_class_name, ( safe_class, ), {} ) + if wrapped_class not in ( SafeStringWrapper, CallableSafeStringWrapper ): + # Save this wrapper for reuse and pickling/copying + global_dict[ wrapped_class_name ] = wrapped_class + do_wrap_func.__name__ = do_wrap_func_name + global_dict[ do_wrap_func_name ] = do_wrap_func + def pickle_safe_object( safe_object ): + return ( wrapped_class, ( safe_object.unsanitized, do_wrap_func, ) ) + # Set pickle and copy properties + copy_reg.pickle( wrapped_class, pickle_safe_object, do_wrap_func ) + return wrapped_class( value, safe_string_wrapper_function = do_wrap_func ) + # Determine classes not to wrap + if no_wrap_classes: + if not isinstance( no_wrap_classes, ( tuple, list ) ): + no_wrap_classes = [ no_wrap_classes ] + no_wrap_classes = list( no_wrap_classes ) + list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ] + else: + no_wrap_classes = list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ] + no_wrap_classes = tuple( set( sorted( no_wrap_classes, key=str ) ) ) + return __do_wrap( value ) + + +# N.B. refer to e.g. https://docs.python.org/2/reference/datamodel.html for information on Python's Data Model. + + +class SafeStringWrapper( object ): + """ + Class that wraps and sanitizes any provided value's attributes + that will attempt to be cast into a string. + + Attempts to mimic behavior of original class, including operands. + + To ensure proper handling of e.g. subclass checks, the *wrap_with_safe_string()* + method should be used. + + This wrapping occurs in a recursive/parasitic fashion, as all called attributes of + the originally wrapped object will also be wrapped and sanitized, unless the attribute + is of a type found in __DONT_SANITIZE_TYPES__ + __DONT_WRAP_TYPES__, where e.g. ~(strings + will still be sanitized, but not wrapped), and e.g. integers will have neither. + """ + __UNSANITIZED_ATTRIBUTE_NAME__ = 'unsanitized' + __NO_WRAP_NAMES__ = [ '__safe_string_wrapper_function__', __UNSANITIZED_ATTRIBUTE_NAME__] + + + def __new__( cls, *arg, **kwd ): + # We need to define a __new__ since, we are subclassing from e.g. immutable str, which internally sets data + # that will be used when other + this (this + other is handled by __add__) + safe_string_wrapper_function = kwd.get( 'safe_string_wrapper_function', None) or wrap_with_safe_string + try: + return super( SafeStringWrapper, cls ).__new__( cls, sanitize_lists_to_string( arg[0], valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) ) + except Exception, e: + log.warning( "Could not provide an argument to %s.__new__: %s; will try without arguments.", cls, e ) + return super( SafeStringWrapper, cls ).__new__( cls ) + + def __init__( self, value, safe_string_wrapper_function = wrap_with_safe_string ): + self.unsanitized = value + self.__safe_string_wrapper_function__ = safe_string_wrapper_function + + def __str__( self ): + return sanitize_lists_to_string( self.unsanitized, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) + + def __repr__( self ): + return "%s object at %x on: %s" % ( sanitize_lists_to_string( self.__class__.__name__, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ), id( self ), sanitize_lists_to_string( repr( self.unsanitized ), valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) ) + + def __lt__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized < other + + def __le__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized <= other + + def __eq__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized == other + + def __ne__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized != other + + def __gt__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized > other + + def __ge__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized >= other + + def __lt__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.unsanitized < other + + def __cmp__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return cmp( self.unsanitized, other ) + + # Do not implement __rcmp__, python 2.2 < 2.6 + + def __hash__( self ): + return hash( self.unsanitized ) + + def __nonzero__( self ): + return bool( self.unsanitized ) + + # Do not implement __unicode__, we will rely on __str__ + + def __getattr__( self, name ): + if name in SafeStringWrapper.__NO_WRAP_NAMES__: + #FIXME: is this ever reached? + return object.__getattr__( self, name ) + return self.__safe_string_wrapper_function__( getattr( self.unsanitized, name ) ) + + def __setattr__( self, name, value ): + if name in SafeStringWrapper.__NO_WRAP_NAMES__: + return object.__setattr__( self, name, value ) + return setattr( self.unsanitized, name, value ) + + def __delattr__( self, name ): + if name in SafeStringWrapper.__NO_WRAP_NAMES__: + return object.__delattr__( self, name ) + return delattr( self.unsanitized, name ) + + def __getattribute__( self, name ): + if name in SafeStringWrapper.__NO_WRAP_NAMES__: + return object.__getattribute__( self, name ) + return self.__safe_string_wrapper_function__( getattr( object.__getattribute__( self, 'unsanitized' ), name ) ) + + # Skip Descriptors + + # Skip __slots__ + + # Don't need __metaclass__, we'll use the helper function to handle with subclassing for e.g. isinstance() + + # Revisit: + # __instancecheck__ + # __subclasscheck__ + # We are using a helper class to create dynamic subclasses to handle class checks + + # We address __call__ as needed based upon unsanitized, through the use of a CallableSafeStringWrapper class + + def __len__( self ): + original_value = self.unsanitized + while isinstance( original_value, SafeStringWrapper ): + original_value = self.unsanitized + return len( self.unsanitized ) + + def __getitem__( self, key ): + return self.__safe_string_wrapper_function__( self.unsanitized[ key ] ) + + def __setitem__( self, key, value ): + while isinstance( value, SafeStringWrapper ): + value = value.unsanitized + self.unsanitized[ key ] = value + + def __delitem__( self, key ): + del self.unsanitized[ key ] + + def __iter__( self ): + return iter( map( self.__safe_string_wrapper_function__, iter( self.unsanitized ) ) ) + + # Do not implement __reversed__ + + def __contains__( self, item ): + # FIXME: Do we need to consider if item is/isn't or does/doesn't contain SafeStringWrapper? + # When considering e.g. nested lists/dicts/etc, this gets complicated + while isinstance( item, SafeStringWrapper ): + item = item.unsanitized + return item in self.unsanitized + + # Not sure that we need these slice methods, but will provide anyway + def __getslice__( self, i, j ): + return self.__safe_string_wrapper_function__( self.unsanitized[ i:j ] ) + + def __setslice__( self, i, j, value ): + self.unsanitized[ i:j ] = value + + def __delslice__( self, i, j ): + del self.unsanitized[ i:j ] + + def __add__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized + other ) + + def __sub__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized - other ) + + def __mul__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized * other ) + + def __floordiv__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized // other ) + + def __mod__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized % other ) + + def __divmod__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( divmod( self.unsanitized, other ) ) + + def __pow__( self, *other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( pow( self.unsanitized, *other ) ) + + def __lshift__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized << other ) + + def __rshift__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized >> other ) + + def __and__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized & other ) + + def __xor__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized ^ other ) + + def __or__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized | other ) + + def __div__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized / other ) + + def __truediv__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( self.unsanitized / other ) + + # The only reflected operand that we will define is __rpow__, due to coercion rules complications as per docs + def __rpow__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return self.__safe_string_wrapper_function__( pow( other, self.unsanitized ) ) + + # Do not implement in-place operands + + def __neg__( self ): + return __safe_string_wrapper_function__( -self.unsanitized ) + + def __pos__( self ): + return __safe_string_wrapper_function__( +self.unsanitized ) + + def __abs__( self ): + return __safe_string_wrapper_function__( abs( self.unsanitized ) ) + + def __invert__( self ): + return __safe_string_wrapper_function__( ~self.unsanitized ) + + def __complex__( self ): + return __safe_string_wrapper_function__( complex( self.unsanitized ) ) + + def __int__( self ): + return int( self.unsanitized ) + + def __float__( self ): + return float( self.unsanitized ) + + def __oct__( self ): + return oct( self.unsanitized ) + + def __hex__( self ): + return hex( self.unsanitized ) + + def __index__( self ): + return self.unsanitized.index() + + def __coerce__( self, other ): + while isinstance( other, SafeStringWrapper ): + other = other.unsanitized + return coerce( self.unsanitized, other ) + + def __enter__( self ): + return self.unsanitized.__enter__() + + def __exit__( self, *args ): + return self.unsanitized.__exit__( *args ) + +class CallableSafeStringWrapper( SafeStringWrapper ): + + def __call__( self, *args, **kwds ): + return self.__safe_string_wrapper_function__( self.unsanitized( *args, **kwds ) ) + + +# Enable pickling/deepcopy +def pickle_SafeStringWrapper( safe_object ): + args = ( safe_object.unsanitized, ) + cls = SafeStringWrapper + if isinstance( safe_object, CallableSafeStringWrapper ): + cls = CallableSafeStringWrapper + return ( cls, args ) +copy_reg.pickle( SafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string ) +copy_reg.pickle( CallableSafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string ) + diff -r a74b0112995cd2ae396d0916a6e845b7709f9002 -r 659fc6822f0afcdf77ba5f8857ba58700ce06b16 lib/galaxy/webapps/galaxy/controllers/tool_runner.py --- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py +++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py @@ -80,14 +80,28 @@ message=message, status=status, redirect=redirect ) ) - params = galaxy.util.Params( kwd, sanitize = False ) #Sanitize parameters when substituting into command line via input wrappers - #do param translation here, used by datasource tools - if tool.input_translator: - tool.input_translator.translate( params ) + + def _validated_params_for( kwd ): + params = galaxy.util.Params( kwd, sanitize=False ) # Sanitize parameters when substituting into command line via input wrappers + #do param translation here, used by datasource tools + if tool.input_translator: + tool.input_translator.translate( params ) + return params + + params = _validated_params_for( kwd ) # We may be visiting Galaxy for the first time ( e.g., sending data from UCSC ), # so make sure to create a new history if we've never had one before. history = tool.get_default_history_by_trans( trans, create=True ) - template, vars = tool.handle_input( trans, params.__dict__ ) + try: + template, vars = tool.handle_input( trans, params.__dict__ ) + except KeyError: + # This error indicates (or at least can indicate) there was a + # problem with the stored tool_state - it is incompatible with + # this variant of the tool - possibly because the tool changed + # or because the tool version changed. + del kwd[ "tool_state" ] + params = _validated_params_for( kwd ) + template, vars = tool.handle_input( trans, params.__dict__ ) if len( params ) > 0: trans.log_event( "Tool params: %s" % ( str( params ) ), tool_id=tool_id ) add_frame = AddFrameData() Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.