mirror of
https://github.com/yweber/lodel2.git
synced 2026-03-14 23:32:02 +01:00
Implements SessionHandlerPlugin ExtensionsPlugin + settings preload
Implements both Plugin child classes handling extensions and session handling. Modified the settings plugin preload piece of code. It is now adapted to the new plugins organisation. Each Plugin child classes provides a confspec indicating where the plugin list is stored in configuration.
This commit is contained in:
parent
8b00660dfd
commit
76e386e321
14 changed files with 104 additions and 94 deletions
|
|
@ -42,3 +42,5 @@
|
|||
from .hooks import LodelHook
|
||||
from .plugins import Plugin, CustomMethod
|
||||
from .datasource_plugin import DatasourcePlugin
|
||||
from .sessionhandler import SessionHandlerPlugin
|
||||
from .extensions import Extension
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ class DatasourcePlugin(Plugin):
|
|||
##@brief Stores confspecs indicating where DatasourcePlugin list is stored
|
||||
_plist_confspecs = {
|
||||
'section': 'lodel2',
|
||||
'key': 'datasources',
|
||||
'key': 'datasource_connectors',
|
||||
'default': None,
|
||||
'validator': SettingValidator('list', none_is_valid = False) }
|
||||
'validator': SettingValidator('strip', none_is_valid = False) }
|
||||
|
||||
def __init__(self, name):
|
||||
super().__init__(name)
|
||||
|
|
@ -30,10 +30,6 @@ class DatasourcePlugin(Plugin):
|
|||
def migration_handler(self):
|
||||
return self.loader_module().migration_handler_class()
|
||||
|
||||
@classmethod
|
||||
def plist_confspec(cls):
|
||||
return copy.copy(cls._plist_confspecs)
|
||||
|
||||
##@brief Return an initialized Datasource instance
|
||||
#@param ds_name str : The name of the datasource to instanciate
|
||||
#@param ro bool
|
||||
|
|
|
|||
12
lodel/plugin/extensions.py
Normal file
12
lodel/plugin/extensions.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from .plugins import Plugin
|
||||
from .exceptions import *
|
||||
from lodel.settings.validator import SettingValidator
|
||||
|
||||
class Extension(Plugin):
|
||||
|
||||
_plist_confspecs = {
|
||||
'section': 'lodel2',
|
||||
'key': 'extensions',
|
||||
'default': [],
|
||||
'validator': SettingValidator('list', none_is_valid = False)}
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import plugins
|
|||
from lodel import logger
|
||||
from lodel.settings.utils import SettingsError
|
||||
from .exceptions import *
|
||||
from lodel.exceptions import *
|
||||
|
||||
## @package lodel.plugins Lodel2 plugins management
|
||||
#
|
||||
|
|
@ -161,7 +162,6 @@ class MetaPlugType(type):
|
|||
#Here we can store all child classes of Plugin
|
||||
super().__init__(name, bases, attrs)
|
||||
if len(bases) == 1 and bases[0] == object:
|
||||
print("Dropped : ", name, bases)
|
||||
return
|
||||
self.__register_types()
|
||||
#list_name= [cls.__name__ for cls in __all_ptypes]
|
||||
|
|
@ -177,6 +177,9 @@ def plug_type_register(cls):
|
|||
__all_ptypes.append(cls)
|
||||
logger.info("New child class registered : %s" % cls.__name__)
|
||||
|
||||
def all_types():
|
||||
return copy.copy(__all_ptypes)
|
||||
|
||||
|
||||
##@brief Handle plugins
|
||||
#
|
||||
|
|
@ -205,6 +208,10 @@ class Plugin(object, metaclass=MetaPlugType):
|
|||
##@brief Store dict representation of discover cache content
|
||||
_discover_cache = None
|
||||
|
||||
#@brief Designed to store, in child classes, the confspec indicating \
|
||||
#where plugin list is stored
|
||||
_plist_confspecs = None
|
||||
|
||||
##@brief Plugin class constructor
|
||||
#
|
||||
# Called by setting in early stage of lodel2 boot sequence using classmethod
|
||||
|
|
@ -213,7 +220,7 @@ class Plugin(object, metaclass=MetaPlugType):
|
|||
# @param plugin_name str : plugin name
|
||||
# @throw PluginError
|
||||
def __init__(self, plugin_name):
|
||||
self.started()
|
||||
|
||||
self.name = plugin_name
|
||||
self.path = self.plugin_path(plugin_name)
|
||||
|
||||
|
|
@ -272,9 +279,9 @@ class Plugin(object, metaclass=MetaPlugType):
|
|||
#PLUGIN_VERSION_VARNAME in init file is mandatory
|
||||
self.__version = getattr(self.module, PLUGIN_VERSION_VARNAME)
|
||||
except AttributeError:
|
||||
msg = "Error that should not append : no %s found in plugin \
|
||||
init file. Malformed plugin"
|
||||
msg %= PLUGIN_VERSION_VARNAME
|
||||
msg = "Error that should not append while loading plugin '%s': no \
|
||||
%s found in plugin init file. Malformed plugin"
|
||||
msg %= (plugin_name, PLUGIN_VERSION_VARNAME)
|
||||
raise LodelFatalError(msg)
|
||||
|
||||
# Load plugin type
|
||||
|
|
@ -459,6 +466,13 @@ name differ from the one found in plugin's init file"
|
|||
def confspecs(self):
|
||||
return copy.copy(self.__confspecs)
|
||||
|
||||
@classmethod
|
||||
def plist_confspecs(cls):
|
||||
if cls._plist_confspecs is None:
|
||||
raise LodelFatalError('Unitialized _plist_confspecs attribute for \
|
||||
%s' % cls.__name__)
|
||||
return copy.copy(cls._plist_confspecs)
|
||||
|
||||
##@brief Retrieves plugin list confspecs
|
||||
#
|
||||
#This method ask for each Plugin child class the confspecs specifying where
|
||||
|
|
@ -525,7 +539,6 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
|
|||
pcls = DatasourcePlugin
|
||||
else:
|
||||
pcls = cls
|
||||
print(plugin_name, ptype, pcls)
|
||||
plugin = pcls(plugin_name)
|
||||
cls._plugin_instances[plugin_name] = plugin
|
||||
logger.debug("Plugin %s available." % plugin)
|
||||
|
|
@ -550,7 +563,7 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
|
|||
# @return the plugin directory path
|
||||
@classmethod
|
||||
def plugin_path(cls, plugin_name):
|
||||
cls.started()
|
||||
|
||||
plist = cls.plugin_list()
|
||||
if plugin_name not in plist:
|
||||
raise PluginError("No plugin named '%s' found" % plugin_name)
|
||||
|
|
@ -572,25 +585,10 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
|
|||
#
|
||||
# This method load path and preload plugins
|
||||
@classmethod
|
||||
def start(cls, plugins_directories, plugins):
|
||||
if cls._plugin_directories is not None:
|
||||
return
|
||||
import inspect
|
||||
self_path = inspect.getsourcefile(Plugin)
|
||||
default_plugin_path = os.path.abspath(self_path + '../../../../plugins')
|
||||
if plugins_directories is None:
|
||||
plugins_directories = list()
|
||||
plugins_directories += [ default_plugin_path ]
|
||||
cls._plugin_directories = list(set(plugins_directories))
|
||||
def start(cls, plugins):
|
||||
for plugin_name in plugins:
|
||||
cls.register(plugin_name)
|
||||
|
||||
@classmethod
|
||||
def started(cls, raise_if_not = True):
|
||||
res = cls._plugin_directories is not None
|
||||
if raise_if_not and not res:
|
||||
raise RuntimeError("Class Plugins is not initialized")
|
||||
|
||||
@classmethod
|
||||
def clear(cls):
|
||||
if cls._plugin_directories is not None:
|
||||
|
|
@ -657,7 +655,7 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
|
|||
##@brief Return a list of child Class Plugin
|
||||
@classmethod
|
||||
def plugin_types(cls):
|
||||
return cls.__all_ptypes
|
||||
return all_types()
|
||||
|
||||
##@brief Attempt to open and load plugin discover cache
|
||||
#@return discover cache
|
||||
|
|
@ -880,31 +878,3 @@ with %s" % (custom_method._method_name, custom_method))
|
|||
custom_method.__get_method())
|
||||
logger.debug(
|
||||
"Custom method %s added to target" % custom_method)
|
||||
|
||||
|
||||
##@page lodel2_plugins Lodel2 plugins system
|
||||
#
|
||||
# @par Plugin structure
|
||||
#A plugin is a package (a folder containing, at least, an __init__.py file.
|
||||
#This file should expose multiple things :
|
||||
# - a CONFSPEC variable containing configuration specifications
|
||||
# - an _activate() method that returns True if the plugin can be activated (
|
||||
# optionnal)
|
||||
#
|
||||
|
||||
|
||||
class SessionHandler(Plugin):
|
||||
__instance = None
|
||||
|
||||
def __init__(self, plugin_name):
|
||||
if self.__instance is None:
|
||||
super(Plugin, self).__init__(plugin_name)
|
||||
self.__instance = True
|
||||
else:
|
||||
raise RuntimeError("A SessionHandler Plugin is already plug")
|
||||
|
||||
class InterfacePlugin(Plugin):
|
||||
def __init__(self, plugin_name):
|
||||
super(Plugin, self).__init__(plugin_name)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class MetaLodelScript(type):
|
|||
#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)
|
||||
|
|
|
|||
28
lodel/plugin/sessionhandler.py
Normal file
28
lodel/plugin/sessionhandler.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from .plugins import Plugin
|
||||
from .exceptions import *
|
||||
from lodel.settings.validator import SettingValidator
|
||||
|
||||
##@page lodel2_plugins Lodel2 plugins system
|
||||
#
|
||||
# @par Plugin structure
|
||||
#A plugin is a package (a folder containing, at least, an __init__.py file.
|
||||
#This file should expose multiple things :
|
||||
# - a CONFSPEC variable containing configuration specifications
|
||||
# - an _activate() method that returns True if the plugin can be activated (
|
||||
# optionnal)
|
||||
#
|
||||
class SessionHandlerPlugin(Plugin):
|
||||
__instance = None
|
||||
_plist_confspecs = {
|
||||
'section': 'lodel2',
|
||||
'key': 'session_handler',
|
||||
'default': None,
|
||||
'validator': SettingValidator('string', none_is_valid=False)}
|
||||
|
||||
def __init__(self, plugin_name):
|
||||
if self.__instance is None:
|
||||
super(Plugin, self).__init__(plugin_name)
|
||||
self.__instance = True
|
||||
else:
|
||||
raise RuntimeError("A SessionHandler Plugin is already plug")
|
||||
|
||||
|
|
@ -11,7 +11,8 @@ from collections import namedtuple
|
|||
from lodel import logger
|
||||
from lodel.plugin.plugins import Plugin, PluginError
|
||||
from lodel.settings.utils import SettingsError, SettingsErrors
|
||||
from lodel.settings.validator import SettingValidator, LODEL2_CONF_SPECS
|
||||
from lodel.settings.validator import SettingValidator, LODEL2_CONF_SPECS, \
|
||||
confspec_append
|
||||
from lodel.settings.settings_loader import SettingsLoader
|
||||
|
||||
## @package lodel.settings.settings Lodel2 settings module
|
||||
|
|
@ -132,6 +133,25 @@ class Settings(object, metaclass=MetaSettings):
|
|||
def __bootstrap(self):
|
||||
logger.debug("Settings bootstraping")
|
||||
lodel2_specs = LODEL2_CONF_SPECS
|
||||
loader = SettingsLoader(self.__conf_dir)
|
||||
plugin_list = []
|
||||
for ptype in Plugin.plugin_types():
|
||||
pls = ptype.plist_confspecs()
|
||||
lodel2_specs = confspec_append(lodel2_specs, **pls)
|
||||
cur_list = loader.getoption(
|
||||
pls['section'],
|
||||
pls['key'],
|
||||
pls['validator'],
|
||||
pls['default'])
|
||||
if cur_list is None:
|
||||
continue
|
||||
try:
|
||||
if isinstance(cur_list, str):
|
||||
cur_list = [cur_list]
|
||||
plugin_list += cur_list
|
||||
except TypeError:
|
||||
plugin_list += [cur_list]
|
||||
#Checking confspecs
|
||||
for section in lodel2_specs:
|
||||
if section.lower() != section:
|
||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||
|
|
@ -139,30 +159,13 @@ class Settings(object, metaclass=MetaSettings):
|
|||
if kname.lower() != kname:
|
||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||
|
||||
# Load specs for the plugins list and plugins_path list conf keys
|
||||
plugins_opt_specs = lodel2_specs['lodel2']['plugins']
|
||||
plugins_path_opt_specs = lodel2_specs['lodel2']['plugins_path']
|
||||
# Init the settings loader
|
||||
loader = SettingsLoader(self.__conf_dir)
|
||||
# fetching list of plugins to load
|
||||
|
||||
plugins_list = loader.getoption( 'lodel2',
|
||||
'plugins',
|
||||
plugins_opt_specs[1],
|
||||
plugins_opt_specs[0],
|
||||
False)
|
||||
plugins_path = loader.getoption( 'lodel2',
|
||||
'plugins_path',
|
||||
plugins_path_opt_specs[1],
|
||||
plugins_path_opt_specs[0],
|
||||
False)
|
||||
# Starting the Plugins class
|
||||
logger.debug("Starting lodel.plugin.Plugin class")
|
||||
Plugin.start(plugins_path, plugins_list)
|
||||
Plugin.start(plugin_list)
|
||||
# Fetching conf specs from plugins
|
||||
specs = [lodel2_specs]
|
||||
errors = list()
|
||||
for plugin_name in plugins_list:
|
||||
for plugin_name in plugin_list:
|
||||
try:
|
||||
specs.append(Plugin.get(plugin_name).confspecs)
|
||||
except PluginError as e:
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ class SettingsLoader(object):
|
|||
# @return the option
|
||||
def getoption(self,section,keyname,validator,default_value=None,mandatory=False):
|
||||
conf=self.__conf
|
||||
|
||||
if section not in conf:
|
||||
conf[section] = dict()
|
||||
|
||||
|
|
|
|||
|
|
@ -357,17 +357,12 @@ SettingValidator.create_re_validator(
|
|||
#@param orig dict : the confspec to update
|
||||
#@param upd dict : the confspec to add
|
||||
#@return new confspec
|
||||
def confspec_append(orig, upd):
|
||||
for section in orig:
|
||||
if section in upd:
|
||||
orig[section].update(upd[section])
|
||||
else:
|
||||
orig[section] = upd[section]
|
||||
return orig
|
||||
|
||||
def confspec_add(orig, section, key, default, validator):
|
||||
def confspec_append(orig, section, key, validator, default):
|
||||
if section not in orig:
|
||||
section[orig] = dict()
|
||||
orig[section] = dict()
|
||||
if key not in orig[section]:
|
||||
orig[section][key] = (default, validator)
|
||||
return orig
|
||||
|
||||
##@brief Global specifications for lodel2 settings
|
||||
LODEL2_CONF_SPECS = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from lodel.settings.validator import SettingValidator
|
||||
|
||||
__plugin_name__ = 'filesystem_session'
|
||||
__version = [0,0,1]
|
||||
__version__ = [0,0,1]
|
||||
__type__ = 'session_handler'
|
||||
__loader__ = 'main.py'
|
||||
__confspec__ = "confspec.py"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from lodel.settings.validator import SettingValidator
|
|||
|
||||
CONFSPEC = {
|
||||
'lodel2.sessions':{
|
||||
'directory': ('/tmp/lodel2_session', SettingValidator('path')),
|
||||
'directory': ('/tmp/', SettingValidator('path')),
|
||||
'expiration': (900, SettingValidator('int')),
|
||||
'file_template': ('lodel2_%s.sess', SettingValidator('dummy'))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
from lodel.auth.exceptions import AuthenticationError
|
||||
from lodel.plugin import LodelHook
|
||||
|
||||
|
|
@ -38,3 +39,4 @@ def read_session(caller, sid):
|
|||
def clean_sessions(caller):
|
||||
FileSystemSession.clean()
|
||||
'''
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import unittest
|
|||
import os.path
|
||||
|
||||
from lodel.settings.utils import *
|
||||
from lodel.plugin.exceptions import *
|
||||
from lodel.settings.settings_loader import SettingsLoader
|
||||
|
||||
|
||||
|
|
@ -255,5 +256,5 @@ class SettingsLoaderTestCase(unittest.TestCase):
|
|||
def test_invalid_conf(self):
|
||||
from lodel.settings.settings import Settings
|
||||
Settings.stop()
|
||||
with self.assertRaises(SettingsErrors):
|
||||
with self.assertRaises((SettingsErrors, PluginError)):
|
||||
Settings('tests/settings/settings_examples/bad_conf.d')
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ sitename = noname
|
|||
plugins_path = /foo/plugins
|
||||
plugins = dummy, dummy_datasource
|
||||
runtest=True
|
||||
extensions = dummy
|
||||
datasource_connectors = dummy_datasource
|
||||
session_handler = filesystem_session
|
||||
|
||||
[lodel2.logging.stderr]
|
||||
level = Error
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue