# -*- coding: utf-8 -*-
'''Handles plugins. Loads/unloads them, starts/stops'''
# This file is part of emesene.
#
# Emesene 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; either version 2 of the License, or
# (at your option) any later version.
#
# emesene 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 emesene; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import sys
import paths
import emesenelib.common
import dialog
class PluginManager( object ):
'''This module handles the plugins'''
def __init__( self, controller ):
'''Constructor'''
self.userPlugins = []
self.systemPlugins = []
self.controller = controller
# a dict that contain an instance of the object plugin (because the
# module can have multiple clases)
self.plugin_data = {} #Meta-data of plugins
self.loaded_plugin = {}#Instances of plugins
self.scanPlugins()
for i in self.getModules():
self.inspectPlugin( i )
def scanPlugins(self):
'''updates userPlugins and systemPlugins'''
# we do the import here because we need to wait until the
# config creates the plugin directory on the user home
userPlugins = {}
sys.path.append(paths.CONFIG_DIR)
userPlugins = self.inspectPackage(paths.CONFIG_DIR, paths.PLUGINS_HOME)
systemPlugins = self.inspectPackage(paths.APP_PATH,
paths.PLUGINS_SYSTEM_WIDE)
# system plugins have preference over user plugins
# unless user plug's version is greater then sys' one
for i in userPlugins.keys():
if not i in systemPlugins:
# the plugin is only in user's place
continue
# both user and system have the plugin
elif not userPlugins[i]:
# user version is None, so it can't be less
del userPlugins[i]
elif not systemPlugins[i]:
del systemPlugins[i]
elif self.comparePluginVersions(userPlugins[i],
systemPlugins[i] ):
# user version is greater than system version
del systemPlugins[i]
else:
del userPlugins[i]
self.userPlugins = userPlugins.keys()
self.systemPlugins = systemPlugins.keys()
def inspectPackage(self, path, package):
'''Searches the given path for plugins in package.
Returns a dict with the found plugins and their versions, if any.'''
try:
__import__(package, globals(), locals(), [])
except:
print 'LO QUE!!!'
path = path + paths.DIR_SEP + package
modules = [ x.split( '.' )[ 0 ] for x in os.listdir( path ) \
if x.endswith( '.py' ) and not x.startswith( '_' ) \
and x != 'Plugin.py' ]
plugins = {}
plugins = plugins.fromkeys( modules )
for i in modules:
try:
mod = __import__( package + '.' + i, globals(), \
None, [ 'VERSION' ] )
plugins[i] = getattr( mod, 'VERSION' )
except AttributeError:
pass
except Exception, e:
del plugins[i]
dialog.error('Exception importing %s\n%s'
% (i, str(e)), title = 'PluginManager')
return plugins
def comparePluginVersions( self, v1, v2 ):
'''Compare versions of the format 'x.y.z'
Returns True if v1 and v2 can be compared and v1>v2, False otherwise '''
v1 = v1.strip().split('.')
v2 = v2.strip().split('.')
try:
for i in range( min( len( v1 ), len( v2 ) ) ):
if int( v1[ i ] ) > int( v2[ i ] ):
return True
if int( v1[ i ] ) < int( v2[ i ] ):
return False
except:
return False
if len( v1 ) > len( v2 ):
return True
else:
return False
def getModules(self):
'''Returns a list with the plugins full module names on the form:
module_name.plugin_name'''
user = [ paths.PLUGINS_HOME + '.' + x for x in self.userPlugins ]
system = [paths.PLUGINS_SYSTEM_WIDE + '.' + x
for x in self.systemPlugins]
user.extend( system )
return user
def getPlugins(self):
'''Returns a list with the plugins names'''
# list = self.userPlugins[:] # makes a copy of userPlugins
# list.extend( self.systemPlugins )
# return list
return self.plugin_data.keys()
def pluginToModuleName(self, plugin):
'''Converts a plugin name into a module name with full path'''
if plugin in self.systemPlugins:
return paths.PLUGINS_SYSTEM_WIDE + '.' + plugin
elif plugin in self.userPlugins:
return paths.PLUGINS_HOME + '.' + plugin
else:
return ''
def inspectPlugin( self, module, doReload=False ):
'''Loads plugin metadata without instanciating it'''
name = module.split('.')[ 1 ]
if module in self.getModules():
try:
mod = __import__( module, globals(), locals(), [] )
plugin = getattr( mod, name )
if doReload:
reload(plugin)
plugin_class = plugin.MainClass
data = {'module':module, 'plugin':plugin}
for field in ['name', 'displayName', 'description',
'authors', 'website']:
#exception raising is intentional
data[field] = getattr(plugin_class, field)
except Exception, e:
print 'Plugin ' + name + ' could not be inspected'
print 'Reason: ' + str(e)
else:
self.plugin_data[name] = data
def loadPlugin( self, name, doReload=False ):
'''Instanciate an object of the plugin, the possible exceptions should
be handled by the caller.'''
if name not in self.plugin_data:
return False
if doReload:
self.inspectPlugin(self.plugin_data[name]['module'], True)
plugin = self.plugin_data[name]['plugin']
try:
self.loaded_plugin[ name ] = plugin.MainClass( self.controller, \
self.controller.msn)
except Exception, e:
print 'Plugin ' + name + ' could not be initialized'
print 'Reason: ' + str( e )
def restartPlugin(self, name):
'''reload a plugin and restart it if it was enabled'''
plugin_instance = self.getPlugin(name)
if plugin_instance and self.isEnabled(name):
was_enabled = True
self.stopPlugin(name)
else:
was_enabled = False
self.loadPlugin(name, True)
# start the plugin again
plugin_instance = self.getPlugin(name)
if plugin_instance and was_enabled:
self.startPlugin(name)
def checkPlugin( self, name ):
'''check if the plugin can be initialized'''
if name in self.loaded_plugin:
return self.loaded_plugin[ name ].check()
else:
return [ False, 'The plugin isn\'t loaded' ]
def startPlugin( self, name ):
'''start the plugin, call check before this!'''
if not name in self.loaded_plugin:
if name in self.plugin_data:
self.loadPlugin(name)
else:
return False
if name in self.loaded_plugin:
self.loaded_plugin[ name ].start()
return True
else:
return False
def startActivePlugins(self):
'''Start the plugins in pluginList'''
plugins = self.controller.config.user['activePlugins'].split(',')
for name in plugins:
if name == '':
continue
if name not in self.loaded_plugin:
self.loadPlugin(name)
try:
(success, message) = self.checkPlugin(name)
except:
success = False
message = _('invalid check() return value')
if not success:
emesenelib.common.debug(
_('plugin %s could not be initialized, reason:') % name)
emesenelib.common.debug(message)
else:
self.startPlugin(name)
def stopPlugin(self, name):
'''stop the plugin'''
if name in self.loaded_plugin:
self.loaded_plugin[ name ].stop()
self.unloadPlugin(name)
return True
else:
return False
def unloadPlugin(self, name):
'''delete a plugin from loaded_plugin. import
Should reduce ram usage when deactivating a plugin.'''
if name in self.loaded_plugin:
del self.loaded_plugin[name]
def getNewModules(self):
'''finds new modules'''
#refresh user/system plugins
self.scanPlugins()
return [x for x in self.getModules()
if not x.split('.')[1] in self.plugin_data]
def getPlugin( self, name, autoLoad=False ):
'''return the plugin if exist or None.
If autoLoad is true, will load plugin'''
if name not in self.loaded_plugin and autoLoad:
self.loadPlugin(name)
if name in self.loaded_plugin:
return self.loaded_plugin[ name ]
else:
return None
def getPluginData( self, name ):
'''return the plugin if exist or None'''
if name in self.plugin_data:
return self.plugin_data[ name ]
else:
return None
def destroy( self ):
'''Stops all the running plugins and cleans references'''
for i in self.loaded_plugin.keys():
try:
self.loaded_plugin[ i ].stop()
self.unloadPlugin(i)
except:
emesenelib.common.debug('Plugin ' + self.loaded_plugin[i].name +
' failed to stop')
def isEnabled( self, name ):
'''checks if plugin called "name" is enabled'''
if name in self.plugin_data and name in self.loaded_plugin:
return self.loaded_plugin[name].enabled
else:
return False
|