Changeset 9745
- Timestamp:
- 06/03/08 01:15:08 (6 months ago)
- Location:
- branches/plugin-system
- Files:
-
- 5 modified
-
epydoc.conf (modified) (1 diff)
-
src/plugins/pluginmanager.py (modified) (5 diffs)
-
src/plugins/plugin.py (modified) (1 diff)
-
src/roster_window.py (modified) (2 diffs)
-
test/test_pluginmanager.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/plugin-system/epydoc.conf
r9737 r9745 9 9 redundant-details: yes 10 10 docformat: restructuredtext 11 # top: gajim 11 top: plugins 12 12 13 13 # The list of modules to document. Modules can be named using 14 14 # dotted names, module filenames, or package directory names. 15 15 # This option may be repeated. 16 modules: src/plugins/*.py 16 modules: src/plugins/*.py test/*.py 17 17 18 18 # Write html output to the directory "apidocs" -
branches/plugin-system/src/plugins/pluginmanager.py
r9737 r9745 17 17 18 18 ''' 19 Helper code related to plug-ins management system.19 Plug-in management related classes. 20 20 21 21 :author: Mateusz Biliński <mateusz@bilinski.it> … … 33 33 import common.gajim as gajim 34 34 35 from helpers import log, log_calls, Singleton36 from plugin import GajimPlugin35 from plugins.helpers import log, log_calls, Singleton 36 from plugins.plugin import GajimPlugin 37 37 38 38 class PluginManager(object): 39 ''' 40 Main plug-in management class. 41 42 Currently: 43 - scans for plugins 44 - activates them 45 - handles GUI extension points, when called by GUI objects after plugin 46 is activated (by dispatching info about call to handlers in plugins) 47 48 :todo: add more info about how GUI extension points work 49 :todo: add list of available GUI extension points 50 :todo: implement mechanism to dynamically load plugins where GUI extension 51 points have been already called (i.e. when plugin is activated 52 after GUI object creation) 53 :todo: implement mechanism to dynamically deactive plugins (call plugin's 54 deactivation handler) 55 56 ''' 57 39 58 __metaclass__ = Singleton 40 59 … … 42 61 def __init__(self): 43 62 self.plugins = [] 44 self.active = [] 63 ''' 64 Detected plugin classes. 65 66 Each class object in list is `GajimPlugin` subclass. 67 68 :type: [] of class objects 69 ''' 70 self.active_plugins = [] 71 ''' 72 Objectsof active plugins. 73 74 These are object instances of classes held `plugins`, but only those 75 that were activated. 76 77 :type: [] of `GajimPlugin` based objects 78 ''' 45 79 self.gui_extension_points = {} 80 ''' 81 Registered GUI extension points. 82 ''' 46 83 47 84 for path in gajim.PLUGINS_DIRS: 48 self.plugins.extend( self._scan_dir_for_plugins(path))85 self.plugins.extend(PluginManager.scan_dir_for_plugins(path)) 49 86 50 87 log.debug('plugins: %s'%(self.plugins)) 51 88 52 89 self._activate_all_plugins() 53 54 log.debug('active: %s'%(self.active ))55 90 91 log.debug('active: %s'%(self.active_plugins)) 92 56 93 @log_calls('PluginManager') 57 94 def gui_extension_point(self, gui_extpoint_name, *args): 95 ''' 96 Invokes all handlers (from plugins) for particular GUI extension point. 97 98 :param gui_extpoint_name: name of GUI extension point. 99 :type gui_extpoint_name: unicode 100 :param args: parameters to be passed to extension point handlers 101 (typically and object that invokes `gui_extension_point`; however, 102 this can be practically anything) 103 :type args: tuple 104 105 :todo: GUI extension points must be documented well - names with 106 parameters that will be passed to handlers (in plugins). Such 107 documentation must be obeyed both in core and in plugins. This 108 is a loosely coupled approach and is pretty natural in Python. 109 110 :bug: what if only some handlers are successfully connected? we should 111 revert all those connections that where successfully made. Maybe 112 call 'self._deactivate_plugin()' or sth similar. 113 Looking closer - we only rewrite tuples here. Real check should be 114 made in method that invokes gui_extpoints handlers. 115 116 ''' 117 118 log.debug(type(args)) 119 58 120 if gui_extpoint_name in self.gui_extension_points: 59 121 for handlers in self.gui_extension_points[gui_extpoint_name]: … … 63 125 def _activate_plugin(self, plugin): 64 126 ''' 65 :param plugin: Plugin to be activated. 66 :type plugin: class object of GajimPlugin subclass 67 ''' 68 p = plugin() 69 127 :param plugin: plugin to be activated 128 :type plugin: class object of `GajimPlugin` subclass 129 ''' 130 131 plugin_object = plugin() 132 70 133 success = True 71 72 # :fix: what if only some handlers are successfully connected? we should 73 # revert all those connections that where successfully made. Maybe 74 # call 'self._deactivate_plugin()' or sth similar. 75 # Looking closer - we only rewrite tuples here. Real check should be 76 # made in method that invokes gui_extpoints handlers. 134 77 135 for gui_extpoint_name, gui_extpoint_handlers in \ 78 p .gui_extension_points.iteritems():79 self.gui_extension_points.setdefault(gui_extpoint_name, []).append(136 plugin_object.gui_extension_points.iteritems(): 137 self.gui_extension_points.setdefault(gui_extpoint_name, []).append( 80 138 gui_extpoint_handlers) 81 139 82 140 if success: 83 self.active .append(p)84 141 self.active_plugins.append(plugin_object) 142 85 143 return success 86 144 87 145 @log_calls('PluginManager') 88 146 def _activate_all_plugins(self): 89 self.active = [] 147 ''' 148 Activates all plugins in `plugins`. 149 150 Activated plugins are appended to `active_plugins` list. 151 ''' 152 self.active_plugins = [] 90 153 for plugin in self.plugins: 91 154 self._activate_plugin(plugin) 92 155 93 @log_calls('PluginManager') 94 def _scan_dir_for_plugins(self, path): 156 @staticmethod 157 @log_calls('PluginManager') 158 def scan_dir_for_plugins(path): 159 ''' 160 Scans given directory for plugin classes. 161 162 :param path: directory to scan for plugins 163 :type path: unicode 164 165 :return: list of found plugin classes (subclasses of `GajimPlugin` 166 :rtype: [] of class objects 167 168 :note: currently it only searches for plugin classes in '\*.py' files 169 present in given direcotory `path` (no recursion here) 170 171 :todo: add scanning packages 172 :todo: add scanning zipped modules 173 ''' 95 174 plugins_found = [] 96 175 if os.path.isdir(path): … … 101 180 log.debug(sys.path) 102 181 103 for file in fnmatch.filter(dir_list, '*.py'):104 log.debug('- "%s"'%(file ))105 file_path = os.path.join(path, file )182 for file_name in fnmatch.filter(dir_list, '*.py'): 183 log.debug('- "%s"'%(file_name)) 184 file_path = os.path.join(path, file_name) 106 185 log.debug(' "%s"'%(file_path)) 107 186 if os.path.isfile(file_path): 108 module_name = os.path.splitext(file )[0]187 module_name = os.path.splitext(file_name)[0] 109 188 module = __import__(module_name) 110 filter_out_bad_names = \ 111 lambda x: not (x.startswith('__') or 112 x.endswith('__')) 113 for module_attr_name in filter(filter_out_bad_names, 114 dir(module)): 189 for module_attr_name in [f_name for f_name in dir(module) 190 if not (f_name.startswith('__') or 191 f_name.endswith('__'))]: 115 192 module_attr = getattr(module, module_attr_name) 116 193 log.debug('%s : %s'%(module_attr_name, module_attr)) 117 194 118 195 try: 119 196 if issubclass(module_attr, GajimPlugin) and \ 120 not module_attr is GajimPlugin:197 not module_attr is GajimPlugin: 121 198 log.debug('is subclass of GajimPlugin') 122 199 plugins_found.append(module_attr) 123 except TypeError, e:200 except TypeError, type_error: 124 201 log.debug('module_attr: %s, error : %s'%( 125 module_name+'.'+module_attr_name,126 e))202 module_name+'.'+module_attr_name, 203 type_error)) 127 204 128 205 log.debug(module) -
branches/plugin-system/src/plugins/plugin.py
r9737 r9745 25 25 ''' 26 26 27 import sys 28 from plugins.helpers import log, log_calls 27 from plugins.helpers import log_calls 29 28 30 29 class GajimPlugin(object): 30 ''' 31 Base class for implementing Gajim plugins. 32 ''' 31 33 name = '' 34 ''' 35 Name of plugin. 36 37 Will be shown in plugins management GUI. 38 39 :type: unicode 40 ''' 32 41 short_name = '' 42 ''' 43 Short name of plugin. 44 45 Used for quick indentification of plugin. 46 47 :type: unicode 48 49 :todo: decide whether we really need this one, because class name (with 50 module name) can act as such short name 51 ''' 33 52 version = '' 53 ''' 54 Version of plugin. 55 56 :type: unicode 57 58 :todo: decide how to compare version between each other (which one 59 is higher). Also rethink: do we really need to compare versions 60 of plugins between each other? This would be only useful if we detect 61 same plugin class but with different version and we want only the newest 62 one to be active - is such policy good? 63 ''' 34 64 description = '' 65 ''' 66 Plugin description. 67 68 :type: unicode 69 70 :todo: should be allow rich text here (like HTML or reStructuredText)? 71 ''' 35 72 authors = [] 73 ''' 74 Plugin authors. 75 76 :type: [] of unicode 77 78 :todo: should we decide on any particular format of author strings? 79 Especially: should we force format of giving author's e-mail? 80 ''' 36 81 gui_extension_points = {} 82 ''' 83 Extension points that plugin wants to connect with. 84 ''' 37 85 38 86 @log_calls('GajimPlugin') -
branches/plugin-system/src/roster_window.py
r9699 r9745 311 311 c4 = time.clock() 312 312 313 print ""314 print "--- Add account contacts of %s ---------" % account315 print "Total Time", c4-c1316 print "Add contact without draw", c6-c5317 print "Draw groups and account", c10-c9318 print "--- contacts added -----------------------------"319 print ""313 #print "" 314 #print "--- Add account contacts of %s ---------" % account 315 #print "Total Time", c4-c1 316 #print "Add contact without draw", c6-c5 317 #print "Draw groups and account", c10-c9 318 #print "--- contacts added -----------------------------" 319 #print "" 320 320 321 321 … … 1217 1217 self.draw_avatar(jid, account) 1218 1218 yield True 1219 print "--- Idle draw of %s -----------" % account1220 print "Draw contact and avatar", time.clock() - t1221 print "-------------------------------"1219 #print "--- Idle draw of %s -----------" % account 1220 #print "Draw contact and avatar", time.clock() - t 1221 #print "-------------------------------" 1222 1222 yield False 1223 1223 -
branches/plugin-system/test/test_pluginmanager.py
r9737 r9745 33 33 sys.path.append(gajim_root + '/src') 34 34 35 # a temporary version of ~/.gajim for testing 36 configdir = gajim_root + '/test/tmp' 37 38 import time 39 40 # define _ for i18n 41 import __builtin__ 42 __builtin__._ = lambda x: x 43 44 # wipe config directory 45 import os 46 if os.path.isdir(configdir): 47 import shutil 48 shutil.rmtree(configdir) 49 50 os.mkdir(configdir) 51 52 import common.configpaths 53 common.configpaths.gajimpaths.init(configdir) 54 common.configpaths.gajimpaths.init_profile() 55 56 # for some reason common.gajim needs to be imported before xmpppy? 57 from common import gajim 58 from common import xmpp 59 60 gajim.DATA_DIR = gajim_root + '/data' 61 62 from common.stanza_session import StanzaSession 63 64 # name to use for the test account 65 account_name = 'test' 66 35 67 from plugins import PluginManager 36 68 37 69 class PluginManagerTestCase(unittest.TestCase): 38 def setUp(self):39 self.pluginmanager = PluginManager()40 41 def tearDown(self):42 pass43 44 def test_01_Singleton(self):45 """ 1. Checking whether PluginManger class is singleton. """46 self.pluginmanager.test_arg = 147 secondPluginManager = PluginManager()48 49 self.failUnlessEqual(id(secondPluginManager), id(self.pluginmanager),50 'Different IDs in references to PluginManager objects (not a singleton)')51 self.failUnlessEqual(secondPluginManager.test_arg, 1,52 'References point to different PluginManager objects (not a singleton')53 70 def setUp(self): 71 self.pluginmanager = PluginManager() 72 73 def tearDown(self): 74 pass 75 76 def test_01_Singleton(self): 77 """ 1. Checking whether PluginManger class is singleton. """ 78 self.pluginmanager.test_arg = 1 79 secondPluginManager = PluginManager() 80 81 self.failUnlessEqual(id(secondPluginManager), id(self.pluginmanager), 82 'Different IDs in references to PluginManager objects (not a singleton)') 83 self.failUnlessEqual(secondPluginManager.test_arg, 1, 84 'References point to different PluginManager objects (not a singleton') 85 54 86 def suite(): 55 suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase)56 return suite87 suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase) 88 return suite 57 89 58 90 if __name__=='__main__': 59 runner = unittest.TextTestRunner()60 test_suite = suite()61 runner.run(test_suite)91 runner = unittest.TextTestRunner() 92 test_suite = suite() 93 runner.run(test_suite)
