[Dicson] use manifest.ini files in plugins to have a separate file for plugin's information. Fixes #5920

This commit is contained in:
Yann Leboulanger 2010-09-18 23:00:43 +02:00
parent 90d1ee3176
commit 0b448cfb10
20 changed files with 198 additions and 164 deletions

View File

@ -33,12 +33,6 @@ from plugins import GajimPlugin
from plugins.helpers import log, log_calls
class AcronymsExpanderPlugin(GajimPlugin):
name = u'Acronyms Expander'
short_name = u'acronyms_expander'
version = u'0.1'
description = u'''Replaces acronyms (or other strings) with given expansions/substitutes.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('AcronymsExpanderPlugin')
def init(self):

View File

@ -0,0 +1,9 @@
[info]
name: Acronyms Expander
short_name: acronyms_expander
version: 0.1
description: Replaces acronyms (or other strings) with given expansions/substitutes.
authors: Mateusz Biliński <mateusz@bilinski.it>
homepage: http://blog.bilinski.it

View File

@ -0,0 +1,10 @@
[info]
name: Banner Tweaks
short_name: banner_tweaks
version: 0.1
description: Allows user to tweak chat window banner appearance (eg. make it compact).
Based on patch by pb in ticket #4133:
http://trac.gajim.org/attachment/ticket/4133.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -43,15 +43,6 @@ from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class BannerTweaksPlugin(GajimPlugin):
name = u'Banner Tweaks'
short_name = u'banner_tweaks'
version = u'0.1'
description = u'''Allows user to tweak chat window banner appearance (eg. make it compact).
Based on patch by pb in ticket #4133:
http://trac.gajim.org/attachment/ticket/4133'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('BannerTweaksPlugin')
def init(self):

View File

@ -0,0 +1,10 @@
[info]
name: D-Bus Support
short_name: dbus
version: 0.1
description: D-Bus support. Based on remote_control module from
Gajim core but uses new events handling system.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -680,13 +680,6 @@ from plugins.helpers import log_calls, log
from common import ged
class DBusPlugin(GajimPlugin):
name = u'D-Bus Support'
short_name = u'dbus'
version = u'0.1'
description = u'''D-Bus support. Based on remote_control module from
Gajim core but uses new events handling system.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('DBusPlugin')
def init(self):

View File

@ -0,0 +1,8 @@
[info]
name: Events Dump
short_name: events_dump
version: 0.1
description: Dumps info about selected events to console.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -33,12 +33,6 @@ from plugins.helpers import log_calls, log
from common import ged
class EventsDumpPlugin(GajimPlugin):
name = u'Events Dump'
short_name = u'events_dump'
version = u'0.1'
description = u'''Dumps info about selected events to console.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('EventsDumpPlugin')
def init(self):

View File

@ -0,0 +1,8 @@
[info]
name: Google Translation
short_name: google_translation
version: 0.1
description: Translates (currently only incoming) messages using Google Translate.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -41,12 +41,6 @@ from common import ged
from common import nec
class GoogleTranslationPlugin(GajimPlugin):
name = u'Google Translation'
short_name = u'google_translation'
version = u'0.1'
description = u'''Translates (currently only incoming) messages using Google Translate.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('GoogleTranslationPlugin')
def init(self):

View File

@ -34,12 +34,6 @@ from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class LengthNotifierPlugin(GajimPlugin):
name = u'Message Length Notifier'
short_name = u'length_notifier'
version = u'0.1'
description = u'''Highlights message entry field in chat window when given length of message is exceeded.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('LengthNotifierPlugin')
def init(self):

View File

@ -0,0 +1,9 @@
[info]
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>
homepage = http://blog.bilinski.it

View File

@ -0,0 +1,11 @@
[info]
name: New Events Example
short_name: new_events_example
version: 0.1
description: Shows how to generate new network events based on existing one using Network Events Controller.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -38,12 +38,6 @@ from common import ged
from common import nec
class NewEventsExamplePlugin(GajimPlugin):
name = u'New Events Example'
short_name = u'new_events_example'
version = u'0.1'
description = u'''Shows how to generate new network events based on existing one using Network Events Controller.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('NewEventsExamplePlugin')
def init(self):

View File

@ -0,0 +1,10 @@
[info]
name: Roster Buttons
short_name: roster_buttons
version: 0.1
description: Adds quick action buttons to roster window.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -34,12 +34,6 @@ from plugins import GajimPlugin
from plugins.helpers import log, log_calls
class RosterButtonsPlugin(GajimPlugin):
name = u'Roster Buttons'
short_name = u'roster_buttons'
version = u'0.1'
description = u'''Adds quick action buttons to roster window.'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('RosterButtonsPlugin')
def init(self):

View File

@ -0,0 +1,11 @@
[info]
name: Snarl Notifications
short_name: snarl_notifications
version: 0.1
description: Shows events notification using Snarl (http://www.fullphat.net/) under Windows. Snarl needs to be installed in system.
PySnarl bindings are used (http://code.google.com/p/pysnarl/).
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -38,13 +38,6 @@ from plugins.helpers import log_calls, log
from common import ged
class SnarlNotificationsPlugin(GajimPlugin):
name = u'Snarl Notifications'
short_name = u'snarl_notifications'
version = u'0.1'
description = u'''Shows events notification using Snarl (http://www.fullphat.net/) under Windows. Snarl needs to be installed in system.
PySnarl bindings are used (http://code.google.com/p/pysnarl/).'''
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
homepage = u'http://blog.bilinski.it'
@log_calls('SnarlNotificationsPlugin')
def init(self):

View File

@ -104,7 +104,7 @@ class PluginsWindow(object):
def _display_installed_plugin_info(self, plugin):
self.plugin_name_label.set_text(plugin.name)
self.plugin_version_label.set_text(plugin.version)
self.plugin_authors_label.set_text(", ".join(plugin.authors))
self.plugin_authors_label.set_text(plugin.authors)
self.plugin_homepage_linkbutton.set_uri(plugin.homepage)
self.plugin_homepage_linkbutton.set_label(plugin.homepage)
self.plugin_homepage_linkbutton.set_property('sensitive', True)

View File

@ -31,6 +31,7 @@ import sys
import fnmatch
import zipfile
from shutil import rmtree
import ConfigParser
from common import gajim
from common import nec
@ -46,8 +47,9 @@ class PluginManager(object):
Currently:
- scans for plugins
- activates them
- handles GUI extension points, when called by GUI objects after plugin
is activated (by dispatching info about call to handlers in plugins)
- handles GUI extension points, when called by GUI objects after
plugin is activated (by dispatching info about call to handlers
in plugins)
:todo: add more info about how GUI extension points work
:todo: add list of available GUI extension points
@ -58,10 +60,11 @@ class PluginManager(object):
deactivation handler) [DONE?]
:todo: when plug-in is deactivated all GUI extension points are removed
from `PluginManager.gui_extension_points_handlers`. But when
object that invoked GUI extension point is abandoned by Gajim, eg.
closed ChatControl object, the reference to called GUI extension
points is still in `PluginManager.gui_extension_points`. These
should be removed, so that object can be destroyed by Python.
object that invoked GUI extension point is abandoned by Gajim,
eg. closed ChatControl object, the reference to called GUI
extension points is still in `PluginManager.gui_extension_points`
These should be removed, so that object can be destroyed by
Python.
Possible solution: add call to clean up method in classes
'destructors' (classes that register GUI extension points)
'''
@ -100,12 +103,8 @@ class PluginManager(object):
for path in gajim.PLUGINS_DIRS:
self.add_plugins(PluginManager.scan_dir_for_plugins(path))
#log.debug('plugins: %s'%(self.plugins))
self._activate_all_plugins_from_global_config()
#log.debug('active: %s'%(self.active_plugins))
@log_calls('PluginManager')
def _plugin_has_entry_in_global_config(self, plugin):
if gajim.config.get_per('plugins', plugin.short_name) is None:
@ -135,8 +134,9 @@ class PluginManager(object):
self.plugins.append(plugin)
plugin.active = False
else:
log.info('Not loading plugin %s v%s from module %s (identified by short name: %s). Plugin already loaded.'%(
plugin.name, plugin.version, plugin.__module__, plugin.short_name))
log.info('Not loading plugin %s v%s from module %s (identified by
short name: %s). Plugin already loaded.' % (plugin.name,
plugin.version, plugin.__module__, plugin.short_name))
@log_calls('PluginManager')
def add_plugins(self, plugin_classes):
@ -147,14 +147,14 @@ class PluginManager(object):
def gui_extension_point(self, gui_extpoint_name, *args):
'''
Invokes all handlers (from plugins) for particular GUI extension point
and adds it to collection for further processing (eg. by plugins not active
yet).
and adds it to collection for further processing (eg. by plugins not
active yet).
:param gui_extpoint_name: name of GUI extension point.
:type gui_extpoint_name: unicode
:param args: parameters to be passed to extension point handlers
(typically and object that invokes `gui_extension_point`; however,
this can be practically anything)
(typically and object that invokes `gui_extension_point`;
however, this can be practically anything)
:type args: tuple
:todo: GUI extension points must be documented well - names with
@ -165,12 +165,13 @@ class PluginManager(object):
:bug: 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.
Looking closer - we only rewrite tuples here. Real check should
be made in method that invokes gui_extpoints handlers.
'''
self._add_gui_extension_point_call_to_list(gui_extpoint_name, *args)
self._execute_all_handlers_of_gui_extension_point(gui_extpoint_name, *args)
self._execute_all_handlers_of_gui_extension_point(gui_extpoint_name,
*args)
@log_calls('PluginManager')
def remove_gui_extension_point(self, gui_extpoint_name, *args):
@ -194,11 +195,11 @@ class PluginManager(object):
extension points. The same arguments and the same name mean
the same extension point.
:todo: instead of using argument to identify which extpoint should be
removed, maybe add additional 'id' argument - this would work similar
hash in Python objects. 'id' would be calculated based on arguments
passed or on anything else (even could be constant). This would give
core developers (that add new extpoints) more freedom, but is this
necessary?
removed, maybe add additional 'id' argument - this would work
similar hash in Python objects. 'id' would be calculated based
on arguments passed or on anything else (even could be constant)
This would give core developers (that add new extpoints) more
freedom, but is this necessary?
:param gui_extpoint_name: name of GUI extension point.
:type gui_extpoint_name: unicode
@ -209,10 +210,8 @@ class PluginManager(object):
'''
if gui_extpoint_name in self.gui_extension_points:
#log.debug('Removing GUI extpoint\n name: %s\n args: %s'%(gui_extpoint_name, args))
self.gui_extension_points[gui_extpoint_name].remove(args)
@log_calls('PluginManager')
def _add_gui_extension_point_call_to_list(self, gui_extpoint_name, *args):
'''
@ -229,36 +228,37 @@ class PluginManager(object):
:type gui_extpoint_name: str
:param args: parameters to be passed to extension point handlers
(typically and object that invokes `gui_extension_point`; however,
this can be practically anything)
(typically and object that invokes `gui_extension_point`;
however, this can be practically anything)
:type args: tuple
'''
if ((gui_extpoint_name not in self.gui_extension_points)
or (args not in self.gui_extension_points[gui_extpoint_name])):
self.gui_extension_points.setdefault(gui_extpoint_name, []).append(args)
or (args not in self.gui_extension_points[gui_extpoint_name])):
self.gui_extension_points.setdefault(gui_extpoint_name,[]).append(
args)
@log_calls('PluginManager')
def _execute_all_handlers_of_gui_extension_point(self, gui_extpoint_name, *args):
def _execute_all_handlers_of_gui_extension_point(self, gui_extpoint_name,
*args):
if gui_extpoint_name in self.gui_extension_points_handlers:
for handlers in self.gui_extension_points_handlers[gui_extpoint_name]:
for handlers in self.gui_extension_points_handlers[
gui_extpoint_name]:
handlers[0](*args)
def _register_events_handlers_in_ged(self, plugin):
for event_name, handler in plugin.events_handlers.iteritems():
priority = handler[0]
handler_function = handler[1]
gajim.ged.register_event_handler(event_name,
priority,
handler_function)
gajim.ged.register_event_handler(event_name, priority,
handler_function)
def _remove_events_handler_from_ged(self, plugin):
for event_name, handler in plugin.events_handlers.iteritems():
priority = handler[0]
handler_function = handler[1]
gajim.ged.remove_event_handler(event_name,
priority,
handler_function)
gajim.ged.remove_event_handler(event_name, priority,
handler_function)
def _register_network_events_in_nec(self, plugin):
for event_class in plugin.events:
@ -308,16 +308,18 @@ class PluginManager(object):
# remove GUI extension points handlers (provided by plug-in) from
# handlers list
for gui_extpoint_name, gui_extpoint_handlers in \
plugin.gui_extension_points.iteritems():
self.gui_extension_points_handlers[gui_extpoint_name].remove(gui_extpoint_handlers)
plugin.gui_extension_points.iteritems():
self.gui_extension_points_handlers[gui_extpoint_name].remove(
gui_extpoint_handlers)
# detaching plug-in from handler GUI extension points (calling
# cleaning up method that must be provided by plug-in developer
# for each handled GUI extension point)
for gui_extpoint_name, gui_extpoint_handlers in \
plugin.gui_extension_points.iteritems():
plugin.gui_extension_points.iteritems():
if gui_extpoint_name in self.gui_extension_points:
for gui_extension_point_args in self.gui_extension_points[gui_extpoint_name]:
for gui_extension_point_args in self.gui_extension_points[
gui_extpoint_name]:
handler = gui_extpoint_handlers[1]
if handler:
handler(*gui_extension_point_args)
@ -338,16 +340,17 @@ class PluginManager(object):
@log_calls('PluginManager')
def _add_gui_extension_points_handlers_from_plugin(self, plugin):
for gui_extpoint_name, gui_extpoint_handlers in \
plugin.gui_extension_points.iteritems():
self.gui_extension_points_handlers.setdefault(gui_extpoint_name, []).append(
gui_extpoint_handlers)
plugin.gui_extension_points.iteritems():
self.gui_extension_points_handlers.setdefault(gui_extpoint_name,
[]).append(gui_extpoint_handlers)
@log_calls('PluginManager')
def _handle_all_gui_extension_points_with_plugin(self, plugin):
for gui_extpoint_name, gui_extpoint_handlers in \
plugin.gui_extension_points.iteritems():
plugin.gui_extension_points.iteritems():
if gui_extpoint_name in self.gui_extension_points:
for gui_extension_point_args in self.gui_extension_points[gui_extpoint_name]:
for gui_extension_point_args in self.gui_extension_points[
gui_extpoint_name]:
handler = gui_extpoint_handlers[0]
if handler:
handler(*gui_extension_point_args)
@ -359,7 +362,6 @@ class PluginManager(object):
Activated plugins are appended to `active_plugins` list.
'''
#self.active_plugins = []
for plugin in self.plugins:
self.activate_plugin(plugin)
@ -393,71 +395,76 @@ class PluginManager(object):
:todo: add scanning zipped modules
'''
plugins_found = []
if os.path.isdir(path):
dir_list = os.listdir(path)
#log.debug(dir_list)
conf = ConfigParser.ConfigParser()
fields = ('name', 'short_name', 'version', 'description', 'authors',
'homepage')
if not os.path.isdir(path):
return
sys.path.insert(0, path)
#log.debug(sys.path)
dir_list = os.listdir(path)
for elem_name in dir_list:
#log.debug('- "%s"'%(elem_name))
file_path = os.path.join(path, elem_name)
#log.debug(' "%s"'%(file_path))
sys.path.insert(0, path)
module = None
for elem_name in dir_list:
file_path = os.path.join(path, elem_name)
if os.path.isfile(file_path) and fnmatch.fnmatch(file_path, '*.py'):
module_name = os.path.splitext(elem_name)[0]
#log.debug('Possible module detected.')
try:
module = __import__(module_name)
#log.debug('Module imported.')
except ValueError, value_error:
pass
#log.debug('Module not imported successfully. ValueError: %s'%(value_error))
except ImportError, import_error:
pass
#log.debug('Module not imported successfully. ImportError: %s'%(import_error))
module = None
elif os.path.isdir(file_path) and scan_dirs:
module_name = elem_name
file_path += os.path.sep
#log.debug('Possible package detected.')
try:
module = __import__(module_name)
#log.debug('Package imported.')
except ValueError, value_error:
pass
#log.debug('Package not imported successfully. ValueError: %s'%(value_error))
except ImportError, import_error:
pass
#log.debug('Package not imported successfully. ImportError: %s'%(import_error))
if os.path.isfile(file_path) and fnmatch.fnmatch(file_path, '*.py'):
module_name = os.path.splitext(elem_name)[0]
try:
module = __import__(module_name)
except ValueError, value_error:
pass
except ImportError, import_error:
pass
elif os.path.isdir(file_path) and scan_dirs:
module_name = elem_name
file_path += os.path.sep
try:
module = __import__(module_name)
except ValueError, value_error:
pass
except ImportError, import_error:
pass
if module:
log.debug('Attributes processing started')
for module_attr_name in [attr_name for attr_name in dir(module)
if not (attr_name.startswith('__') or
attr_name.endswith('__'))]:
module_attr = getattr(module, module_attr_name)
log.debug('%s : %s'%(module_attr_name, module_attr))
if module is None:
continue
log.debug('Attributes processing started')
for module_attr_name in [attr_name for attr_name in dir(module)
if not (attr_name.startswith('__') or attr_name.endswith('__'))]:
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')
#log.debug('file_path: %s\nabspath: %s\ndirname: %s'%(file_path, os.path.abspath(file_path), os.path.dirname(os.path.abspath(file_path))))
#log.debug('file_path: %s\ndirname: %s\nabspath: %s'%(file_path, os.path.dirname(file_path), os.path.abspath(os.path.dirname(file_path))))
module_attr.__path__ = os.path.abspath(os.path.dirname(file_path))
plugins_found.append(module_attr)
except TypeError, type_error:
pass
#log.debug('module_attr: %s, error : %s'%(
#module_name+'.'+module_attr_name,
#type_error))
try:
if not issubclass(module_attr, GajimPlugin) or \
module_attr is GajimPlugin:
continue
log.debug('is subclass of GajimPlugin')
module_attr.__path__ = os.path.abspath(
os.path.dirname(file_path))
#log.debug(module)
manifest_path = os.path.join(module_attr.__path__,
'manifest.ini')
# read metadata from manifest.ini
if not os.path.isfile(manifest_path):
continue
conf.readfp(open(manifest_path, 'r'))
for option in fields:
if conf.get('info', option) is '':
raise ConfigParser.NoOptionError, 'field empty'
setattr(module_attr, option, conf.get('info', option))
conf.remove_section('info')
plugins_found.append(module_attr)
except TypeError, type_error:
pass
except ConfigParser.NoOptionError, type_error:
# all fields are required
pass
return plugins_found