Changeset 9745

Show
Ignore:
Timestamp:
06/03/08 01:15:08 (6 months ago)
Author:
vardo
Message:

Added docstrings in reST format (also with todos). Commented out 'print' statements related to roster window. A few modifications to make code prettier (PyLint? driven).

Location:
branches/plugin-system
Files:
5 modified

Legend:

Unmodified
Added
Removed
  • branches/plugin-system/epydoc.conf

    r9737 r9745  
    99redundant-details: yes 
    1010docformat: restructuredtext 
    11 # top: gajim 
     11top: plugins 
    1212 
    1313# The list of modules to document.  Modules can be named using 
    1414# dotted names, module filenames, or package directory names. 
    1515# This option may be repeated. 
    16 modules: src/plugins/*.py 
     16modules: src/plugins/*.py test/*.py 
    1717 
    1818# Write html output to the directory "apidocs" 
  • branches/plugin-system/src/plugins/pluginmanager.py

    r9737 r9745  
    1717 
    1818''' 
    19 Helper code related to plug-ins management system. 
     19Plug-in management related classes. 
    2020 
    2121:author: Mateusz Biliński <mateusz@bilinski.it> 
     
    3333import common.gajim as gajim 
    3434 
    35 from helpers import log, log_calls, Singleton 
    36 from plugin import GajimPlugin 
     35from plugins.helpers import log, log_calls, Singleton 
     36from plugins.plugin import GajimPlugin 
    3737 
    3838class 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         
    3958        __metaclass__ = Singleton 
    4059 
     
    4261        def __init__(self): 
    4362                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                ''' 
    4579                self.gui_extension_points = {} 
     80                ''' 
     81                Registered GUI extension points. 
     82                ''' 
    4683 
    4784                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)) 
    4986 
    5087                log.debug('plugins: %s'%(self.plugins)) 
    51                  
     88 
    5289                self._activate_all_plugins() 
    53                  
    54                 log.debug('active: %s'%(self.active)) 
    55          
     90 
     91                log.debug('active: %s'%(self.active_plugins)) 
     92 
    5693        @log_calls('PluginManager') 
    5794        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 
    58120                if gui_extpoint_name in self.gui_extension_points: 
    59121                        for handlers in self.gui_extension_points[gui_extpoint_name]: 
     
    63125        def _activate_plugin(self, plugin): 
    64126                ''' 
    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 
    70133                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 
    77135                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( 
    80138                                        gui_extpoint_handlers) 
    81                          
     139 
    82140                if success: 
    83                         self.active.append(p) 
    84                          
     141                        self.active_plugins.append(plugin_object) 
     142 
    85143                return success 
    86                  
     144 
    87145        @log_calls('PluginManager') 
    88146        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 = [] 
    90153                for plugin in self.plugins: 
    91154                        self._activate_plugin(plugin) 
    92155 
    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                ''' 
    95174                plugins_found = [] 
    96175                if os.path.isdir(path): 
     
    101180                        log.debug(sys.path) 
    102181 
    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) 
    106185                                log.debug('  "%s"'%(file_path)) 
    107186                                if os.path.isfile(file_path): 
    108                                         module_name = os.path.splitext(file)[0] 
     187                                        module_name = os.path.splitext(file_name)[0] 
    109188                                        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('__'))]: 
    115192                                                module_attr = getattr(module, module_attr_name) 
    116193                                                log.debug('%s : %s'%(module_attr_name, module_attr)) 
    117                                                  
     194 
    118195                                                try: 
    119196                                                        if issubclass(module_attr, GajimPlugin) and \ 
    120                                                                         not module_attr is GajimPlugin: 
     197                                                           not module_attr is GajimPlugin: 
    121198                                                                log.debug('is subclass of GajimPlugin') 
    122199                                                                plugins_found.append(module_attr) 
    123                                                 except TypeError, e: 
     200                                                except TypeError, type_error: 
    124201                                                        log.debug('module_attr: %s, error : %s'%( 
    125                                                                         module_name+'.'+module_attr_name, 
    126                                                                         e)) 
     202                                                                module_name+'.'+module_attr_name, 
     203                                                                type_error)) 
    127204 
    128205                                        log.debug(module) 
  • branches/plugin-system/src/plugins/plugin.py

    r9737 r9745  
    2525''' 
    2626 
    27 import sys 
    28 from plugins.helpers import log, log_calls 
     27from plugins.helpers import log_calls 
    2928 
    3029class GajimPlugin(object): 
     30        ''' 
     31        Base class for implementing Gajim plugins. 
     32        ''' 
    3133        name = '' 
     34        ''' 
     35        Name of plugin. 
     36         
     37        Will be shown in plugins management GUI. 
     38         
     39        :type: unicode 
     40        ''' 
    3241        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        ''' 
    3352        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        ''' 
    3464        description = '' 
     65        ''' 
     66        Plugin description. 
     67         
     68        :type: unicode 
     69         
     70        :todo: should be allow rich text here (like HTML or reStructuredText)? 
     71        ''' 
    3572        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        ''' 
    3681        gui_extension_points = {} 
     82        ''' 
     83        Extension points that plugin wants to connect with. 
     84        ''' 
    3785         
    3886        @log_calls('GajimPlugin') 
  • branches/plugin-system/src/roster_window.py

    r9699 r9745  
    311311                        c4 = time.clock() 
    312312                 
    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 "" 
     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 "" 
    320320 
    321321         
     
    12171217                                self.draw_avatar(jid, account) 
    12181218                                yield True 
    1219                         print "--- Idle draw of %s -----------" % account 
    1220                         print "Draw contact and avatar", time.clock() - t 
    1221                         print "-------------------------------" 
     1219                        #print "--- Idle draw of %s -----------" % account 
     1220                        #print "Draw contact and avatar", time.clock() - t 
     1221                        #print "-------------------------------" 
    12221222                        yield False 
    12231223                 
  • branches/plugin-system/test/test_pluginmanager.py

    r9737 r9745  
    3333sys.path.append(gajim_root + '/src') 
    3434 
     35# a temporary version of ~/.gajim for testing 
     36configdir = gajim_root + '/test/tmp' 
     37 
     38import time 
     39 
     40# define _ for i18n 
     41import __builtin__ 
     42__builtin__._ = lambda x: x 
     43 
     44# wipe config directory 
     45import os 
     46if os.path.isdir(configdir): 
     47        import shutil 
     48        shutil.rmtree(configdir) 
     49 
     50os.mkdir(configdir) 
     51 
     52import common.configpaths 
     53common.configpaths.gajimpaths.init(configdir) 
     54common.configpaths.gajimpaths.init_profile() 
     55 
     56# for some reason common.gajim needs to be imported before xmpppy? 
     57from common import gajim 
     58from common import xmpp 
     59 
     60gajim.DATA_DIR = gajim_root + '/data' 
     61 
     62from common.stanza_session import StanzaSession 
     63 
     64# name to use for the test account 
     65account_name = 'test' 
     66 
    3567from plugins import PluginManager 
    3668 
    3769class PluginManagerTestCase(unittest.TestCase): 
    38     def setUp(self): 
    39         self.pluginmanager = PluginManager() 
    40      
    41     def tearDown(self): 
    42         pass 
    43      
    44     def test_01_Singleton(self): 
    45         """ 1. Checking whether PluginManger class is singleton. """ 
    46         self.pluginmanager.test_arg = 1 
    47         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 
    5486def suite(): 
    55     suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase) 
    56     return suite 
     87        suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase) 
     88        return suite 
    5789 
    5890if __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)