1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2025-10-31 19:49:02 +01:00

Lodel2 script helper class implementation (#122)

- Allow to extend easily lodel_admin.py script.
- Writtent first lodel_admin.py action : discover-plugins
This commit is contained in:
Yann 2016-08-18 14:49:31 +02:00
commit cddf221988
6 changed files with 207 additions and 13 deletions

View file

@ -32,10 +32,11 @@ if 'LODEL2_NO_SETTINGS_LOAD' not in os.environ:
if not settings.started():
settings('conf.d')
from lodel.settings import Settings
#Starts hooks
from lodel.plugin import LodelHook
from lodel.plugin import core_hooks
from lodel.plugin import core_scripts
def start():
#Load plugins

View file

@ -4,14 +4,20 @@ import sys
import os, os.path
import argparse
"""
#Dirty hack to solve symlinks problems :
# When loader was imported the original one (LODEL_LIBDIR/install/loader)
# because lodel_admin.py is a symlink from this folder
#Another solution can be delete loader from install folder
sys.path[0] = os.getcwd()
import loader
"""
##@brief Dirty hack to avoid problems with simlink to lodel2 lib folder
#
#In instance folder we got a loader.py (the one we want to import here when
#writing "import loader". The problem is that lodel_admin.py is a simlink to
#LODEL2LIB_FOLDER/install/lodel_admin.py . In this folder there is the
#generic loader.py template. And when writing "import loader" its
#LODEL2LIB_FOLDER/install/loader.py that gets imported.
#
#In order to solve this problem the _simlink_hack() function delete the
#LODEL2LIB_FOLDER/install entry from sys.path
#@note This problem will be solved once lodel lib dir will be in
#/usr/lib/python3/lodel
def _simlink_hack():
sys.path[0] = os.getcwd()
## @brief Utility method to generate python code given an emfile and a
# translator
@ -141,4 +147,10 @@ def update_plugin_discover_cache(path_list = None):
for pname, pinfos in res['plugins'].items():
print("\t- %s %s -> %s" % (
pname, pinfos['version'], pinfos['path']))
if __name__ == '__main__':
_simlink_hack()
import loader
loader.start()
from lodel.plugin.scripts import main_run
main_run()

View file

@ -0,0 +1,29 @@
import lodel.plugin.scripts as lodel_script
class DiscoverPlugin(lodel_script.LodelScript):
_action = 'discover-plugin'
_description = 'Walk through given folders looking for plugins'
@classmethod
def argparser_config(cls, parser):
parser.add_argument('-d', '--directory',
help="Directory to walk through looking for lodel2 plugins",
nargs='+')
parser.add_argument('-l', '--list-only', default=False,
action = 'store_true',
help="Use this option to print a list of discovered plugins \
without modifying existing cache")
@classmethod
def run(cls, args):
from lodel.plugin.plugins import Plugin
if args.directory is None or len(args.directory) == 0:
cls.help_exit("Specify a least one directory")
no_cache = args.list_only
res = Plugin.discover(args.directory, no_cache)
print("Found plugins in : %s" % ', '.join(args.directory))
for pname, pinfos in res['plugins'].items():
print("\t- %s(%s) in %s" % (
pname, pinfos['version'], pinfos['path']))

View file

@ -1,2 +1,5 @@
class PluginError(Exception):
pass
class LodelScriptError(Exception):
pass

View file

@ -512,9 +512,12 @@ name differ from the one found in plugin's init file"
##@brief Reccursively walk throught paths to find plugin, then stores
#found plugin in a file...
#@param paths list : list of directory paths
#@param no_cache bool : if true only return a list of found plugins
#without modifying the cache file
#@return a dict {'path_list': [...], 'plugins': { see @ref _discover }}
@classmethod
def discover(cls, paths = None):
def discover(cls, paths = None, no_cache = False):
logger.info("Running plugin discover")
if paths is None:
paths = DEFAULT_PLUGINS_PATH_LIST
@ -535,8 +538,9 @@ name differ from the one found in plugin's init file"
pass
result = {'path_list': paths, 'plugins': result}
#Writing to cache
with open(DISCOVER_CACHE_FILENAME, 'w+') as pdcache:
pdcache.write(json.dumps(result))
if not no_cache:
with open(DISCOVER_CACHE_FILENAME, 'w+') as pdcache:
pdcache.write(json.dumps(result))
return result
##@brief Return discover result

145
lodel/plugin/scripts.py Normal file
View file

@ -0,0 +1,145 @@
import argparse
import sys
from lodel import logger
from lodel.exceptions import *
##@brief Stores registered scripts
__registered_scripts = dict()
##@brief LodelScript metaclass that allows to "catch" child class
#declaration
#
#Automatic script registration on child class declaration
class MetaLodelScript(type):
def __init__(self, name, bases, attrs):
#Here we can store all child classes of LodelScript
super().__init__(name, bases, attrs)
if len(bases) == 1 and bases[0] == object:
print("Dropped : ", name, bases)
return
self.__register_script(name)
#_action initialization
if self._action is None:
logger.warning("%s._action is None. Trying to use class name as \
action identifier" % name)
self._action = name
self._action = self._action.lower()
if self._description is None:
self._description = self._default_description()
self._parser = argparse.ArgumentParser(
prog = self._prog_name,
description = self._description)
self.argparser_config(self._parser)
##@brief Handles script registration
#@note Script list is maitained in
#lodel.plugin.admin_script.__registered_scripts
def __register_script(self, name):
if self._action is None:
logger.warning("%s._action is None. Trying to use class name as \
action identifier" % name)
self._action = name
self._action = self._action.lower()
script_registration(self._action, self)
class LodelScript(object, metaclass=MetaLodelScript):
##@brief A string to identify the action
_action = None
##@brief Script descripiton (argparse argument)
_description = None
##@brief argparse.ArgumentParser instance
_parser = None
##@brief No instanciation
def __init__(self):
raise NotImplementedError("Static class")
##@brief Virtual method. Designed to initialize arguement parser.
#@param argparser ArgumentParser : Child class argument parser instance
#@return MUST return the argument parser (NOT SURE ABOUT THAT !! Maybe it \
#works by reference)
@classmethod
def argparser_config(cls, parser):
raise LodelScriptError("LodelScript.argparser_config() is a pure \
virtual method! MUST be implemented by ALL child classes")
##@brief Virtual method. Run the script
#@return None or an integer that will be the script return code
@classmethod
def run(cls, args):
raise LodelScriptError("LodelScript.run() is a pure virtual method. \
MUST be implemented by ALL child classes")
##@brief Called by main_run() to execute a script.
#
#Handles argument parsing and then call LodelScript.run()
@classmethod
def _run(cls):
args = cls._parser.parse_args()
return cls.run(args)
##@brief Append action name to the prog name
#@note See argparse.ArgumentParser() prog argument
@classmethod
def _prog_name(cls):
return '%s %s' % (sys.argv[0], self._action)
##@brief Return the default description for an action
@classmethod
def _default_description(cls):
return "Lodel2 script : %s" % cls._action
@classmethod
def help_exit(cls,msg = None, return_code = 1, exit_after = True):
if not (msg is None):
print(msg, file=sys.stderr)
cls._parser.print_help()
if exit_after:
exit(1)
def script_registration(action_name, cls):
__registered_scripts[action_name] = cls
logger.info("New script registered : %s" % action_name)
##@brief Return a list containing all available actions
def _available_actions():
return [ act for act in __registered_scripts ]
##@brief Returns default runner argument parser
def _default_parser():
action_list = _available_actions()
if len(action_list) > 0:
action_list = ', '.join(sorted(action_list))
else:
action_list = 'NO SCRIPT FOUND !'
parser = argparse.ArgumentParser(description = "Lodel2 script runner")
parser.add_argument('action', metavar="ACTION", type=str,
help="One of the following actions : %s" % action_list)
parser.add_argument('option', metavar="OPTIONS", type=str, nargs='*',
help="Action options. Use %s ACTION -h to have help on a specific \
action" % sys.argv[0])
return parser
def main_run():
default_parser = _default_parser()
if len(sys.argv) == 1:
default_parser.print_help()
exit(1)
#preparing sys.argv (deleting action)
action = sys.argv[1].lower()
del(sys.argv[1])
if action not in __registered_scripts:
print("Unknow action '%s'\n" % action, file=sys.stderr)
default_parser.print_help()
exit(1)
script = __registered_scripts[action]
ret = script._run()
ret = 0 if ret is None else ret
exit(ret)