diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index f93c2217419..e9fcbf2c4dd 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -83,6 +83,7 @@ class Configuration( object ): # The value of migrated_tools_config is the file reserved for containing only those tools that have been eliminated from the distribution # and moved to the tool shed. self.integrated_tool_panel_config = resolve_path( kwargs.get( 'integrated_tool_panel_config', 'integrated_tool_panel.xml' ), self.root ) + self.toolbox_filter_base_modules = listify( kwargs.get( "toolbox_filter_base_modules", "galaxy.tools.filters" ) ) self.tool_filters = listify( kwargs.get( "tool_filters", [] ), do_strip=True ) self.tool_label_filters = listify( kwargs.get( "tool_label_filters", [] ), do_strip=True ) self.tool_section_filters = listify( kwargs.get( "tool_section_filters", [] ), do_strip=True ) diff --git a/lib/galaxy/tools/filters/__init__.py b/lib/galaxy/tools/filters/__init__.py index 04d72ff1e38..c7402d176f0 100644 --- a/lib/galaxy/tools/filters/__init__.py +++ b/lib/galaxy/tools/filters/__init__.py @@ -1,6 +1,8 @@ -import logging -from galaxy.util import listify from copy import deepcopy +import sys +import logging + +from galaxy.util import listify log = logging.getLogger( __name__ ) @@ -20,6 +22,7 @@ class FilterFactory( object ): self.default_filters = dict( tool=[ _not_hidden, _handle_requires_login ], section=[], label=[] ) # Add dynamic filters to these default filters. config = toolbox.app.config + self.__base_modules = listify( getattr( config, "toolbox_filter_base_modules", "galaxy.tools.filters" ) ) self.__init_filters( "tool", getattr( config, "tool_filters", "" ), self.default_filters ) self.__init_filters( "section", getattr( config, "tool_section_filters", "" ), self.default_filters ) self.__init_filters( "label", getattr( config, "tool_label_filters", "" ), self.default_filters ) @@ -65,14 +68,27 @@ class FilterFactory( object ): if ":" in filter_name: # Should be a submodule of filters (e.g. examples:restrict_development_tools) (module_name, function_name) = filter_name.rsplit(":", 1) - module = __import__( module_name.strip(), globals() ) - function = getattr( module, function_name.strip() ) + function = self._import_filter( module_name, function_name ) else: # No module found, just load a function from this file or # one that has be explicitly imported. function = getattr( globals(), filter_name.strip() ) return function + def _import_filter( self, module_name, function_name ): + function_name = function_name.strip() + for base_module in self.__base_modules: + full_module_name = "%s.%s" % ( base_module, module_name.strip() ) + try: + __import__( full_module_name ) + except ImportError: + # log.debug("Failed to load module.", exc_info=True) + continue + module = sys.modules[ full_module_name ] + if hasattr( module, function_name ): + return getattr( module, function_name ) + raise Exception("Failed to find filter %s.%s" % (module_name, function_name)) + ## Stock Filter Functions def _not_hidden( context, tool ): diff --git a/test/unit/tools/filter_modules/__init__.py b/test/unit/tools/filter_modules/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/unit/tools/filter_modules/filtermod.py b/test/unit/tools/filter_modules/filtermod.py new file mode 100644 index 00000000000..64adfd451b0 --- /dev/null +++ b/test/unit/tools/filter_modules/filtermod.py @@ -0,0 +1,22 @@ +""" Test filters used by test_toolbox_filters.py. +""" + + +def filter_tool( context, tool ): + """Test Filter Tool""" + return False + + +def filter_section( context, section ): + """Test Filter Section""" + return False + + +def filter_label_1( context, label ): + """Test Filter Label 1""" + return False + + +def filter_label_2( context, label ): + """Test Filter Label 2""" + return False diff --git a/test/unit/tools/test_toolbox_filters.py b/test/unit/tools/test_toolbox_filters.py new file mode 100644 index 00000000000..482b59d91f0 --- /dev/null +++ b/test/unit/tools/test_toolbox_filters.py @@ -0,0 +1,79 @@ +from galaxy.util.bunch import Bunch + +from galaxy.tools.filters import FilterFactory + + +def test_stock_filtering_requires_login_tools( ): + anonymous_user_trans = mock_trans( has_user=False ) + filters = filter_factory( {} ).build_filters( mock_trans() )[ 'tool' ] + assert not is_filtered( filters, anonymous_user_trans, mock_tool( require_login=False ) ) + assert is_filtered( filters, anonymous_user_trans, mock_tool( require_login=True ) ) + + logged_in_trans = mock_trans( has_user=True ) + filters = filter_factory( {} ).build_filters( logged_in_trans )[ 'tool' ] + assert not is_filtered( filters, logged_in_trans, mock_tool( require_login=True ) ) + + +def test_stock_filtering_hidden_tools( ): + filters = filter_factory( {} ).build_filters( mock_trans() )[ 'tool' ] + assert not is_filtered( filters, mock_trans(), mock_tool( hidden=False ) ) + assert is_filtered( filters, mock_trans(), mock_tool( hidden=True ) ) + + +def test_trackster_filtering( ): + filters = filter_factory( {} ).build_filters( mock_trans(), trackster=True )[ 'tool' ] + # Ekkk... is trackster_conf broken? Why is it showing up. + # assert is_filtered( filters, mock_trans(), mock_tool( trackster_conf=False ) ) + assert not is_filtered( filters, mock_trans(), mock_tool( trackster_conf=True ) ) + + +def test_custom_filters(): + filters = filter_factory().build_filters( mock_trans() ) + tool_filters = filters[ "tool" ] + # TODO: the fact that -1 is the custom filter is an implementation + # detail that should not be tested here. + assert tool_filters[ -1 ].__doc__ == "Test Filter Tool" + + section_filters = filters[ "section" ] + assert section_filters[ 0 ].__doc__ == "Test Filter Section" + + label_filters = filters[ "label" ] + assert label_filters[ 0 ].__doc__ == "Test Filter Label 1" + assert label_filters[ 1 ].__doc__ == "Test Filter Label 2" + + +def filter_factory(config_dict=None): + if config_dict is None: + config_dict = dict( + tool_filters=["filtermod:filter_tool"], + tool_section_filters=["filtermod:filter_section"], + tool_label_filters=["filtermod:filter_label_1", "filtermod:filter_label_2"], + ) + config = Bunch( **config_dict ) + config.toolbox_filter_base_modules = "galaxy.tools.filters,tools.filter_modules" + app = Bunch(config=config) + toolbox = Bunch(app=app) + return FilterFactory(toolbox) + + +def is_filtered( filters, trans, tool ): + context = Bunch( trans=trans ) + return not all( map( lambda filter: filter( context, tool ), filters ) ) + + +def mock_tool( require_login=False, hidden=False, trackster_conf=False ): + tool = Bunch( + require_login=require_login, + hidden=hidden, + trackster_conf=trackster_conf, + ) + return tool + + +def mock_trans( has_user=True ): + trans = Bunch( ) + if has_user: + trans.user = Bunch(preferences={}) + else: + trans.user = None + return trans