mirror of
https://github.com/yweber/lodel2.git
synced 2025-11-01 03:59: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:
parent
d14a7377a2
commit
cddf221988
6 changed files with 207 additions and 13 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
29
lodel/plugin/core_scripts.py
Normal file
29
lodel/plugin/core_scripts.py
Normal 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']))
|
||||
|
||||
|
||||
|
|
@ -1,2 +1,5 @@
|
|||
class PluginError(Exception):
|
||||
pass
|
||||
|
||||
class LodelScriptError(Exception):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -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
145
lodel/plugin/scripts.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue