Package yapsy :: Module ConfigurablePluginManager
[hide private]

Source Code for Module yapsy.ConfigurablePluginManager

  1  #!/usr/bin/python 
  2  # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- 
  3   
  4  """ 
  5  Defines plugin managers that can handle configuration files similar to 
  6  the ini files manipulated by Python's ConfigParser module. 
  7  """ 
  8   
  9  import sys, os 
 10  import logging 
 11  import ConfigParser 
 12   
 13  from IPlugin import IPlugin 
 14   
 15   
 16  from PluginManager import PluginManager,PluginManagerDecorator 
 17  from PluginManager import PLUGIN_NAME_FORBIDEN_STRING 
 18   
 19   
 20   
21 -class ConfigurablePluginManager(PluginManagerDecorator):
22 """ 23 A plugin manager that also manages a configuration file. 24 25 The configuration file will be accessed through a ``ConfigParser`` 26 derivated object. The file can be used for other purpose by the 27 application using this plugin manager as it will only add a new 28 specific section ``[Plugin Management]`` for itself and also new 29 sections for some plugins that will start with ``[Plugin:...]`` 30 (only the plugins that explicitly requires to save configuration 31 options will have this kind of section). 32 33 .. warning:: when giving/building the list of plugins to activate by 34 default, there must not be any space in the list (neither in the 35 names nor in between) 36 """ 37 38 CONFIG_SECTION_NAME = "Plugin Management" 39 40
41 - def __init__(self, 42 configparser_instance=None, 43 config_change_trigger= lambda x:True, 44 decorated_manager=None, 45 # The following args will only be used if we need to 46 # create a default PluginManager 47 categories_filter={"Default":IPlugin}, 48 directories_list=None, 49 plugin_info_ext="yapsy-plugin"):
50 """ 51 Create the plugin manager and record the ConfigParser instance 52 that will be used afterwards. 53 54 The ``config_change_trigger`` argument can be used to set a 55 specific method to call when the configuration is 56 altered. This will let the client application manage the way 57 they want the configuration to be updated (e.g. write on file 58 at each change or at precise time intervalls or whatever....) 59 """ 60 # Create the base decorator class 61 PluginManagerDecorator.__init__(self,decorated_manager, 62 categories_filter, 63 directories_list, 64 plugin_info_ext) 65 self.setConfigParser(configparser_instance, config_change_trigger)
66 67
68 - def setConfigParser(self,configparser_instance,config_change_trigger):
69 """ 70 Set the ConfigParser instance. 71 """ 72 self.config_parser = configparser_instance 73 # set the (optional) fucntion to be called when the 74 # configuration is changed: 75 self.config_has_changed = config_change_trigger
76
77 - def __getCategoryPluginsListFromConfig(self, plugin_list_str):
78 """ 79 Parse the string describing the list of plugins to activate, 80 to discover their actual names and return them. 81 """ 82 return plugin_list_str.strip(" ").split("%s"%PLUGIN_NAME_FORBIDEN_STRING)
83
84 - def __getCategoryPluginsConfigFromList(self, plugin_list):
85 """ 86 Compose a string describing the list of plugins to activate 87 """ 88 return PLUGIN_NAME_FORBIDEN_STRING.join(plugin_list)
89
90 - def __getCategoryOptionsName(self,category_name):
91 """ 92 Return the appropirately formated version of the category's 93 option. 94 """ 95 return "%s_plugins_to_load" % category_name.replace(" ","_")
96
97 - def __addPluginToConfig(self,category_name, plugin_name):
98 """ 99 Utility function to add a plugin to the list of plugin to be 100 activated. 101 """ 102 # check that the section is here 103 if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): 104 self.config_parser.add_section(self.CONFIG_SECTION_NAME) 105 # check that the category's list of activated plugins is here too 106 option_name = self.__getCategoryOptionsName(category_name) 107 if not self.config_parser.has_option(self.CONFIG_SECTION_NAME, option_name): 108 # if there is no list yet add a new one 109 self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,plugin_name) 110 return self.config_has_changed() 111 else: 112 # get the already existing list and append the new 113 # activated plugin to it. 114 past_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME,option_name) 115 past_list = self.__getCategoryPluginsListFromConfig(past_list_str) 116 # make sure we don't add it twice 117 if plugin_name not in past_list: 118 past_list.append(plugin_name) 119 new_list_str = self.__getCategoryPluginsConfigFromList(past_list) 120 self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,new_list_str) 121 return self.config_has_changed()
122
123 - def __removePluginFromConfig(self,category_name, plugin_name):
124 """ 125 Utility function to add a plugin to the list of plugin to be 126 activated. 127 """ 128 # check that the section is here 129 if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): 130 # then nothing to remove :) 131 return 132 # check that the category's list of activated plugins is here too 133 option_name = self.__getCategoryOptionsName(category_name) 134 if not self.config_parser.has_option(self.CONFIG_SECTION_NAME, option_name): 135 # if there is no list still nothing to do 136 return 137 else: 138 # get the already existing list 139 past_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME,option_name) 140 past_list = self.__getCategoryPluginsListFromConfig(past_list_str) 141 if plugin_name in past_list: 142 past_list.remove(plugin_name) 143 new_list_str = self.__getCategoryPluginsConfigFromList(past_list) 144 self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,new_list_str) 145 self.config_has_changed()
146 147 148
149 - def registerOptionFromPlugin(self, 150 category_name, plugin_name, 151 option_name, option_value):
152 """ 153 To be called from a plugin object, register a given option in 154 the name of a given plugin. 155 """ 156 section_name = "%s Plugin: %s" % (category_name,plugin_name) 157 # if the plugin's section is not here yet, create it 158 if not self.config_parser.has_section(section_name): 159 self.config_parser.add_section(section_name) 160 # set the required option 161 self.config_parser.set(section_name,option_name,option_value) 162 self.config_has_changed()
163
164 - def hasOptionFromPlugin(self, 165 category_name, plugin_name, option_name):
166 """ 167 To be called from a plugin object, return True if the option 168 has already been registered. 169 """ 170 section_name = "%s Plugin: %s" % (category_name,plugin_name) 171 return self.config_parser.has_section(section_name) and self.config_parser.has_option(section_name,option_name)
172
173 - def readOptionFromPlugin(self, 174 category_name, plugin_name, option_name):
175 """ 176 To be called from a plugin object, read a given option in 177 the name of a given plugin. 178 """ 179 section_name = "%s Plugin: %s" % (category_name,plugin_name) 180 return self.config_parser.get(section_name,option_name)
181 182
183 - def __decoratePluginObject(self, category_name, plugin_name, plugin_object):
184 """ 185 Add two methods to the plugin objects that will make it 186 possible for it to benefit from this class's api concerning 187 the management of the options. 188 """ 189 plugin_object.setConfigOption = lambda x,y: self.registerOptionFromPlugin(category_name, 190 plugin_name, 191 x,y) 192 plugin_object.setConfigOption.__doc__ = self.registerOptionFromPlugin.__doc__ 193 plugin_object.getConfigOption = lambda x: self.readOptionFromPlugin(category_name, 194 plugin_name, 195 x) 196 plugin_object.getConfigOption.__doc__ = self.readOptionFromPlugin.__doc__ 197 plugin_object.hasConfigOption = lambda x: self.hasOptionFromPlugin(category_name, 198 plugin_name, 199 x) 200 plugin_object.hasConfigOption.__doc__ = self.hasOptionFromPlugin.__doc__
201
202 - def activatePluginByName(self, plugin_name, category_name="Default", save_state=True):
203 """ 204 Activate a plugin, , and remember it (in the config file). 205 206 If you want the plugin to benefit from the configuration 207 utility defined by this manager, it is crucial to use this 208 method to activate a plugin and not call the plugin object's 209 ``activate`` method. In fact, this method will also "decorate" 210 the plugin object so that it can use this class's methods to 211 register its own options. 212 213 By default, the plugin's activation is registered in the 214 config file but if you d'ont want this set the 'save_state' 215 argument to False. 216 """ 217 # first decorate the plugin 218 pta = self._component.getPluginByName(plugin_name,category_name) 219 if pta is None: 220 return None 221 self.__decoratePluginObject(category_name,plugin_name,pta.plugin_object) 222 # activate the plugin 223 plugin_object = self._component.activatePluginByName(plugin_name,category_name) 224 # check the activation and then optionally set the config option 225 if plugin_object.is_activated: 226 if save_state: 227 self.__addPluginToConfig(category_name,plugin_name) 228 return plugin_object 229 return None
230
231 - def deactivatePluginByName(self, plugin_name, category_name="Default", save_state=True):
232 """ 233 Deactivate a plugin, and remember it (in the config file). 234 235 By default, the plugin's deactivation is registered in the 236 config file but if you d'ont want this set the ``save_state`` 237 argument to False. 238 """ 239 # activate the plugin 240 plugin_object = self._component.deactivatePluginByName(plugin_name,category_name) 241 if plugin_object is None: 242 return None 243 # check the deactivation and then optionnally set the config option 244 if not plugin_object.is_activated: 245 if save_state: 246 self.__removePluginFromConfig(category_name,plugin_name) 247 return plugin_object 248 return None
249
250 - def loadPlugins(self,callback=None):
251 """ 252 Walk through the plugins' places and look for plugins. Then 253 for each plugin candidate look for its category, load it and 254 stores it in the appropriate slot of the ``category_mapping``. 255 """ 256 self._component.loadPlugins() 257 # now load the plugins according to the recorded configuration 258 if self.config_parser.has_section(self.CONFIG_SECTION_NAME): 259 # browse all the categories 260 for category_name in self._component.category_mapping.keys(): 261 # get the list of plugins to be activated for this 262 # category 263 option_name = "%s_plugins_to_load"%category_name 264 if self.config_parser.has_option(self.CONFIG_SECTION_NAME, 265 option_name): 266 plugin_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME, 267 option_name) 268 plugin_list = self.__getCategoryPluginsListFromConfig(plugin_list_str) 269 # activate all the plugins that should be 270 # activated 271 for plugin_name in plugin_list: 272 self.activatePluginByName(plugin_name,category_name)
273