Initial commit related to plug-in system:
- basic PluginManager class that loads plugins from *.py files
  in given directories
- Singleton metaclass was created to use with PluginManager;
  notice: __init__ of class is called only once (not like in code
	that is included in Python Cookbook)
- variable to keep paths of plugin directories has been created
  (common.gajim.PLUGINS_DIRS); also added initilization of these
  paths to common.ConfigPaths
- added global variable with PluginManager object:
  common.gajim.plugin_manager
- created customized logger for plugin system ('gajim.plugin_system')
- created function decorator plugins.helpers.log_calls which logs
  each call of function/method; it also logs when function is left
- base class Plugin for plug-in implementation added; not much
  here - only empty class attributes: name, short_name, authors,
	version, description
- based on Plugin class, first plugin was created named
  LengthNotifierPlugin; it is used to notify users when they
  exceed given length of message during writing it (text entry
  field highlights)
- first GUI extension points works when ChatControl object
  is created (it is used in mentioned plugin)
- added 'epydoc.conf' file customized a little bit (file
	is also in trunk now)
- fixed indentation in common.sleepy module (also in trunk
	now)
			
			
This commit is contained in:
		
							parent
							
								
									a5e218efba
								
							
						
					
					
						commit
						95b1e45920
					
				
					 12 changed files with 522 additions and 12 deletions
				
			
		
							
								
								
									
										28
									
								
								epydoc.conf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								epydoc.conf
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
[epydoc]
 | 
			
		||||
 | 
			
		||||
# Information about the project.
 | 
			
		||||
name: Gajim
 | 
			
		||||
url: http://gajim.org
 | 
			
		||||
 | 
			
		||||
verbosity: 3
 | 
			
		||||
imports: yes
 | 
			
		||||
redundant-details: yes
 | 
			
		||||
docformat: restructuredtext
 | 
			
		||||
# top: gajim
 | 
			
		||||
 | 
			
		||||
# The list of modules to document.  Modules can be named using
 | 
			
		||||
# dotted names, module filenames, or package directory names.
 | 
			
		||||
# This option may be repeated.
 | 
			
		||||
modules: src/plugins/*.py
 | 
			
		||||
 | 
			
		||||
# Write html output to the directory "apidocs"
 | 
			
		||||
#output: pdf
 | 
			
		||||
output: html
 | 
			
		||||
target: apidocs/
 | 
			
		||||
 | 
			
		||||
# Include all automatically generated graphs.  These graphs are
 | 
			
		||||
# generated using Graphviz dot.
 | 
			
		||||
graph: all
 | 
			
		||||
dotpath: /usr/bin/dot
 | 
			
		||||
graph-font: Sans
 | 
			
		||||
graph-font-size: 10
 | 
			
		||||
							
								
								
									
										95
									
								
								plugins/length_notifier.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								plugins/length_notifier.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Message length notifier plugin.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 06/01/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
from plugins import GajimPlugin
 | 
			
		||||
from plugins.helpers import log, log_calls
 | 
			
		||||
 | 
			
		||||
class LengthNotifierPlugin(GajimPlugin):
 | 
			
		||||
	name = 'Message Length Notifier'
 | 
			
		||||
	short_name = 'length_notifier'
 | 
			
		||||
	version = '0.1'
 | 
			
		||||
	description = '''Highlights message entry field in chat window when given 
 | 
			
		||||
length of message is exceeded.'''
 | 
			
		||||
	authors = ['Mateusz Biliński <mateusz@bilinski.it>']
 | 
			
		||||
 | 
			
		||||
	@log_calls('LengthNotifierPlugin')
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		super(LengthNotifierPlugin, self).__init__()
 | 
			
		||||
		
 | 
			
		||||
		self.__class__.gui_extension_points = {
 | 
			
		||||
			'chat_control' : (self.connect_with_chat_control,
 | 
			
		||||
							  self.disconnect_from_chat_control)
 | 
			
		||||
		}		
 | 
			
		||||
		
 | 
			
		||||
		self.MESSAGE_WARNING_LENGTH = 140
 | 
			
		||||
		self.WARNING_COLOR = gtk.gdk.color_parse('#F0DB3E')
 | 
			
		||||
		self.JIDS = []
 | 
			
		||||
		
 | 
			
		||||
	@log_calls('LengthNotifierPlugin')
 | 
			
		||||
	def textview_length_warning(self, tb, chat_control):
 | 
			
		||||
		tv = chat_control.msg_textview
 | 
			
		||||
		d = chat_control.length_notifier_plugin_data
 | 
			
		||||
		t = tb.get_text(tb.get_start_iter(), tb.get_end_iter())
 | 
			
		||||
		if t:
 | 
			
		||||
			len_t = len(t)
 | 
			
		||||
			#print("len_t: %d"%(len_t))
 | 
			
		||||
			if len_t>self.MESSAGE_WARNING_LENGTH:
 | 
			
		||||
				if not d['prev_color']:
 | 
			
		||||
					d['prev_color'] = tv.style.copy().base[gtk.STATE_NORMAL]
 | 
			
		||||
				tv.modify_base(gtk.STATE_NORMAL, self.WARNING_COLOR)
 | 
			
		||||
			elif d['prev_color']:
 | 
			
		||||
				tv.modify_base(gtk.STATE_NORMAL, d['prev_color'])
 | 
			
		||||
				d['prev_color'] = None
 | 
			
		||||
	
 | 
			
		||||
	@log_calls('LengthNotifierPlugin')
 | 
			
		||||
	def connect_with_chat_control(self, chat_control):
 | 
			
		||||
		jid = chat_control.contact.jid
 | 
			
		||||
		if self.jid_is_ok(jid):
 | 
			
		||||
			d = {'prev_color' : None}
 | 
			
		||||
			tv = chat_control.msg_textview
 | 
			
		||||
			b = tv.get_buffer()
 | 
			
		||||
			h_id = b.connect('changed', self.textview_length_warning, chat_control)
 | 
			
		||||
			d['h_id'] = h_id
 | 
			
		||||
			chat_control.length_notifier_plugin_data = d
 | 
			
		||||
			
 | 
			
		||||
			return True
 | 
			
		||||
		
 | 
			
		||||
		return False
 | 
			
		||||
	
 | 
			
		||||
	@log_calls('LengthNotifierPlugin')
 | 
			
		||||
	def disconnect_from_chat_control(self, chat_control):
 | 
			
		||||
		d = chat_control.length_notifier_plugin_data
 | 
			
		||||
		chat_control.msg_textview.get_buffer().disconnect(d['h_id'])
 | 
			
		||||
		if d['prev_color']:
 | 
			
		||||
			tv.modify_base(gtk.STATE_NORMAL, self.PREV_COLOR)
 | 
			
		||||
	
 | 
			
		||||
	@log_calls('LengthNotifierPlugin')
 | 
			
		||||
	def jid_is_ok(self, jid):
 | 
			
		||||
		return True
 | 
			
		||||
| 
						 | 
				
			
			@ -1139,6 +1139,8 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
		self.update_ui()
 | 
			
		||||
		# restore previous conversation
 | 
			
		||||
		self.restore_conversation()
 | 
			
		||||
		
 | 
			
		||||
		gajim.plugin_manager.gui_extension_point('chat_control', self)
 | 
			
		||||
 | 
			
		||||
	def on_avatar_eventbox_enter_notify_event(self, widget, event):
 | 
			
		||||
		'''we enter the eventbox area so we under conditions add a timeout
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,6 +92,10 @@ class ConfigPaths:
 | 
			
		|||
		self.add('DATA', os.path.join(u'..', windowsify(u'data')))
 | 
			
		||||
		self.add('HOME', fse(os.path.expanduser('~')))
 | 
			
		||||
		self.add('TMP', fse(tempfile.gettempdir()))
 | 
			
		||||
		
 | 
			
		||||
		# dirs for plugins
 | 
			
		||||
		self.add('PLUGINS_BASE', os.path.join(u'..', windowsify(u'plugins')))
 | 
			
		||||
		self.add_from_root('PLUGINS_USER', u'plugins')
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			import svn_config
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,7 @@ version = config.get('version')
 | 
			
		|||
connections = {} # 'account name': 'account (connection.Connection) instance'
 | 
			
		||||
verbose = False
 | 
			
		||||
ipython_window = None
 | 
			
		||||
plugin_manager = None
 | 
			
		||||
 | 
			
		||||
h = logging.StreamHandler()
 | 
			
		||||
f = logging.Formatter('%(asctime)s %(name)s: %(message)s', '%d %b %Y %H:%M:%S')
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +86,8 @@ MY_CACERTS =  gajimpaths['MY_CACERTS']
 | 
			
		|||
TMP = gajimpaths['TMP']
 | 
			
		||||
DATA_DIR = gajimpaths['DATA']
 | 
			
		||||
HOME_DIR = gajimpaths['HOME']
 | 
			
		||||
PLUGINS_DIRS = [gajimpaths['PLUGINS_BASE'],
 | 
			
		||||
				gajimpaths['PLUGINS_USER']]
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
	LANG = locale.getdefaultlocale()[0] # en_US, fr_FR, el_GR etc..
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,22 +35,22 @@ STATE_AWAKE	= 'awake'
 | 
			
		|||
 | 
			
		||||
SUPPORTED = True
 | 
			
		||||
try:
 | 
			
		||||
		if os.name == 'nt':
 | 
			
		||||
			import ctypes
 | 
			
		||||
	if os.name == 'nt':
 | 
			
		||||
		import ctypes
 | 
			
		||||
 | 
			
		||||
			GetTickCount = ctypes.windll.kernel32.GetTickCount
 | 
			
		||||
			GetLastInputInfo = ctypes.windll.user32.GetLastInputInfo
 | 
			
		||||
		GetTickCount = ctypes.windll.kernel32.GetTickCount
 | 
			
		||||
		GetLastInputInfo = ctypes.windll.user32.GetLastInputInfo
 | 
			
		||||
 | 
			
		||||
			class LASTINPUTINFO(ctypes.Structure):
 | 
			
		||||
				_fields_ = [('cbSize', ctypes.c_uint), ('dwTime', ctypes.c_uint)]
 | 
			
		||||
		class LASTINPUTINFO(ctypes.Structure):
 | 
			
		||||
			_fields_ = [('cbSize', ctypes.c_uint), ('dwTime', ctypes.c_uint)]
 | 
			
		||||
 | 
			
		||||
			lastInputInfo = LASTINPUTINFO()
 | 
			
		||||
			lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo)
 | 
			
		||||
		lastInputInfo = LASTINPUTINFO()
 | 
			
		||||
		lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo)
 | 
			
		||||
 | 
			
		||||
		elif sys.platform == 'darwin':
 | 
			
		||||
			import osx.idle as idle
 | 
			
		||||
		else: # unix
 | 
			
		||||
			import idle
 | 
			
		||||
	elif sys.platform == 'darwin':
 | 
			
		||||
		import osx.idle as idle
 | 
			
		||||
	else: # unix
 | 
			
		||||
		import idle
 | 
			
		||||
except:
 | 
			
		||||
	gajim.log.debug('Unable to load idle module')
 | 
			
		||||
	SUPPORTED = False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3461,6 +3461,10 @@ class Interface:
 | 
			
		|||
			gobject.timeout_add_seconds(2, self.process_connections)
 | 
			
		||||
		gobject.timeout_add_seconds(gajim.config.get(
 | 
			
		||||
			'check_idle_every_foo_seconds'), self.read_sleepy)
 | 
			
		||||
		
 | 
			
		||||
		# Creating plugin manager
 | 
			
		||||
		import plugins
 | 
			
		||||
		gajim.plugin_manager = plugins.PluginManager()
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	def sigint_cb(num, stack):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								src/plugins/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/plugins/__init__.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Main file of plugins package.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 05/30/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
from pluginmanager import PluginManager
 | 
			
		||||
from plugin import GajimPlugin
 | 
			
		||||
 | 
			
		||||
__all__ = ['PluginManager', 'GajimPlugin']
 | 
			
		||||
							
								
								
									
										113
									
								
								src/plugins/helpers.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/plugins/helpers.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Helper code related to plug-ins management system.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 05/30/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
__all__ = ['log', 'log_calls', 'Singleton']
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
log = logging.getLogger('gajim.plugin_system')
 | 
			
		||||
'''
 | 
			
		||||
Logger for code related to plug-in system.
 | 
			
		||||
 | 
			
		||||
:type: logging.Logger
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
consoleloghandler = logging.StreamHandler()
 | 
			
		||||
consoleloghandler.setLevel(1)
 | 
			
		||||
consoleloghandler.setFormatter(
 | 
			
		||||
        logging.Formatter('%(levelname)s: %(message)s'))
 | 
			
		||||
        #logging.Formatter('%(asctime)s %(name)s: %(levelname)s: %(message)s'))
 | 
			
		||||
log.setLevel(logging.DEBUG)
 | 
			
		||||
log.addHandler(consoleloghandler)
 | 
			
		||||
log.propagate = False
 | 
			
		||||
 | 
			
		||||
import functools
 | 
			
		||||
 | 
			
		||||
class log_calls(object):
 | 
			
		||||
    '''
 | 
			
		||||
    Decorator class for functions to easily log when they are entered and left.
 | 
			
		||||
    '''
 | 
			
		||||
    
 | 
			
		||||
    def __init__(self, classname='', log=log):
 | 
			
		||||
        '''
 | 
			
		||||
        :Keywords:
 | 
			
		||||
          classname : str
 | 
			
		||||
            Name of class to prefix function name (if function is a method).
 | 
			
		||||
          log : logging.Logger
 | 
			
		||||
            Logger to use when outputing debug information on when function has
 | 
			
		||||
            been entered and when left. By default: `plugins.helpers.log`
 | 
			
		||||
            is used.            
 | 
			
		||||
        '''
 | 
			
		||||
        
 | 
			
		||||
        self.full_func_name = ''
 | 
			
		||||
        '''
 | 
			
		||||
        Full name of function, with class name (as prefix) if given 
 | 
			
		||||
        to decorator.
 | 
			
		||||
        
 | 
			
		||||
        Otherwise, it's only function name retrieved from function object
 | 
			
		||||
        for which decorator was called.
 | 
			
		||||
        
 | 
			
		||||
        :type: str
 | 
			
		||||
        '''
 | 
			
		||||
        
 | 
			
		||||
        if classname:
 | 
			
		||||
            self.full_func_name = classname+'.'
 | 
			
		||||
        
 | 
			
		||||
    def __call__(self, f):
 | 
			
		||||
        '''
 | 
			
		||||
        :param f: function to be wrapped with logging statements
 | 
			
		||||
        
 | 
			
		||||
        :return: given function wrapped by *log.debug* statements
 | 
			
		||||
        :rtype: function
 | 
			
		||||
        '''
 | 
			
		||||
        self.full_func_name += f.func_name
 | 
			
		||||
        @functools.wraps(f)
 | 
			
		||||
        def wrapper(*args, **kwargs):
 | 
			
		||||
            log.debug('%(funcname)s() <entered>'%{
 | 
			
		||||
                'funcname': self.full_func_name})
 | 
			
		||||
            result = f(*args, **kwargs)
 | 
			
		||||
            log.debug('%(funcname)s() <left>'%{
 | 
			
		||||
                'funcname': self.full_func_name})
 | 
			
		||||
            return result
 | 
			
		||||
        return wrapper
 | 
			
		||||
 | 
			
		||||
class Singleton(type):
 | 
			
		||||
    '''
 | 
			
		||||
    Singleton metaclass.
 | 
			
		||||
    '''
 | 
			
		||||
    def __init__(cls,name,bases,dic):
 | 
			
		||||
        super(Singleton,cls).__init__(name,bases,dic)
 | 
			
		||||
        cls.instance=None
 | 
			
		||||
        
 | 
			
		||||
    def __call__(cls,*args,**kw):
 | 
			
		||||
        if cls.instance is None:
 | 
			
		||||
            cls.instance=super(Singleton,cls).__call__(*args,**kw)
 | 
			
		||||
            log.debug('%(classname)s - new instance created'%{
 | 
			
		||||
                'classname' : cls.__name__})
 | 
			
		||||
        else:
 | 
			
		||||
            log.debug('%(classname)s - returning already existing instance'%{
 | 
			
		||||
                'classname' : cls.__name__})
 | 
			
		||||
            
 | 
			
		||||
        return cls.instance
 | 
			
		||||
							
								
								
									
										40
									
								
								src/plugins/plugin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/plugins/plugin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Base class for implementing plugin.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 06/01/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
from plugins.helpers import log, log_calls
 | 
			
		||||
 | 
			
		||||
class GajimPlugin(object):
 | 
			
		||||
	name = ''
 | 
			
		||||
	short_name = ''
 | 
			
		||||
	version = ''
 | 
			
		||||
	description = ''
 | 
			
		||||
	authors = []
 | 
			
		||||
	gui_extension_points = {}
 | 
			
		||||
	
 | 
			
		||||
	@log_calls('GajimPlugin')
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		pass
 | 
			
		||||
							
								
								
									
										130
									
								
								src/plugins/pluginmanager.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/plugins/pluginmanager.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,130 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Helper code related to plug-ins management system.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 05/30/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
__all__ = ['PluginManager']
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import fnmatch
 | 
			
		||||
 | 
			
		||||
import common.gajim as gajim
 | 
			
		||||
 | 
			
		||||
from helpers import log, log_calls, Singleton
 | 
			
		||||
from plugin import GajimPlugin
 | 
			
		||||
 | 
			
		||||
class PluginManager(object):
 | 
			
		||||
	__metaclass__ = Singleton
 | 
			
		||||
 | 
			
		||||
	@log_calls('PluginManager')
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		self.plugins = []
 | 
			
		||||
		self.active = []
 | 
			
		||||
		self.gui_extension_points = {}
 | 
			
		||||
 | 
			
		||||
		for path in gajim.PLUGINS_DIRS:
 | 
			
		||||
			self.plugins.extend(self._scan_dir_for_plugins(path))
 | 
			
		||||
 | 
			
		||||
		log.debug('plugins: %s'%(self.plugins))
 | 
			
		||||
		
 | 
			
		||||
		self._activate_all_plugins()
 | 
			
		||||
		
 | 
			
		||||
		log.debug('active: %s'%(self.active))
 | 
			
		||||
	
 | 
			
		||||
	@log_calls('PluginManager')
 | 
			
		||||
	def gui_extension_point(self, gui_extpoint_name, *args):
 | 
			
		||||
		if gui_extpoint_name in self.gui_extension_points:
 | 
			
		||||
			for handlers in self.gui_extension_points[gui_extpoint_name]:
 | 
			
		||||
				handlers[0](*args)
 | 
			
		||||
 | 
			
		||||
	@log_calls('PluginManager')
 | 
			
		||||
	def _activate_plugin(self, plugin):
 | 
			
		||||
		'''
 | 
			
		||||
		:param plugin: Plugin to be activated.
 | 
			
		||||
		:type plugin: class object of GajimPlugin subclass
 | 
			
		||||
		'''
 | 
			
		||||
		p = plugin()
 | 
			
		||||
		
 | 
			
		||||
		success = True
 | 
			
		||||
		
 | 
			
		||||
		# :fix: what if only some handlers are successfully connected? we should
 | 
			
		||||
		# revert all those connections that where successfully made. Maybe
 | 
			
		||||
		# call 'self._deactivate_plugin()' or sth similar.
 | 
			
		||||
		# Looking closer - we only rewrite tuples here. Real check should be
 | 
			
		||||
		# made in method that invokes gui_extpoints handlers.
 | 
			
		||||
		for gui_extpoint_name, gui_extpoint_handlers in \
 | 
			
		||||
				p.gui_extension_points.iteritems():
 | 
			
		||||
			self.gui_extension_points.setdefault(gui_extpoint_name,[]).append(
 | 
			
		||||
					gui_extpoint_handlers)
 | 
			
		||||
			
 | 
			
		||||
		if success:
 | 
			
		||||
			self.active.append(p)
 | 
			
		||||
			
 | 
			
		||||
		return success
 | 
			
		||||
		
 | 
			
		||||
	@log_calls('PluginManager')
 | 
			
		||||
	def _activate_all_plugins(self):
 | 
			
		||||
		self.active = []
 | 
			
		||||
		for plugin in self.plugins:
 | 
			
		||||
			self._activate_plugin(plugin)
 | 
			
		||||
 | 
			
		||||
	@log_calls('PluginManager')
 | 
			
		||||
	def _scan_dir_for_plugins(self, path):
 | 
			
		||||
		plugins_found = []
 | 
			
		||||
		if os.path.isdir(path):
 | 
			
		||||
			dir_list = os.listdir(path)
 | 
			
		||||
			log.debug(dir_list)
 | 
			
		||||
 | 
			
		||||
			sys.path.insert(0, path)
 | 
			
		||||
			log.debug(sys.path)
 | 
			
		||||
 | 
			
		||||
			for file in fnmatch.filter(dir_list, '*.py'):
 | 
			
		||||
				log.debug('- "%s"'%(file))
 | 
			
		||||
				file_path = os.path.join(path, file)
 | 
			
		||||
				log.debug('  "%s"'%(file_path))
 | 
			
		||||
				if os.path.isfile(file_path):
 | 
			
		||||
					module_name = os.path.splitext(file)[0]
 | 
			
		||||
					module = __import__(module_name)
 | 
			
		||||
					filter_out_bad_names = \
 | 
			
		||||
										 lambda x: not (x.startswith('__') or 
 | 
			
		||||
														x.endswith('__'))
 | 
			
		||||
					for module_attr_name in filter(filter_out_bad_names,
 | 
			
		||||
												   dir(module)):
 | 
			
		||||
						module_attr = getattr(module, module_attr_name)
 | 
			
		||||
						log.debug('%s : %s'%(module_attr_name, module_attr))
 | 
			
		||||
						
 | 
			
		||||
						try:
 | 
			
		||||
							if issubclass(module_attr, GajimPlugin) and \
 | 
			
		||||
									not module_attr is GajimPlugin:
 | 
			
		||||
								log.debug('is subclass of GajimPlugin')
 | 
			
		||||
								plugins_found.append(module_attr)
 | 
			
		||||
						except TypeError, e:
 | 
			
		||||
							log.debug('module_attr: %s, error : %s'%(
 | 
			
		||||
									module_name+'.'+module_attr_name,
 | 
			
		||||
									e))
 | 
			
		||||
 | 
			
		||||
					log.debug(module)
 | 
			
		||||
 | 
			
		||||
		return plugins_found
 | 
			
		||||
							
								
								
									
										61
									
								
								test/test_pluginmanager.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								test/test_pluginmanager.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
## This file is part of Gajim.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is free software; you can redistribute it and/or modify
 | 
			
		||||
## it under the terms of the GNU General Public License as published
 | 
			
		||||
## by the Free Software Foundation; version 3 only.
 | 
			
		||||
##
 | 
			
		||||
## Gajim is distributed in the hope that it will be useful,
 | 
			
		||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
## GNU General Public License for more details.
 | 
			
		||||
##
 | 
			
		||||
## You should have received a copy of the GNU General Public License
 | 
			
		||||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Testing PluginManager class.
 | 
			
		||||
 | 
			
		||||
:author: Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:since: 05/30/2008
 | 
			
		||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
 | 
			
		||||
:license: GPL
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
 | 
			
		||||
sys.path.append(gajim_root + '/src')
 | 
			
		||||
 | 
			
		||||
from plugins import PluginManager
 | 
			
		||||
 | 
			
		||||
class PluginManagerTestCase(unittest.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.pluginmanager = PluginManager()
 | 
			
		||||
    
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        pass
 | 
			
		||||
    
 | 
			
		||||
    def test_01_Singleton(self):
 | 
			
		||||
        """ 1. Checking whether PluginManger class is singleton. """
 | 
			
		||||
        self.pluginmanager.test_arg = 1
 | 
			
		||||
        secondPluginManager = PluginManager()
 | 
			
		||||
        
 | 
			
		||||
        self.failUnlessEqual(id(secondPluginManager), id(self.pluginmanager),
 | 
			
		||||
            'Different IDs in references to PluginManager objects (not a singleton)')
 | 
			
		||||
        self.failUnlessEqual(secondPluginManager.test_arg, 1, 
 | 
			
		||||
            'References point to different PluginManager objects (not a singleton')
 | 
			
		||||
    
 | 
			
		||||
def suite():
 | 
			
		||||
    suite = unittest.TestLoader().loadTestsFromTestCase(PluginManagerTestCase)
 | 
			
		||||
    return suite
 | 
			
		||||
 | 
			
		||||
if __name__=='__main__':
 | 
			
		||||
    runner = unittest.TextTestRunner()
 | 
			
		||||
    test_suite = suite()
 | 
			
		||||
    runner.run(test_suite)
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue