#
# Metrix++, Copyright 2009-2013, Metrix++ Project
# Link: http://metrixplusplus.sourceforge.net
#
# This file is a part of Metrix++ Tool.
#
# Metrix++ 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 of the License.
#
# Metrix++ 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 Metrix++. If not, see .
#
import mpp.api
import os
import sys
import ConfigParser
import re
import optparse
class MultiOptionParser(optparse.OptionParser):
class MultipleOption(optparse.Option):
ACTIONS = optparse.Option.ACTIONS + ("multiopt",)
STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ("multiopt",)
TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ("multiopt",)
ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ("multiopt",)
def take_action(self, action, dest, opt, value, values, parser):
if action == "multiopt":
values.ensure_value(dest, []).append(value)
else:
optparse.Option.take_action(self, action, dest, opt, value, values, parser)
def __init__(self, *args, **kwargs):
optparse.OptionParser.__init__(self, *args, option_class=self.MultipleOption, **kwargs)
class Loader(object):
def __init__(self):
self.plugins = []
self.hash = {}
self.action = None
def get_action(self):
return self.action
def get_plugin(self, name):
return self.hash[name]['instance']
def iterate_plugins(self, is_reversed = False):
if is_reversed == False:
for item in self.plugins:
yield item['instance']
else:
for item in reversed(self.plugins):
yield item['instance']
def load(self, command, directories, args):
class IniContainer(object):
def __init__(self):
self.plugins = []
self.actions = []
self.hash = {}
def load_recursively(inicontainer, directory):
active_plugins = []
if os.path.exists(directory) == False or os.path.isdir(directory) == False:
return active_plugins
pattern = re.compile(r'.*[.]ini$', flags=re.IGNORECASE)
dirList = os.listdir(directory)
for fname in dirList:
fname = os.path.join(directory, fname)
if os.path.isdir(fname):
active_plugins += load_recursively(inicontainer, fname)
elif re.match(pattern, fname):
config = ConfigParser.ConfigParser()
config.read(fname)
item = {'package': config.get('Plugin', 'package'),
'module': config.get('Plugin', 'module'),
'class': config.get('Plugin', 'class'),
'version': config.get('Plugin', 'version'),
'depends': config.get('Plugin', 'depends'),
'actions': config.get('Plugin', 'actions'),
'enabled': config.getboolean('Plugin', 'enabled'),
'instance': None}
if item['enabled']:
item['actions'] = item['actions'].split(',')
for (ind, action) in enumerate(item['actions']):
action = action.strip()
item['actions'][ind] = action
if action not in inicontainer.actions + ['*', 'None', 'none', 'False', 'false']:
inicontainer.actions.append(action)
if action == '*' or action == command:
active_plugins.append(item['package'] + '.' + item['module'])
inicontainer.plugins.append(item)
inicontainer.hash[item['package'] + '.' + item['module']] = item
return active_plugins
def list_dependants_recursively(inicontainer, required_plugin_name):
assert required_plugin_name in inicontainer.hash.keys(), \
"depends section requires unknown plugin: " + required_plugin_name
item = inicontainer.hash[required_plugin_name]
if item['depends'] in ('None', 'none', 'False', 'false'):
return []
result = []
for child in item['depends'].split(','):
child = child.strip()
result += list_dependants_recursively(inicontainer, child)
result.append(child)
return result
# configure python path for loading
std_ext_dir = os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], 'ext')
std_ext_priority_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
for each in [std_ext_dir] + directories:
sys.path.append(each)
inicontainer = IniContainer()
# load available plugin ini files
required_plugins = []
for each in ([std_ext_priority_dir, std_ext_dir] + directories):
required_plugins += load_recursively(inicontainer, each)
# upgrade the list of required plugins
required_and_dependant_plugins = []
for name in required_plugins:
for each in list_dependants_recursively(inicontainer, name):
if each not in required_and_dependant_plugins:
required_and_dependant_plugins.append(each)
required_and_dependant_plugins.append(name)
# load
for plugin_name in required_and_dependant_plugins:
item = inicontainer.hash[plugin_name]
plugin = __import__(item['package'], globals(), locals(), [item['module']], -1)
module_attr = plugin.__getattribute__(item['module'])
class_attr = module_attr.__getattribute__(item['class'])
item['instance'] = class_attr.__new__(class_attr)
item['instance'].__init__()
item['instance'].set_name(item['package'] + "." + item['module'])
item['instance'].set_version(item['version'])
item['instance']._set_plugin_loader(self)
self.plugins.append(item)
self.hash[plugin_name] = item
optparser = MultiOptionParser(
usage = "Usage: python %prog --help\n" +
" python %prog --help\n" +
" python %prog [options] -- [path 1] ... [path N]\n" +
"\n" +
"Actions: \n " + "\n ".join(inicontainer.actions))
if command in ['--help', '--h', '-h']:
optparser.print_help()
exit(0)
if command.strip() == "":
optparser.error("Mandatory action argument required")
if command not in inicontainer.actions:
optparser.error("action {action}: Unknown command".format(action=command))
self.action = command
optparser = MultiOptionParser(
usage="Usage: python %prog --help\n"
" python %prog {command} --help\n"
" python %prog {command} [options] -- [path 1] ... [path N]".format(command=command))
for item in self.iterate_plugins():
if (isinstance(item, mpp.api.IConfigurable)):
item.declare_configuration(optparser)
(options, args) = optparser.parse_args(args)
for item in self.iterate_plugins():
if (isinstance(item, mpp.api.IConfigurable)):
item.configure(options)
for item in self.iterate_plugins():
item.initialize()
return args
def unload(self):
for item in self.iterate_plugins(is_reversed = True):
item.terminate()
def run(self, args):
exit_code = 0
for item in self.iterate_plugins():
if (isinstance(item, mpp.api.IRunable)):
exit_code += item.run(args)
return exit_code
def __repr__(self):
result = object.__repr__(self) + ' with loaded:'
for item in self.iterate_plugins():
result += '\n\t' + item.__repr__()
if isinstance(item, mpp.api.Parent):
result += ' with subscribed:'
for child in item.iterate_children():
result += '\n\t\t' + child.__repr__()
return result