123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- #
- # Metrix++, Copyright 2009-2019, Metrix++ Project
- # Link: https://github.com/metrixplusplus/metrixplusplus
- #
- # This file is a part of Metrix++ Tool.
- #
- import mpp.api
- import os
- import sys
- from configparser 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 = sorted(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()
- 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 list(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)
-
- required_plugins.sort(); # sort the plugin list to get similar results independant of the os
-
- # 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)
- if name not in required_and_dependant_plugins:
- 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(), [str(item['module'])])
- 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 <action> --help\n" +
- " python %prog <action> [options] -- [path 1] ... [path N]\n" +
- "\n" +
- "Actions: \n " + "\n ".join(sorted(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
|