mirror of
https://github.com/yweber/lodel2.git
synced 2026-07-13 10:41:59 +02:00
Merge branch 'lodel2-datahandlers'
This commit is contained in:
commit
5b09492048
39 changed files with 1173 additions and 1036 deletions
|
|
@ -12,6 +12,8 @@ AC_CONFIG_FILES([Makefile \
|
||||||
lodel/leapi/datahandlers/Makefile \
|
lodel/leapi/datahandlers/Makefile \
|
||||||
lodel/plugin/Makefile \
|
lodel/plugin/Makefile \
|
||||||
lodel/settings/Makefile \
|
lodel/settings/Makefile \
|
||||||
|
lodel/validator/Makefile \
|
||||||
|
lodel/mlnamedobject/Makefile \
|
||||||
lodel/utils/Makefile \
|
lodel/utils/Makefile \
|
||||||
progs/Makefile \
|
progs/Makefile \
|
||||||
progs/slim/Makefile \
|
progs/slim/Makefile \
|
||||||
|
|
|
||||||
|
|
@ -691,7 +691,7 @@ user.new_field(
|
||||||
group = user_group, data_handler = 'password', internal = False)
|
group = user_group, data_handler = 'password', internal = False)
|
||||||
|
|
||||||
|
|
||||||
#em.save('xmlfile', filename = 'examples/em_test.xml')
|
em.save('xmlfile', filename = 'editorial_models/em_simple.xml')
|
||||||
pickle_file = 'examples/em_simple.pickle'
|
pickle_file = 'examples/em_simple.pickle'
|
||||||
em.save('picklefile', filename = pickle_file)
|
em.save('picklefile', filename = pickle_file)
|
||||||
print("Output written in %s" % pickle_file)
|
print("Output written in %s" % pickle_file)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
SUBDIRS=auth editorial_model leapi plugin settings utils plugins
|
SUBDIRS=auth editorial_model leapi plugin settings utils plugins validator mlnamedobject
|
||||||
EXTRA_DIST = plugins
|
EXTRA_DIST = plugins
|
||||||
lodel_PYTHON = *.py
|
lodel_PYTHON = *.py
|
||||||
CLEANFILES = buildconf.py
|
CLEANFILES = buildconf.py
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#-*- coding: utf-8 -*-
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
##@package lodel.editorial_model.components
|
# @package lodel.editorial_model.components
|
||||||
#@brief Defines all @ref lodel2_em "EM" components
|
#@brief Defines all @ref lodel2_em "EM" components
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
|
|
||||||
|
|
@ -12,17 +12,20 @@ import hashlib
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.utils.mlstring': ['MlString'],
|
'lodel.utils.mlstring': ['MlString'],
|
||||||
|
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||||
'lodel.settings': ['Settings'],
|
'lodel.settings': ['Settings'],
|
||||||
'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'],
|
'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'],
|
||||||
'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME']})
|
'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME']})
|
||||||
|
|
||||||
##@brief Abstract class to represent editorial model components
|
# @brief Abstract class to represent editorial model components
|
||||||
# @see EmClass EmField
|
# @see EmClass EmField
|
||||||
# @todo forbid '.' in uid
|
# @todo forbid '.' in uid
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
class EmComponent(object):
|
|
||||||
|
|
||||||
##@brief Instanciate an EmComponent
|
|
||||||
|
class EmComponent(MlNamedObject):
|
||||||
|
|
||||||
|
# @brief Instanciate an EmComponent
|
||||||
# @param uid str : uniq identifier
|
# @param uid str : uniq identifier
|
||||||
# @param display_name MlString|str|dict : component display_name
|
# @param display_name MlString|str|dict : component display_name
|
||||||
# @param help_text MlString|str|dict : help_text
|
# @param help_text MlString|str|dict : help_text
|
||||||
|
|
@ -30,9 +33,8 @@ class EmComponent(object):
|
||||||
if self.__class__ == EmComponent:
|
if self.__class__ == EmComponent:
|
||||||
raise NotImplementedError('EmComponent is an abstract class')
|
raise NotImplementedError('EmComponent is an abstract class')
|
||||||
self.uid = uid
|
self.uid = uid
|
||||||
self.display_name = None if display_name is None else MlString(display_name)
|
|
||||||
self.help_text = None if help_text is None else MlString(help_text)
|
|
||||||
self.group = group
|
self.group = group
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.display_name is None:
|
if self.display_name is None:
|
||||||
|
|
@ -51,11 +53,11 @@ class EmComponent(object):
|
||||||
return int.from_bytes(m.digest(), byteorder='big')
|
return int.from_bytes(m.digest(), byteorder='big')
|
||||||
|
|
||||||
|
|
||||||
##@brief Handles editorial model objects classes
|
# @brief Handles editorial model objects classes
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
class EmClass(EmComponent):
|
class EmClass(EmComponent):
|
||||||
|
|
||||||
##@brief Instanciate a new EmClass
|
# @brief Instanciate a new EmClass
|
||||||
#@param uid str : uniq identifier
|
#@param uid str : uniq identifier
|
||||||
#@param display_name MlString|str|dict : component display_name
|
#@param display_name MlString|str|dict : component display_name
|
||||||
#@param abstract bool : set the class as asbtract if True
|
#@param abstract bool : set the class as asbtract if True
|
||||||
|
|
@ -85,11 +87,12 @@ class EmClass(EmComponent):
|
||||||
parents = [parents]
|
parents = [parents]
|
||||||
for parent in parents:
|
for parent in parents:
|
||||||
if not isinstance(parent, EmClass):
|
if not isinstance(parent, EmClass):
|
||||||
raise ValueError("<class EmClass> expected in parents list, but %s found" % type(parent))
|
raise ValueError(
|
||||||
|
"<class EmClass> expected in parents list, but %s found" % type(parent))
|
||||||
else:
|
else:
|
||||||
parents = list()
|
parents = list()
|
||||||
self.parents = parents
|
self.parents = parents
|
||||||
##@brief Stores EmFields instances indexed by field uid
|
# @brief Stores EmFields instances indexed by field uid
|
||||||
self.__fields = dict()
|
self.__fields = dict()
|
||||||
|
|
||||||
self.group = group
|
self.group = group
|
||||||
|
|
@ -112,7 +115,7 @@ class EmClass(EmComponent):
|
||||||
internal=True,
|
internal=True,
|
||||||
group=group)
|
group=group)
|
||||||
|
|
||||||
##@brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
|
# @brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
|
||||||
# @todo use Settings.editorialmodel.groups to determine wich fields should be returned
|
# @todo use Settings.editorialmodel.groups to determine wich fields should be returned
|
||||||
@property
|
@property
|
||||||
def __all_fields(self):
|
def __all_fields(self):
|
||||||
|
|
@ -122,12 +125,12 @@ class EmClass(EmComponent):
|
||||||
res.update(self.__fields)
|
res.update(self.__fields)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief RO access to datasource attribute
|
# @brief RO access to datasource attribute
|
||||||
@property
|
@property
|
||||||
def datasource(self):
|
def datasource(self):
|
||||||
return self.__datasource
|
return self.__datasource
|
||||||
|
|
||||||
##@brief Return the list of all dependencies
|
# @brief Return the list of all dependencies
|
||||||
#
|
#
|
||||||
# Reccursive parents listing
|
# Reccursive parents listing
|
||||||
@property
|
@property
|
||||||
|
|
@ -140,7 +143,7 @@ class EmClass(EmComponent):
|
||||||
res |= parent.parents_recc
|
res |= parent.parents_recc
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief EmField getter
|
# @brief EmField getter
|
||||||
# @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance
|
# @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance
|
||||||
# @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes
|
# @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes
|
||||||
# @return A list on EmFields instances (if uid is None) else return an EmField instance
|
# @return A list on EmFields instances (if uid is None) else return an EmField instance
|
||||||
|
|
@ -152,7 +155,7 @@ class EmClass(EmComponent):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelError("No such EmField '%s'" % uid)
|
raise EditorialModelError("No such EmField '%s'" % uid)
|
||||||
|
|
||||||
##@brief Keep in __fields only fields contained in active groups
|
# @brief Keep in __fields only fields contained in active groups
|
||||||
def _set_active_fields(self, active_groups):
|
def _set_active_fields(self, active_groups):
|
||||||
if not Settings.editorialmodel.editormode:
|
if not Settings.editorialmodel.editormode:
|
||||||
active_fields = []
|
active_fields = []
|
||||||
|
|
@ -162,7 +165,7 @@ class EmClass(EmComponent):
|
||||||
self.__fields = {fname: fdh for fname, fdh in self.__fields.items()
|
self.__fields = {fname: fdh for fname, fdh in self.__fields.items()
|
||||||
if fdh in active_fields}
|
if fdh in active_fields}
|
||||||
|
|
||||||
##@brief Add a field to the EmClass
|
# @brief Add a field to the EmClass
|
||||||
# @param emfield EmField : an EmField instance
|
# @param emfield EmField : an EmField instance
|
||||||
# @warning do not add an EmField allready in another class !
|
# @warning do not add an EmField allready in another class !
|
||||||
# @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
|
# @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
|
||||||
|
|
@ -170,16 +173,18 @@ class EmClass(EmComponent):
|
||||||
def add_field(self, emfield):
|
def add_field(self, emfield):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
if emfield.uid in self.__fields:
|
if emfield.uid in self.__fields:
|
||||||
raise EditorialModelError("Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self))
|
raise EditorialModelError(
|
||||||
|
"Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self))
|
||||||
# Incomplete field override check
|
# Incomplete field override check
|
||||||
if emfield.uid in self.__all_fields:
|
if emfield.uid in self.__all_fields:
|
||||||
parent_field = self.__all_fields[emfield.uid]
|
parent_field = self.__all_fields[emfield.uid]
|
||||||
if not emfield.data_handler_instance.can_override(parent_field.data_handler_instance):
|
if not emfield.data_handler_instance.can_override(parent_field.data_handler_instance):
|
||||||
raise AttributeError("'%s' field override a parent field, but data_handles are not compatible" % emfield.uid)
|
raise AttributeError(
|
||||||
|
"'%s' field override a parent field, but data_handles are not compatible" % emfield.uid)
|
||||||
self.__fields[emfield.uid] = emfield
|
self.__fields[emfield.uid] = emfield
|
||||||
return emfield
|
return emfield
|
||||||
|
|
||||||
##@brief Create a new EmField and add it to the EmClass
|
# @brief Create a new EmField and add it to the EmClass
|
||||||
# @param data_handler str : A DataHandler name
|
# @param data_handler str : A DataHandler name
|
||||||
# @param uid str : the EmField uniq id
|
# @param uid str : the EmField uniq id
|
||||||
# @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() )
|
# @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() )
|
||||||
|
|
@ -212,11 +217,11 @@ class EmClass(EmComponent):
|
||||||
return "<class %s EmClass uid=%s>" % (abstract, repr(self.uid))
|
return "<class %s EmClass uid=%s>" % (abstract, repr(self.uid))
|
||||||
|
|
||||||
|
|
||||||
##@brief Handles editorial model classes fields
|
# @brief Handles editorial model classes fields
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
class EmField(EmComponent):
|
class EmField(EmComponent):
|
||||||
|
|
||||||
##@brief Instanciate a new EmField
|
# @brief Instanciate a new EmField
|
||||||
# @param uid str : uniq identifier
|
# @param uid str : uniq identifier
|
||||||
# @param display_name MlString|str|dict : field display_name
|
# @param display_name MlString|str|dict : field display_name
|
||||||
# @param data_handler str : A DataHandler name
|
# @param data_handler str : A DataHandler name
|
||||||
|
|
@ -226,15 +231,15 @@ class EmField(EmComponent):
|
||||||
def __init__(self, uid, data_handler, em_class=None, display_name=None, help_text=None, group=None, **handler_kwargs):
|
def __init__(self, uid, data_handler, em_class=None, display_name=None, help_text=None, group=None, **handler_kwargs):
|
||||||
from lodel.leapi.datahandlers.base_classes import DataHandler
|
from lodel.leapi.datahandlers.base_classes import DataHandler
|
||||||
super().__init__(uid, display_name, help_text, group)
|
super().__init__(uid, display_name, help_text, group)
|
||||||
##@brief The data handler name
|
# @brief The data handler name
|
||||||
self.data_handler_name = data_handler
|
self.data_handler_name = data_handler
|
||||||
##@brief The data handler class
|
# @brief The data handler class
|
||||||
self.data_handler_cls = DataHandler.from_name(data_handler)
|
self.data_handler_cls = DataHandler.from_name(data_handler)
|
||||||
##@brief The data handler instance associated with this EmField
|
# @brief The data handler instance associated with this EmField
|
||||||
self.data_handler_instance = self.data_handler_cls(**handler_kwargs)
|
self.data_handler_instance = self.data_handler_cls(**handler_kwargs)
|
||||||
##@brief Stores data handler instanciation options
|
# @brief Stores data handler instanciation options
|
||||||
self.data_handler_options = handler_kwargs
|
self.data_handler_options = handler_kwargs
|
||||||
##@brief Stores the emclass that contains this field (set by EmClass.add_field() method)
|
# @brief Stores the emclass that contains this field (set by EmClass.add_field() method)
|
||||||
self._emclass = em_class
|
self._emclass = em_class
|
||||||
if self._emclass is None:
|
if self._emclass is None:
|
||||||
warnings.warn("No EmClass for field %s" % uid)
|
warnings.warn("No EmClass for field %s" % uid)
|
||||||
|
|
@ -243,13 +248,13 @@ class EmField(EmComponent):
|
||||||
else:
|
else:
|
||||||
group.add_components([self])
|
group.add_components([self])
|
||||||
|
|
||||||
##@brief Returns data_handler_name attribute
|
# @brief Returns data_handler_name attribute
|
||||||
def get_data_handler_name(self):
|
def get_data_handler_name(self):
|
||||||
return copy.copy(self.data_handler_name)
|
return copy.copy(self.data_handler_name)
|
||||||
|
|
||||||
##@brief Returns data_handler_cls attribute
|
# @brief Returns data_handler_cls attribute
|
||||||
def get_data_handler_cls(self):
|
def get_data_handler_cls(self):
|
||||||
return copy.copy(selfdata_handler_cls)
|
return copy.copy(self.data_handler_cls)
|
||||||
|
|
||||||
##@brief Returne the uid of the emclass which contains this field
|
##@brief Returne the uid of the emclass which contains this field
|
||||||
def get_emclass_uid(self):
|
def get_emclass_uid(self):
|
||||||
|
|
@ -266,11 +271,13 @@ class EmField(EmComponent):
|
||||||
'utf-8')
|
'utf-8')
|
||||||
).digest(), byteorder='big')
|
).digest(), byteorder='big')
|
||||||
|
|
||||||
##@brief Handles functionnal group of EmComponents
|
# @brief Handles functionnal group of EmComponents
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
class EmGroup(object):
|
|
||||||
|
|
||||||
##@brief Create a new EmGroup
|
|
||||||
|
class EmGroup(MlNamedObject):
|
||||||
|
|
||||||
|
# @brief Create a new EmGroup
|
||||||
# @note you should NEVER call the constructor yourself. Use Model.add_group instead
|
# @note you should NEVER call the constructor yourself. Use Model.add_group instead
|
||||||
# @param uid str : Uniq identifier
|
# @param uid str : Uniq identifier
|
||||||
# @param depends list : A list of EmGroup dependencies
|
# @param depends list : A list of EmGroup dependencies
|
||||||
|
|
@ -278,22 +285,21 @@ class EmGroup(object):
|
||||||
# @param help_text MlString|str :
|
# @param help_text MlString|str :
|
||||||
def __init__(self, uid, depends=None, display_name=None, help_text=None):
|
def __init__(self, uid, depends=None, display_name=None, help_text=None):
|
||||||
self.uid = uid
|
self.uid = uid
|
||||||
##@brief Stores the list of groups that depends on this EmGroup indexed by uid
|
# @brief Stores the list of groups that depends on this EmGroup indexed by uid
|
||||||
self.required_by = dict()
|
self.required_by = dict()
|
||||||
##@brief Stores the list of dependencies (EmGroup) indexed by uid
|
# @brief Stores the list of dependencies (EmGroup) indexed by uid
|
||||||
self.require = dict()
|
self.require = dict()
|
||||||
##@brief Stores the list of EmComponent instances contained in this group
|
# @brief Stores the list of EmComponent instances contained in this group
|
||||||
self.__components = set()
|
self.__components = set()
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
self.display_name = None if display_name is None else MlString(display_name)
|
|
||||||
self.help_text = None if help_text is None else MlString(help_text)
|
|
||||||
if depends is not None:
|
if depends is not None:
|
||||||
for grp in depends:
|
for grp in depends:
|
||||||
if not isinstance(grp, EmGroup):
|
if not isinstance(grp, EmGroup):
|
||||||
raise ValueError("EmGroup expected in depends argument but %s found" % grp)
|
raise ValueError("EmGroup expected in depends argument but %s found" % grp)
|
||||||
self.add_dependencie(grp)
|
self.add_dependencie(grp)
|
||||||
|
|
||||||
##@brief Returns EmGroup dependencie
|
# @brief Returns EmGroup dependencie
|
||||||
# @param recursive bool : if True return all dependencies and their dependencies
|
# @param recursive bool : if True return all dependencies and their dependencies
|
||||||
# @return a dict of EmGroup identified by uid
|
# @return a dict of EmGroup identified by uid
|
||||||
def dependencies(self, recursive=False):
|
def dependencies(self, recursive=False):
|
||||||
|
|
@ -309,7 +315,7 @@ class EmGroup(object):
|
||||||
res[new_dep.uid] = new_dep
|
res[new_dep.uid] = new_dep
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Returns EmGroup applicants
|
# @brief Returns EmGroup applicants
|
||||||
# @param recursive bool : if True return all dependencies and their dependencies
|
# @param recursive bool : if True return all dependencies and their dependencies
|
||||||
# @returns a dict of EmGroup identified by uid
|
# @returns a dict of EmGroup identified by uid
|
||||||
def applicants(self, recursive=False):
|
def applicants(self, recursive=False):
|
||||||
|
|
@ -325,28 +331,30 @@ class EmGroup(object):
|
||||||
res[new_app.uid] = new_app
|
res[new_app.uid] = new_app
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Returns EmGroup components
|
# @brief Returns EmGroup components
|
||||||
# @returns a copy of the set of components
|
# @returns a copy of the set of components
|
||||||
def components(self):
|
def components(self):
|
||||||
return (self.__components).copy()
|
return (self.__components).copy()
|
||||||
|
|
||||||
##@brief Returns EmGroup display_name
|
# @brief Returns EmGroup display_name
|
||||||
# @param lang str | None : If None return default lang translation
|
# @param lang str | None : If None return default lang translation
|
||||||
# @returns None if display_name is None, a str for display_name else
|
# @returns None if display_name is None, a str for display_name else
|
||||||
def get_display_name(self, lang=None):
|
def get_display_name(self, lang=None):
|
||||||
name = self.display_name
|
name = self.display_name
|
||||||
if name is None : return None
|
if name is None:
|
||||||
return name.get(lang);
|
return None
|
||||||
|
return name.get(lang)
|
||||||
|
|
||||||
##@brief Returns EmGroup help_text
|
# @brief Returns EmGroup help_text
|
||||||
# @param lang str | None : If None return default lang translation
|
# @param lang str | None : If None return default lang translation
|
||||||
# @returns None if display_name is None, a str for display_name else
|
# @returns None if display_name is None, a str for display_name else
|
||||||
def get_help_text(self, lang=None):
|
def get_help_text(self, lang=None):
|
||||||
help = self.help_text
|
help = self.help_text
|
||||||
if help is None : return None
|
if help is None:
|
||||||
return help.get(lang);
|
return None
|
||||||
|
return help.get(lang)
|
||||||
|
|
||||||
##@brief Add components in a group
|
# @brief Add components in a group
|
||||||
# @param components list : EmComponent instances list
|
# @param components list : EmComponent instances list
|
||||||
def add_components(self, components):
|
def add_components(self, components):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
|
|
@ -357,10 +365,11 @@ class EmGroup(object):
|
||||||
msg %= (component, self)
|
msg %= (component, self)
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
elif not isinstance(component, EmClass):
|
elif not isinstance(component, EmClass):
|
||||||
raise EditorialModelError("Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
|
raise EditorialModelError(
|
||||||
|
"Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
|
||||||
self.__components |= set(components)
|
self.__components |= set(components)
|
||||||
|
|
||||||
##@brief Add a dependencie
|
# @brief Add a dependencie
|
||||||
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
||||||
def add_dependencie(self, grp):
|
def add_dependencie(self, grp):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
|
|
@ -368,7 +377,8 @@ class EmGroup(object):
|
||||||
for group in grp:
|
for group in grp:
|
||||||
self.add_dependencie(group)
|
self.add_dependencie(group)
|
||||||
return
|
return
|
||||||
except TypeError: pass
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if grp.uid in self.require:
|
if grp.uid in self.require:
|
||||||
return
|
return
|
||||||
|
|
@ -377,7 +387,7 @@ class EmGroup(object):
|
||||||
self.require[grp.uid] = grp
|
self.require[grp.uid] = grp
|
||||||
grp.required_by[self.uid] = self
|
grp.required_by[self.uid] = self
|
||||||
|
|
||||||
##@brief Add a applicant
|
# @brief Add a applicant
|
||||||
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
||||||
# Useless ???
|
# Useless ???
|
||||||
def add_applicant(self, grp):
|
def add_applicant(self, grp):
|
||||||
|
|
@ -386,7 +396,8 @@ class EmGroup(object):
|
||||||
for group in grp:
|
for group in grp:
|
||||||
self.add_applicant(group)
|
self.add_applicant(group)
|
||||||
return
|
return
|
||||||
except TypeError: pass
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if grp.uid in self.required_by:
|
if grp.uid in self.required_by:
|
||||||
return
|
return
|
||||||
|
|
@ -395,17 +406,17 @@ class EmGroup(object):
|
||||||
self.required_by[grp.uid] = grp
|
self.required_by[grp.uid] = grp
|
||||||
grp.require[self.uid] = self
|
grp.require[self.uid] = self
|
||||||
|
|
||||||
##@brief Search for circular dependencie
|
# @brief Search for circular dependencie
|
||||||
# @return True if circular dep found else False
|
# @return True if circular dep found else False
|
||||||
def __circular_dependencie(self, new_dep):
|
def __circular_dependencie(self, new_dep):
|
||||||
return self.uid in new_dep.dependencies(True)
|
return self.uid in new_dep.dependencies(True)
|
||||||
|
|
||||||
##@brief Search for circular applicant
|
# @brief Search for circular applicant
|
||||||
# @return True if circular app found else False
|
# @return True if circular app found else False
|
||||||
def __circular_applicant(self, new_app):
|
def __circular_applicant(self, new_app):
|
||||||
return self.uid in new_app.applicants(True)
|
return self.uid in new_app.applicants(True)
|
||||||
|
|
||||||
##@brief Fancy string representation of an EmGroup
|
# @brief Fancy string representation of an EmGroup
|
||||||
# @return a string
|
# @return a string
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.display_name is None:
|
if self.display_name is None:
|
||||||
|
|
@ -431,7 +442,7 @@ class EmGroup(object):
|
||||||
byteorder='big'
|
byteorder='big'
|
||||||
)
|
)
|
||||||
|
|
||||||
##@brief Complete string representation of an EmGroup
|
# @brief Complete string representation of an EmGroup
|
||||||
# @return a string
|
# @return a string
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<class EmGroup '%s' depends : [%s]>" % (self.uid, ', '.join([duid for duid in self.dependencies(False)]))
|
return "<class EmGroup '%s' depends : [%s]>" % (self.uid, ', '.join([duid for duid in self.dependencies(False)]))
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import copy
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.utils.mlstring': ['MlString'],
|
'lodel.utils.mlstring': ['MlString'],
|
||||||
|
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||||
'lodel.logger': 'logger',
|
'lodel.logger': 'logger',
|
||||||
'lodel.settings': ['Settings'],
|
'lodel.settings': ['Settings'],
|
||||||
'lodel.settings.utils': ['SettingsError'],
|
'lodel.settings.utils': ['SettingsError'],
|
||||||
|
|
@ -14,27 +15,32 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.editorial_model.components': ['EmClass', 'EmField', 'EmGroup']})
|
'lodel.editorial_model.components': ['EmClass', 'EmField', 'EmGroup']})
|
||||||
|
|
||||||
|
|
||||||
##@brief Describe an editorial model
|
# @brief Describe an editorial model
|
||||||
#@ingroup lodel2_em
|
#@ingroup lodel2_em
|
||||||
class EditorialModel(object):
|
class EditorialModel(MlNamedObject):
|
||||||
|
|
||||||
##@brief Create a new editorial model
|
# @brief Create a new editorial model
|
||||||
# @param name MlString|str|dict : the editorial model name
|
# @param name MlString|str|dict : the editorial model name
|
||||||
# @param description MlString|str|dict : the editorial model description
|
# @param description MlString|str|dict : the editorial model description
|
||||||
def __init__(self, name, description = None):
|
def __init__(self, name, description=None, display_name=None, help_text=None):
|
||||||
self.name = MlString(name)
|
self.name = MlString(name)
|
||||||
self.description = MlString(description)
|
self.description = MlString(description)
|
||||||
##@brief Stores all groups indexed by id
|
# @brief Stores all groups indexed by id
|
||||||
self.__groups = dict()
|
self.__groups = dict()
|
||||||
##@brief Stores all classes indexed by id
|
# @brief Stores all classes indexed by id
|
||||||
self.__classes = dict()
|
self.__classes = dict()
|
||||||
## @brief Stores all activated groups indexed by id
|
# @brief Stores all activated groups indexed by id
|
||||||
self.__active_groups = dict()
|
self.__active_groups = dict()
|
||||||
## @brief Stores all activated classes indexed by id
|
# @brief Stores all activated classes indexed by id
|
||||||
self.__active_classes = dict()
|
self.__active_classes = dict()
|
||||||
self.__set_actives()
|
self.__set_actives()
|
||||||
|
if display_name is None:
|
||||||
|
display_name = name
|
||||||
|
if help_text is None:
|
||||||
|
help_text = description
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
##@brief EmClass uids accessor
|
# @brief EmClass uids accessor
|
||||||
#@return a dict of emclasses
|
#@return a dict of emclasses
|
||||||
def all_classes(self, uid=None):
|
def all_classes(self, uid=None):
|
||||||
if uid is None:
|
if uid is None:
|
||||||
|
|
@ -54,13 +60,12 @@ class EditorialModel(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||||
|
|
||||||
##@brief active EmClass uids accessor
|
# @brief active EmClass uids accessor
|
||||||
#@return a list of class uids
|
#@return a list of class uids
|
||||||
def active_classes_uids(self):
|
def active_classes_uids(self):
|
||||||
return list(self.__active_classes.keys())
|
return list(self.__active_classes.keys())
|
||||||
|
|
||||||
|
# @brief EmGroups accessor
|
||||||
##@brief EmGroups accessor
|
|
||||||
#@return a dict of groups
|
#@return a dict of groups
|
||||||
def all_groups(self, uid=None):
|
def all_groups(self, uid=None):
|
||||||
if uid is None:
|
if uid is None:
|
||||||
|
|
@ -71,7 +76,7 @@ class EditorialModel(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||||
|
|
||||||
##@brief EmGroups accessor
|
# @brief EmGroups accessor
|
||||||
#@return a dict of groups
|
#@return a dict of groups
|
||||||
def all_groups_ref(self, uid=None):
|
def all_groups_ref(self, uid=None):
|
||||||
if uid is None:
|
if uid is None:
|
||||||
|
|
@ -82,12 +87,12 @@ class EditorialModel(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||||
|
|
||||||
##@brief active EmClass uids accessor
|
# @brief active EmClass uids accessor
|
||||||
#@return a list of class uids
|
#@return a list of class uids
|
||||||
def active_groups_uids(self):
|
def active_groups_uids(self):
|
||||||
return list(self.__active_groups.keys())
|
return list(self.__active_groups.keys())
|
||||||
|
|
||||||
##@brief EmClass accessor
|
# @brief EmClass accessor
|
||||||
#@param uid None | str : give this argument to get a specific EmClass
|
#@param uid None | str : give this argument to get a specific EmClass
|
||||||
#@return if uid is given returns an EmClass else returns an EmClass
|
#@return if uid is given returns an EmClass else returns an EmClass
|
||||||
# iterator
|
# iterator
|
||||||
|
|
@ -100,7 +105,7 @@ class EditorialModel(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmClass not found : '%s'" % uid)
|
raise EditorialModelException("EmClass not found : '%s'" % uid)
|
||||||
|
|
||||||
##@brief EmClass child list accessor
|
# @brief EmClass child list accessor
|
||||||
#@param uid str : the EmClass uid
|
#@param uid str : the EmClass uid
|
||||||
#@return a set of EmClass
|
#@return a set of EmClass
|
||||||
def get_class_childs(self, uid):
|
def get_class_childs(self, uid):
|
||||||
|
|
@ -111,8 +116,7 @@ class EditorialModel(object):
|
||||||
res.append(cls)
|
res.append(cls)
|
||||||
return set(res)
|
return set(res)
|
||||||
|
|
||||||
|
# @brief EmGroup getter
|
||||||
##@brief EmGroup getter
|
|
||||||
# @param uid None | str : give this argument to get a specific EmGroup
|
# @param uid None | str : give this argument to get a specific EmGroup
|
||||||
# @return if uid is given returns an EmGroup else returns an EmGroup iterator
|
# @return if uid is given returns an EmGroup else returns an EmGroup iterator
|
||||||
def groups(self, uid=None):
|
def groups(self, uid=None):
|
||||||
|
|
@ -122,12 +126,12 @@ class EditorialModel(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||||
|
|
||||||
##@brief Private getter for __groups or __classes
|
# @brief Private getter for __groups or __classes
|
||||||
# @see classes() groups()
|
# @see classes() groups()
|
||||||
def __elt_getter(self, elts, uid):
|
def __elt_getter(self, elts, uid):
|
||||||
return list(elts.values()) if uid is None else elts[uid]
|
return list(elts.values()) if uid is None else elts[uid]
|
||||||
|
|
||||||
##@brief Update the EditorialModel.__active_groups and
|
# @brief Update the EditorialModel.__active_groups and
|
||||||
# EditorialModel.__active_classes attibutes
|
# EditorialModel.__active_classes attibutes
|
||||||
def __set_actives(self):
|
def __set_actives(self):
|
||||||
if Settings.editorialmodel.editormode:
|
if Settings.editorialmodel.editormode:
|
||||||
|
|
@ -154,7 +158,7 @@ class EditorialModel(object):
|
||||||
for clsname, acls in self.__active_classes.items():
|
for clsname, acls in self.__active_classes.items():
|
||||||
acls._set_active_fields(self.__active_groups)
|
acls._set_active_fields(self.__active_groups)
|
||||||
|
|
||||||
##@brief EmField getter
|
# @brief EmField getter
|
||||||
# @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
|
# @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
|
||||||
# @return Fals or an EmField instance
|
# @return Fals or an EmField instance
|
||||||
#
|
#
|
||||||
|
|
@ -175,7 +179,7 @@ class EditorialModel(object):
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
##@brief Add a class to the editorial model
|
# @brief Add a class to the editorial model
|
||||||
# @param emclass EmClass : the EmClass instance to add
|
# @param emclass EmClass : the EmClass instance to add
|
||||||
# @return emclass
|
# @return emclass
|
||||||
def add_class(self, emclass):
|
def add_class(self, emclass):
|
||||||
|
|
@ -187,7 +191,7 @@ class EditorialModel(object):
|
||||||
self.__classes[emclass.uid] = emclass
|
self.__classes[emclass.uid] = emclass
|
||||||
return emclass
|
return emclass
|
||||||
|
|
||||||
##@brief Add a group to the editorial model
|
# @brief Add a group to the editorial model
|
||||||
# @param emgroup EmGroup : the EmGroup instance to add
|
# @param emgroup EmGroup : the EmGroup instance to add
|
||||||
# @return emgroup
|
# @return emgroup
|
||||||
def add_group(self, emgroup):
|
def add_group(self, emgroup):
|
||||||
|
|
@ -199,7 +203,7 @@ class EditorialModel(object):
|
||||||
self.__groups[emgroup.uid] = emgroup
|
self.__groups[emgroup.uid] = emgroup
|
||||||
return emgroup
|
return emgroup
|
||||||
|
|
||||||
##@brief Add a new EmClass to the editorial model
|
# @brief Add a new EmClass to the editorial model
|
||||||
#@param uid str : EmClass uid
|
#@param uid str : EmClass uid
|
||||||
#@param **kwargs : EmClass constructor options (
|
#@param **kwargs : EmClass constructor options (
|
||||||
# see @ref lodel.editorial_model.component.EmClass.__init__() )
|
# see @ref lodel.editorial_model.component.EmClass.__init__() )
|
||||||
|
|
@ -207,7 +211,7 @@ class EditorialModel(object):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
return self.add_class(EmClass(uid, **kwargs))
|
return self.add_class(EmClass(uid, **kwargs))
|
||||||
|
|
||||||
##@brief Add a new EmGroup to the editorial model
|
# @brief Add a new EmGroup to the editorial model
|
||||||
#@param uid str : EmGroup uid
|
#@param uid str : EmGroup uid
|
||||||
#@param *kwargs : EmGroup constructor keywords arguments (
|
#@param *kwargs : EmGroup constructor keywords arguments (
|
||||||
# see @ref lodel.editorial_model.component.EmGroup.__init__() )
|
# see @ref lodel.editorial_model.component.EmGroup.__init__() )
|
||||||
|
|
@ -215,7 +219,7 @@ class EditorialModel(object):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
return self.add_group(EmGroup(uid, **kwargs))
|
return self.add_group(EmGroup(uid, **kwargs))
|
||||||
|
|
||||||
##@brief Save a model
|
# @brief Save a model
|
||||||
# @param translator module : The translator module to use
|
# @param translator module : The translator module to use
|
||||||
# @param **translator_args
|
# @param **translator_args
|
||||||
def save(self, translator, **translator_kwargs):
|
def save(self, translator, **translator_kwargs):
|
||||||
|
|
@ -224,13 +228,14 @@ class EditorialModel(object):
|
||||||
translator = self.translator_from_name(translator)
|
translator = self.translator_from_name(translator)
|
||||||
return translator.save(self, **translator_kwargs)
|
return translator.save(self, **translator_kwargs)
|
||||||
|
|
||||||
##@brief Raise an error if lodel is not in EM edition mode
|
# @brief Raise an error if lodel is not in EM edition mode
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def raise_if_ro():
|
def raise_if_ro():
|
||||||
if not Settings.editorialmodel.editormode:
|
if not Settings.editorialmodel.editormode:
|
||||||
raise EditorialModelError("Lodel in not in EM editor mode. The EM is in read only state")
|
raise EditorialModelError(
|
||||||
|
"Lodel in not in EM editor mode. The EM is in read only state")
|
||||||
|
|
||||||
##@brief Load a model
|
# @brief Load a model
|
||||||
# @param translator module : The translator module to use
|
# @param translator module : The translator module to use
|
||||||
# @param **translator_args
|
# @param **translator_args
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -241,7 +246,7 @@ class EditorialModel(object):
|
||||||
res.__set_actives()
|
res.__set_actives()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Return a translator module given a translator name
|
# @brief Return a translator module given a translator name
|
||||||
# @param translator_name str : The translator name
|
# @param translator_name str : The translator name
|
||||||
# @return the translator python module
|
# @return the translator python module
|
||||||
# @throw NameError if the translator does not exists
|
# @throw NameError if the translator does not exists
|
||||||
|
|
@ -254,7 +259,7 @@ class EditorialModel(object):
|
||||||
raise NameError("No translator named %s")
|
raise NameError("No translator named %s")
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
##@brief Lodel hash
|
# @brief Lodel hash
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
payload = "%s%s" % (
|
payload = "%s%s" % (
|
||||||
self.name,
|
self.name,
|
||||||
|
|
@ -270,4 +275,3 @@ class EditorialModel(object):
|
||||||
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
||||||
byteorder='big'
|
byteorder='big'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,28 @@ import warnings
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
|
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': [
|
||||||
'LodelFatalError', 'DataNoneValid', 'FieldValidationError'],
|
'LodelException',
|
||||||
'lodel.leapi.datahandlers.exceptions': ['LodelDataHandlerConsistencyException', 'LodelDataHandlerException'],
|
'LodelExceptions',
|
||||||
'lodel.logger': 'logger'})
|
'LodelFatalError',
|
||||||
|
'DataNoneValid',
|
||||||
|
'FieldValidationError'
|
||||||
|
],
|
||||||
|
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||||
|
'lodel.leapi.datahandlers.exceptions': [
|
||||||
|
'LodelDataHandlerConsistencyException',
|
||||||
|
'LodelDataHandlerException'
|
||||||
|
],
|
||||||
|
'lodel.validator.validator': [
|
||||||
|
'ValidationError'
|
||||||
|
],
|
||||||
|
'lodel.logger': 'logger',
|
||||||
|
'lodel.utils.mlstring': ['MlString']})
|
||||||
|
|
||||||
|
|
||||||
## @brief Base class for all data handlers
|
## @brief Base class for all data handlers
|
||||||
# @ingroup lodel2_datahandlers
|
# @ingroup lodel2_datahandlers
|
||||||
class DataHandler(object):
|
class DataHandler(MlNamedObject):
|
||||||
base_type = "type"
|
base_type = "type"
|
||||||
_HANDLERS_MODULES = ('datas_base', 'datas', 'references')
|
_HANDLERS_MODULES = ('datas_base', 'datas', 'references')
|
||||||
## @brief Stores the DataHandler childs classes indexed by name
|
## @brief Stores the DataHandler childs classes indexed by name
|
||||||
|
|
@ -30,16 +43,20 @@ class DataHandler(object):
|
||||||
__custom_handlers = dict()
|
__custom_handlers = dict()
|
||||||
|
|
||||||
help_text = 'Generic Field Data Handler'
|
help_text = 'Generic Field Data Handler'
|
||||||
|
display_name = "Generic Field"
|
||||||
|
options_spec = dict()
|
||||||
|
options_values = dict()
|
||||||
|
|
||||||
## @brief List fields that will be exposed to the construct_data_method
|
## @brief List fields that will be exposed to the construct_data_method
|
||||||
_construct_datas_deps = []
|
_construct_datas_deps = []
|
||||||
|
|
||||||
directly_editable = True
|
directly_editable = True
|
||||||
|
|
||||||
## @brief constructor
|
## @brief constructor
|
||||||
|
#
|
||||||
# @param internal False | str : define whether or not a field is internal
|
# @param internal False | str : define whether or not a field is internal
|
||||||
# @param immutable bool : indicates if the fieldtype has to be defined in child classes of LeObject or if it is
|
# @param immutable bool : indicates if the fieldtype has to be defined in child classes of
|
||||||
# designed globally and immutable
|
# LeObject or if it is designed globally and immutable
|
||||||
# @param **args
|
|
||||||
# @throw NotImplementedError if it is instanciated directly
|
# @throw NotImplementedError if it is instanciated directly
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
if self.__class__ == DataHandler:
|
if self.__class__ == DataHandler:
|
||||||
|
|
@ -54,9 +71,30 @@ class DataHandler(object):
|
||||||
self.default, error = self.check_data_value(kwargs['default'])
|
self.default, error = self.check_data_value(kwargs['default'])
|
||||||
if error:
|
if error:
|
||||||
raise error
|
raise error
|
||||||
del(kwargs['default'])
|
del kwargs['default']
|
||||||
for argname, argval in kwargs.items():
|
for argname, argval in kwargs.items():
|
||||||
setattr(self, argname, argval)
|
setattr(self, argname, argval)
|
||||||
|
self.check_options()
|
||||||
|
|
||||||
|
display_name = kwargs.get('display_name',MlString(self.display_name))
|
||||||
|
help_text = kwargs.get('help_text', MlString(self.help_text))
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
|
## @brief Sets properly casted and checked options for the datahandler
|
||||||
|
# @raises LodelDataHandlerNotAllowedOptionException when a passed option is not in the option
|
||||||
|
# specifications of the datahandler
|
||||||
|
def check_options(self):
|
||||||
|
for option_name, option_datas in self.options_spec.items():
|
||||||
|
if option_name in self.options_values:
|
||||||
|
# There is a configured option, we check its value
|
||||||
|
try:
|
||||||
|
self.options_values[option_name] = option_datas[1].check_value(
|
||||||
|
self.options_values[option_name])
|
||||||
|
except ValueError:
|
||||||
|
pass # TODO Deal with the case where the value used for an option is invalid
|
||||||
|
else:
|
||||||
|
# This option was not configured, we get the default value from the specs
|
||||||
|
self.options_values[option_name] = option_datas[0]
|
||||||
|
|
||||||
## Fieldtype name
|
## Fieldtype name
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -79,7 +117,7 @@ class DataHandler(object):
|
||||||
def is_internal(self):
|
def is_internal(self):
|
||||||
return self.internal is not False
|
return self.internal is not False
|
||||||
|
|
||||||
##brief check if a value can be nullable
|
## @brief check if a value can be nullable
|
||||||
# @param value *
|
# @param value *
|
||||||
# @throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable
|
# @throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable
|
||||||
# @return value (if not None)
|
# @return value (if not None)
|
||||||
|
|
@ -204,7 +242,8 @@ class DataHandler(object):
|
||||||
## @brief given a field type name, returns the associated python class
|
## @brief given a field type name, returns the associated python class
|
||||||
# @param fieldtype_name str : A field type name (not case sensitive)
|
# @param fieldtype_name str : A field type name (not case sensitive)
|
||||||
# @return DataField child class
|
# @return DataField child class
|
||||||
# @note To access custom data handlers it can be cool to prefix the handler name by plugin name for example ? (to ensure name unicity)
|
# @note To access custom data handlers it can be cool to prefix the handler name by plugin
|
||||||
|
# name for example ? (to ensure name unicity)
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_name(cls, name):
|
def from_name(cls, name):
|
||||||
cls.load_base_handlers()
|
cls.load_base_handlers()
|
||||||
|
|
@ -214,6 +253,23 @@ class DataHandler(object):
|
||||||
raise NameError("No data handlers named '%s'" % (name,))
|
raise NameError("No data handlers named '%s'" % (name,))
|
||||||
return all_handlers[name]
|
return all_handlers[name]
|
||||||
|
|
||||||
|
# @brief List all datahandlers
|
||||||
|
# @return a dict with, display_name for keys, and a dict for value
|
||||||
|
@classmethod
|
||||||
|
def list_data_handlers(cls):
|
||||||
|
cls.load_base_handlers()
|
||||||
|
all_handlers = dict(cls._base_handlers, **cls.__custom_handlers)
|
||||||
|
list_dh = dict()
|
||||||
|
for hdl in all_handlers:
|
||||||
|
list_dh[hdl.display_name] = {'help_text' : hdl.help_text,
|
||||||
|
'nullable' : hdl.nullable, \
|
||||||
|
'internal' : hdl.internal,
|
||||||
|
'immutable' : hdl.immutable, \
|
||||||
|
'primary_key' : hdl.primary_key, \
|
||||||
|
'options' : self.options_spec}
|
||||||
|
|
||||||
|
return list_dh
|
||||||
|
|
||||||
## @brief Return the module name to import in order to use the datahandler
|
## @brief Return the module name to import in order to use the datahandler
|
||||||
# @param data_handler_name str : Data handler name
|
# @param data_handler_name str : Data handler name
|
||||||
# @return a str
|
# @return a str
|
||||||
|
|
@ -238,14 +294,13 @@ class DataHandler(object):
|
||||||
class DataField(DataHandler):
|
class DataField(DataHandler):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
## @brief Abstract class for all references
|
## @brief Abstract class for all references
|
||||||
# @ingroup lodel2_datahandlers
|
# @ingroup lodel2_datahandlers
|
||||||
#
|
#
|
||||||
# References are fields that stores a reference to another
|
# References are fields that stores a reference to another
|
||||||
# editorial object
|
# editorial object
|
||||||
#@todo Construct data implementation : transform the data into a LeObject
|
# @todo Construct data implementation : transform the data into a LeObject instance
|
||||||
#instance
|
|
||||||
|
|
||||||
class Reference(DataHandler):
|
class Reference(DataHandler):
|
||||||
base_type = "ref"
|
base_type = "ref"
|
||||||
|
|
||||||
|
|
@ -256,12 +311,15 @@ class Reference(DataHandler):
|
||||||
# @param **kwargs : other arguments
|
# @param **kwargs : other arguments
|
||||||
def __init__(self, allowed_classes=None, back_reference=None, internal=False, **kwargs):
|
def __init__(self, allowed_classes=None, back_reference=None, internal=False, **kwargs):
|
||||||
self.__allowed_classes = set() if allowed_classes is None else set(allowed_classes)
|
self.__allowed_classes = set() if allowed_classes is None else set(allowed_classes)
|
||||||
self.allowed_classes = list() if allowed_classes is None else allowed_classes # For now usefull to jinja 2
|
# For now usefull to jinja 2
|
||||||
|
self.allowed_classes = list() if allowed_classes is None else allowed_classes
|
||||||
if back_reference is not None:
|
if back_reference is not None:
|
||||||
if len(back_reference) != 2:
|
if len(back_reference) != 2:
|
||||||
raise ValueError("A tuple (classname, fieldname) expected but got '%s'" % back_reference)
|
raise ValueError("A tuple (classname, fieldname) expected but got '%s'" % back_reference)
|
||||||
#if not issubclass(lodel.leapi.leobject.LeObject, back_reference[0]) or not isinstance(back_reference[1], str):
|
# if not issubclass(lodel.leapi.leobject.LeObject, back_reference[0])
|
||||||
# raise TypeError("Back reference was expected to be a tuple(<class LeObject>, str) but got : (%s, %s)" % (back_reference[0], back_reference[1]))
|
# or not isinstance(back_reference[1], str):
|
||||||
|
# raise TypeError("Back reference was expected to be a tuple(<class LeObject>, str)
|
||||||
|
# but got : (%s, %s)" % (back_reference[0], back_reference[1]))
|
||||||
self.__back_reference = back_reference
|
self.__back_reference = back_reference
|
||||||
super().__init__(internal=internal, **kwargs)
|
super().__init__(internal=internal, **kwargs)
|
||||||
|
|
||||||
|
|
@ -384,7 +442,7 @@ referenced object with uid %s" % value)
|
||||||
# @note for the moment split on ',' chars
|
# @note for the moment split on ',' chars
|
||||||
class MultipleRef(Reference):
|
class MultipleRef(Reference):
|
||||||
|
|
||||||
##
|
## @brief Constructor
|
||||||
# @param max_item int | None : indicate the maximum number of item referenced by this field, None mean no limit
|
# @param max_item int | None : indicate the maximum number of item referenced by this field, None mean no limit
|
||||||
def __init__(self, max_item=None, **kwargs):
|
def __init__(self, max_item=None, **kwargs):
|
||||||
self.max_item = max_item
|
self.max_item = max_item
|
||||||
|
|
@ -442,6 +500,7 @@ class MultipleRef(Reference):
|
||||||
raise LodelDataHandlerConsistencyException("Unable to find \
|
raise LodelDataHandlerConsistencyException("Unable to find \
|
||||||
some referenced objects. Following uids were not found : %s" % ','.join(left))
|
some referenced objects. Following uids were not found : %s" % ','.join(left))
|
||||||
|
|
||||||
|
|
||||||
## @brief Class designed to handle datas access will fieldtypes are constructing datas
|
## @brief Class designed to handle datas access will fieldtypes are constructing datas
|
||||||
# @ingroup lodel2_datahandlers
|
# @ingroup lodel2_datahandlers
|
||||||
#
|
#
|
||||||
|
|
@ -457,15 +516,15 @@ class DatasConstructor(object):
|
||||||
# @param datas dict : dict with field name as key and field values as value
|
# @param datas dict : dict with field name as key and field values as value
|
||||||
# @param fields_handler dict : dict with field name as key and data handler instance as value
|
# @param fields_handler dict : dict with field name as key and data handler instance as value
|
||||||
def __init__(self, leobject, datas, fields_handler):
|
def __init__(self, leobject, datas, fields_handler):
|
||||||
## Stores concerned class
|
# Stores concerned class
|
||||||
self._leobject = leobject
|
self._leobject = leobject
|
||||||
## Stores datas and constructed datas
|
# Stores datas and constructed datas
|
||||||
self._datas = copy.copy(datas)
|
self._datas = copy.copy(datas)
|
||||||
## Stores fieldtypes
|
# Stores fieldtypes
|
||||||
self._fields_handler = fields_handler
|
self._fields_handler = fields_handler
|
||||||
## Stores list of fieldname for constructed datas
|
# Stores list of fieldname for constructed datas
|
||||||
self._constructed = []
|
self._constructed = []
|
||||||
## Stores construct calls list
|
# Stores construct calls list
|
||||||
self._construct_calls = []
|
self._construct_calls = []
|
||||||
|
|
||||||
## @brief Implements the dict.keys() method on instance
|
## @brief Implements the dict.keys() method on instance
|
||||||
|
|
@ -488,3 +547,30 @@ class DatasConstructor(object):
|
||||||
self._datas[fname] = value
|
self._datas[fname] = value
|
||||||
warnings.warn("Setting value of an DatasConstructor instance")
|
warnings.warn("Setting value of an DatasConstructor instance")
|
||||||
|
|
||||||
|
|
||||||
|
## @brief Class designed to handle an option of a DataHandler
|
||||||
|
class DatahandlerOption(MlNamedObject):
|
||||||
|
|
||||||
|
## @brief instanciates a new Datahandler option object
|
||||||
|
#
|
||||||
|
# @param id str
|
||||||
|
# @param display_name MlString
|
||||||
|
# @param help_text MlString
|
||||||
|
# @param validator function
|
||||||
|
def __init__(self, id, display_name, help_text, validator):
|
||||||
|
self.__id = id
|
||||||
|
self.__validator = validator
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
## @brief checks a value corresponding to this option is valid
|
||||||
|
# @param value
|
||||||
|
# @return casted value
|
||||||
|
def check_value(self, value):
|
||||||
|
try:
|
||||||
|
return self.__validator(value)
|
||||||
|
except ValidationError:
|
||||||
|
raise ValueError()
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ be internal")
|
||||||
cls = emcomponent.__class__
|
cls = emcomponent.__class__
|
||||||
return cls.__name__
|
return cls.__name__
|
||||||
|
|
||||||
|
|
||||||
##@brief Data field designed to handle concatenated fields
|
##@brief Data field designed to handle concatenated fields
|
||||||
class Concat(FormatString):
|
class Concat(FormatString):
|
||||||
help = 'Automatic strings concatenation'
|
help = 'Automatic strings concatenation'
|
||||||
|
|
@ -118,9 +119,9 @@ class Concat(FormatString):
|
||||||
# @param **kwargs
|
# @param **kwargs
|
||||||
def __init__(self, field_list, separator=' ', **kwargs):
|
def __init__(self, field_list, separator=' ', **kwargs):
|
||||||
format_string = separator.join(['%s' for _ in field_list])
|
format_string = separator.join(['%s' for _ in field_list])
|
||||||
super().__init__(
|
super().__init__(format_string=format_string,
|
||||||
format_string = format_string, field_list = field_list, **kwargs)
|
field_list=field_list,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Password(Varchar):
|
class Password(Varchar):
|
||||||
|
|
@ -129,7 +130,6 @@ class Password(Varchar):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VarcharList(Varchar):
|
class VarcharList(Varchar):
|
||||||
help = 'DataHandler designed to make a list out of a string.'
|
help = 'DataHandler designed to make a list out of a string.'
|
||||||
base_type = 'varchar'
|
base_type = 'varchar'
|
||||||
|
|
@ -140,7 +140,6 @@ class VarcharList(Varchar):
|
||||||
self.delimiter = str(delimiter)
|
self.delimiter = str(delimiter)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def construct_data(self, emcomponent, fname, datas, cur_value):
|
def construct_data(self, emcomponent, fname, datas, cur_value):
|
||||||
result = cur_value.split(self.delimiter)
|
result = cur_value.split(self.delimiter)
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class Boolean(DataField):
|
||||||
raise FieldValidationError("The value '%s' is not, and will never, be a boolean" % value)
|
raise FieldValidationError("The value '%s' is not, and will never, be a boolean" % value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## @brief Data field designed to handle integer values
|
## @brief Data field designed to handle integer values
|
||||||
class Integer(DataField):
|
class Integer(DataField):
|
||||||
|
|
||||||
|
|
@ -61,6 +62,7 @@ class Integer(DataField):
|
||||||
raise FieldValidationError("The value '%s' is not, and will never, be an integer" % value)
|
raise FieldValidationError("The value '%s' is not, and will never, be an integer" % value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## @brief Data field designed to handle string
|
## @brief Data field designed to handle string
|
||||||
class Varchar(DataField):
|
class Varchar(DataField):
|
||||||
|
|
||||||
|
|
@ -95,6 +97,7 @@ class Varchar(DataField):
|
||||||
raise FieldValidationError("The value '%s' is longer than the maximum length of this field (%s)" % (value, self.max_length))
|
raise FieldValidationError("The value '%s' is longer than the maximum length of this field (%s)" % (value, self.max_length))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## @brief Data field designed to handle date & time
|
## @brief Data field designed to handle date & time
|
||||||
class DateTime(DataField):
|
class DateTime(DataField):
|
||||||
|
|
||||||
|
|
@ -131,6 +134,7 @@ class DateTime(DataField):
|
||||||
return datetime.datetime.now()
|
return datetime.datetime.now()
|
||||||
return cur_value
|
return cur_value
|
||||||
|
|
||||||
|
|
||||||
## @brief Data field designed to handle long string
|
## @brief Data field designed to handle long string
|
||||||
class Text(DataField):
|
class Text(DataField):
|
||||||
help = 'A text field (big string)'
|
help = 'A text field (big string)'
|
||||||
|
|
@ -149,6 +153,7 @@ class Text(DataField):
|
||||||
raise FieldValidationError("The content passed to this Text field is not a convertible to a string")
|
raise FieldValidationError("The content passed to this Text field is not a convertible to a string")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## @brief Data field designed to handle Files
|
## @brief Data field designed to handle Files
|
||||||
class File(DataField):
|
class File(DataField):
|
||||||
|
|
||||||
|
|
@ -164,4 +169,3 @@ class File(DataField):
|
||||||
# @todo Add here a check for the validity of the given value (should have a correct path syntax)
|
# @todo Add here a check for the validity of the given value (should have a correct path syntax)
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
return super()._check_data_value(value)
|
return super()._check_data_value(value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
def LodelDataHandlerException(Exception):
|
class LodelDataHandlerException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def LodelDataHandlerConsistencyException(LodelDataHandlerException):
|
|
||||||
|
class LodelDataHandlerConsistencyException(LodelDataHandlerException):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ LodelContext.expose_modules(globals(), {
|
||||||
'SingleRef'],
|
'SingleRef'],
|
||||||
'lodel.logger': 'logger',
|
'lodel.logger': 'logger',
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||||
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
'LodelFatalError', 'DataNoneValid',
|
||||||
|
'FieldValidationError']})
|
||||||
|
|
||||||
|
|
||||||
class Link(SingleRef):
|
class Link(SingleRef):
|
||||||
pass
|
pass
|
||||||
|
|
@ -39,7 +41,7 @@ class List(MultipleRef):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FieldValidationError("Given iterable is not castable in \
|
raise FieldValidationError("Given iterable is not castable in \
|
||||||
a list : %s" % e)
|
a list : %s" % e)
|
||||||
return value
|
|
||||||
|
|
||||||
## @brief Child class of MultipleRef where references are represented in the form of a python set
|
## @brief Child class of MultipleRef where references are represented in the form of a python set
|
||||||
class Set(MultipleRef):
|
class Set(MultipleRef):
|
||||||
|
|
@ -67,6 +69,7 @@ class Set(MultipleRef):
|
||||||
raise FieldValidationError("Given iterable is not castable in \
|
raise FieldValidationError("Given iterable is not castable in \
|
||||||
a set : %s" % e)
|
a set : %s" % e)
|
||||||
|
|
||||||
|
|
||||||
## @brief Child class of MultipleRef where references are represented in the form of a python dict
|
## @brief Child class of MultipleRef where references are represented in the form of a python dict
|
||||||
class Map(MultipleRef):
|
class Map(MultipleRef):
|
||||||
|
|
||||||
|
|
@ -91,10 +94,12 @@ class Map(MultipleRef):
|
||||||
raise FieldValidationError("Values for dict fields should be dict")
|
raise FieldValidationError("Values for dict fields should be dict")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## @brief This Reference class is designed to handler hierarchy with some constraint
|
## @brief This Reference class is designed to handler hierarchy with some constraint
|
||||||
class Hierarch(MultipleRef):
|
class Hierarch(MultipleRef):
|
||||||
|
|
||||||
directly_editable = False
|
directly_editable = False
|
||||||
|
|
||||||
## @brief Instanciate a data handler handling hierarchical relation with constraints
|
## @brief Instanciate a data handler handling hierarchical relation with constraints
|
||||||
# @param back_reference tuple : Here it is mandatory to have a back ref (like a parent field)
|
# @param back_reference tuple : Here it is mandatory to have a back ref (like a parent field)
|
||||||
# @param max_depth int | None : limit of depth
|
# @param max_depth int | None : limit of depth
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,20 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin': ['Plugin', 'DatasourcePlugin'],
|
'lodel.plugin': ['Plugin', 'DatasourcePlugin'],
|
||||||
'lodel.leapi.datahandlers.base_classes': ['DatasConstructor', 'Reference']})
|
'lodel.leapi.datahandlers.base_classes': ['DatasConstructor', 'Reference']})
|
||||||
|
|
||||||
##@brief Stores the name of the field present in each LeObject that indicates
|
# @brief Stores the name of the field present in each LeObject that indicates
|
||||||
# the name of LeObject subclass represented by this object
|
# the name of LeObject subclass represented by this object
|
||||||
CLASS_ID_FIELDNAME = "classname"
|
CLASS_ID_FIELDNAME = "classname"
|
||||||
|
|
||||||
##@brief Wrapper class for LeObject getter & setter
|
# @brief Wrapper class for LeObject getter & setter
|
||||||
#
|
#
|
||||||
# This class intend to provide easy & friendly access to LeObject fields values
|
# This class intend to provide easy & friendly access to LeObject fields values
|
||||||
# without name collision problems
|
# without name collision problems
|
||||||
# @note Wrapped methods are : LeObject.data() & LeObject.set_data()
|
# @note Wrapped methods are : LeObject.data() & LeObject.set_data()
|
||||||
|
|
||||||
|
|
||||||
class LeObjectValues(object):
|
class LeObjectValues(object):
|
||||||
|
|
||||||
##@brief Construct a new LeObjectValues
|
# @brief Construct a new LeObjectValues
|
||||||
# @param fieldnames_callback method
|
# @param fieldnames_callback method
|
||||||
# @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
|
# @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
|
||||||
# @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
|
# @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
|
||||||
|
|
@ -41,14 +43,14 @@ class LeObjectValues(object):
|
||||||
self._setter = set_callback
|
self._setter = set_callback
|
||||||
self._getter = get_callback
|
self._getter = get_callback
|
||||||
|
|
||||||
##@brief Provide read access to datas values
|
# @brief Provide read access to datas values
|
||||||
# @note Read access should be provided for all fields
|
# @note Read access should be provided for all fields
|
||||||
# @param fname str : Field name
|
# @param fname str : Field name
|
||||||
def __getattribute__(self, fname):
|
def __getattribute__(self, fname):
|
||||||
getter = super().__getattribute__('_getter')
|
getter = super().__getattribute__('_getter')
|
||||||
return getter(fname)
|
return getter(fname)
|
||||||
|
|
||||||
##@brief Provide write access to datas values
|
# @brief Provide write access to datas values
|
||||||
# @note Write acces shouldn't be provided for internal or immutable fields
|
# @note Write acces shouldn't be provided for internal or immutable fields
|
||||||
# @param fname str : Field name
|
# @param fname str : Field name
|
||||||
# @param fval * : the field value
|
# @param fval * : the field value
|
||||||
|
|
@ -59,29 +61,29 @@ class LeObjectValues(object):
|
||||||
|
|
||||||
class LeObject(object):
|
class LeObject(object):
|
||||||
|
|
||||||
##@brief boolean that tells if an object is abtract or not
|
# @brief boolean that tells if an object is abtract or not
|
||||||
_abstract = None
|
_abstract = None
|
||||||
##@brief A dict that stores DataHandler instances indexed by field name
|
# @brief A dict that stores DataHandler instances indexed by field name
|
||||||
_fields = None
|
_fields = None
|
||||||
##@brief A tuple of fieldname (or a uniq fieldname) representing uid
|
# @brief A tuple of fieldname (or a uniq fieldname) representing uid
|
||||||
_uid = None
|
_uid = None
|
||||||
##@brief Read only datasource ( see @ref lodel2_datasources )
|
# @brief Read only datasource ( see @ref lodel2_datasources )
|
||||||
_ro_datasource = None
|
_ro_datasource = None
|
||||||
##@brief Read & write datasource ( see @ref lodel2_datasources )
|
# @brief Read & write datasource ( see @ref lodel2_datasources )
|
||||||
_rw_datasource = None
|
_rw_datasource = None
|
||||||
##@brief Store the list of child classes
|
# @brief Store the list of child classes
|
||||||
_child_classes = None
|
_child_classes = None
|
||||||
##@brief Name of the datasource plugin
|
# @brief Name of the datasource plugin
|
||||||
_datasource_name = None
|
_datasource_name = None
|
||||||
|
|
||||||
def __new__(cls, **kwargs):
|
def __new__(cls, **kwargs):
|
||||||
|
|
||||||
self = object.__new__(cls)
|
self = object.__new__(cls)
|
||||||
##@brief A dict that stores fieldvalues indexed by fieldname
|
# @brief A dict that stores fieldvalues indexed by fieldname
|
||||||
self.__datas = {fname: None for fname in self._fields}
|
self.__datas = {fname: None for fname in self._fields}
|
||||||
##@brief Store a list of initianilized fields when instanciation not complete else store True
|
# @brief Store a list of initianilized fields when instanciation not complete else store True
|
||||||
self.__initialized = list()
|
self.__initialized = list()
|
||||||
##@brief Datas accessor. Instance of @ref LeObjectValues
|
# @brief Datas accessor. Instance of @ref LeObjectValues
|
||||||
self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
|
self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
|
||||||
for fieldname, fieldval in kwargs.items():
|
for fieldname, fieldval in kwargs.items():
|
||||||
self.__datas[fieldname] = fieldval
|
self.__datas[fieldname] = fieldval
|
||||||
|
|
@ -90,11 +92,12 @@ class LeObject(object):
|
||||||
self.__set_initialized()
|
self.__set_initialized()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
##@brief Construct an object representing an Editorial component
|
# @brief Construct an object representing an Editorial component
|
||||||
# @note Can be considered as EmClass instance
|
# @note Can be considered as EmClass instance
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
if self._abstract:
|
if self._abstract:
|
||||||
raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
|
raise NotImplementedError(
|
||||||
|
"%s is abstract, you cannot instanciate it." % self.__class__.__name__)
|
||||||
|
|
||||||
# Checks that uid is given
|
# Checks that uid is given
|
||||||
for uid_name in self._uid:
|
for uid_name in self._uid:
|
||||||
|
|
@ -127,17 +130,17 @@ class LeObject(object):
|
||||||
# Fields datas handling methods #
|
# Fields datas handling methods #
|
||||||
#-----------------------------------#
|
#-----------------------------------#
|
||||||
|
|
||||||
##@brief Property method True if LeObject is initialized else False
|
# @brief Property method True if LeObject is initialized else False
|
||||||
@property
|
@property
|
||||||
def initialized(self):
|
def initialized(self):
|
||||||
return self.__is_initialized
|
return self.__is_initialized
|
||||||
|
|
||||||
##@return The uid field name
|
# @return The uid field name
|
||||||
@classmethod
|
@classmethod
|
||||||
def uid_fieldname(cls):
|
def uid_fieldname(cls):
|
||||||
return cls._uid
|
return cls._uid
|
||||||
|
|
||||||
##@brief Return a list of fieldnames
|
# @brief Return a list of fieldnames
|
||||||
# @param include_ro bool : if True include read only field names
|
# @param include_ro bool : if True include read only field names
|
||||||
# @return a list of str
|
# @return a list of str
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -151,7 +154,7 @@ class LeObject(object):
|
||||||
def name2objname(cls, name):
|
def name2objname(cls, name):
|
||||||
return name.title()
|
return name.title()
|
||||||
|
|
||||||
##@brief Return the datahandler asssociated with a LeObject field
|
# @brief Return the datahandler asssociated with a LeObject field
|
||||||
# @param fieldname str : The fieldname
|
# @param fieldname str : The fieldname
|
||||||
# @return A data handler instance
|
# @return A data handler instance
|
||||||
#@todo update class of exception raised
|
#@todo update class of exception raised
|
||||||
|
|
@ -161,17 +164,17 @@ class LeObject(object):
|
||||||
raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
|
raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
|
||||||
return cls._fields[fieldname]
|
return cls._fields[fieldname]
|
||||||
|
|
||||||
##@brief Getter for references datahandlers
|
# @brief Getter for references datahandlers
|
||||||
#@param with_backref bool : if true return only references with back_references
|
#@param with_backref bool : if true return only references with back_references
|
||||||
#@return <code>{'fieldname': datahandler, ...}</code>
|
#@return <code>{'fieldname': datahandler, ...}</code>
|
||||||
@classmethod
|
@classmethod
|
||||||
def reference_handlers(cls, with_backref=True):
|
def reference_handlers(cls, with_backref=True):
|
||||||
return {fname: fdh
|
return {fname: fdh
|
||||||
for fname, fdh in cls.fields(True).items()
|
for fname, fdh in cls.fields(True).items()
|
||||||
if fdh.is_reference() and \
|
if fdh.is_reference() and
|
||||||
(not with_backref or fdh.back_reference is not None)}
|
(not with_backref or fdh.back_reference is not None)}
|
||||||
|
|
||||||
##@brief Return a LeObject child class from a name
|
# @brief Return a LeObject child class from a name
|
||||||
# @warning This method has to be called from dynamically generated LeObjects
|
# @warning This method has to be called from dynamically generated LeObjects
|
||||||
# @param leobject_name str : LeObject name
|
# @param leobject_name str : LeObject name
|
||||||
# @return A LeObject child class
|
# @return A LeObject child class
|
||||||
|
|
@ -190,7 +193,7 @@ class LeObject(object):
|
||||||
def is_abstract(cls):
|
def is_abstract(cls):
|
||||||
return cls._abstract
|
return cls._abstract
|
||||||
|
|
||||||
##@brief Field data handler getter
|
# @brief Field data handler getter
|
||||||
#@param fieldname str : The field name
|
#@param fieldname str : The field name
|
||||||
#@return A datahandler instance
|
#@return A datahandler instance
|
||||||
#@throw NameError if the field doesn't exist
|
#@throw NameError if the field doesn't exist
|
||||||
|
|
@ -201,15 +204,17 @@ class LeObject(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise NameError("No field named '%s' in %s" % (fieldname,
|
raise NameError("No field named '%s' in %s" % (fieldname,
|
||||||
cls.__name__))
|
cls.__name__))
|
||||||
##@return A dict with fieldname as key and datahandler as instance
|
# @return A dict with fieldname as key and datahandler as instance
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls, include_ro=False):
|
def fields(cls, include_ro=False):
|
||||||
if include_ro:
|
if include_ro:
|
||||||
return copy.copy(cls._fields)
|
return copy.copy(cls._fields)
|
||||||
else:
|
else:
|
||||||
return {fname:cls._fields[fname] for fname in cls._fields if not cls._fields[fname].is_internal()}
|
return {fname: cls._fields[fname] for fname in cls._fields\
|
||||||
|
if not cls._fields[fname].is_internal()}
|
||||||
|
|
||||||
##@brief Return the list of parents classes
|
# @brief Return the list of parents classes
|
||||||
#
|
#
|
||||||
#@note the first item of the list is the current class, the second is it's
|
#@note the first item of the list is the current class, the second is it's
|
||||||
# parent etc...
|
# parent etc...
|
||||||
|
|
@ -229,14 +234,13 @@ class LeObject(object):
|
||||||
res.append(cur)
|
res.append(cur)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Return a tuple a child classes
|
# @brief Return a tuple a child classes
|
||||||
#@return a tuple of child classes
|
#@return a tuple of child classes
|
||||||
@classmethod
|
@classmethod
|
||||||
def child_classes(cls):
|
def child_classes(cls):
|
||||||
return copy.copy(cls._child_classes)
|
return copy.copy(cls._child_classes)
|
||||||
|
|
||||||
|
# @brief Return the parent class that is the "source" of uid
|
||||||
##@brief Return the parent class that is the "source" of uid
|
|
||||||
#
|
#
|
||||||
# The method goal is to return the parent class that defines UID.
|
# The method goal is to return the parent class that defines UID.
|
||||||
#@return a LeObject child class or false if no UID defined
|
#@return a LeObject child class or false if no UID defined
|
||||||
|
|
@ -255,7 +259,7 @@ class LeObject(object):
|
||||||
prev = pcls
|
prev = pcls
|
||||||
return prev
|
return prev
|
||||||
|
|
||||||
##@brief Initialise both datasources (ro and rw)
|
# @brief Initialise both datasources (ro and rw)
|
||||||
#
|
#
|
||||||
# This method is used once at dyncode load to replace the datasource string
|
# This method is used once at dyncode load to replace the datasource string
|
||||||
# by a datasource instance to avoid doing this operation for each query
|
# by a datasource instance to avoid doing this operation for each query
|
||||||
|
|
@ -287,13 +291,13 @@ class LeObject(object):
|
||||||
log_msg %= (ro_ds, cls.__name__)
|
log_msg %= (ro_ds, cls.__name__)
|
||||||
logger.debug(log_msg)
|
logger.debug(log_msg)
|
||||||
|
|
||||||
##@brief Return the uid of the current LeObject instance
|
# @brief Return the uid of the current LeObject instance
|
||||||
#@return the uid value
|
#@return the uid value
|
||||||
#@warning Broke multiple uid capabilities
|
#@warning Broke multiple uid capabilities
|
||||||
def uid(self):
|
def uid(self):
|
||||||
return self.data(self._uid[0])
|
return self.data(self._uid[0])
|
||||||
|
|
||||||
##@brief Read only access to all datas
|
# @brief Read only access to all datas
|
||||||
# @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
|
# @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
|
||||||
# @param field_name str : field name
|
# @param field_name str : field name
|
||||||
# @return the Value
|
# @return the Value
|
||||||
|
|
@ -303,16 +307,16 @@ class LeObject(object):
|
||||||
if field_name not in self._fields.keys():
|
if field_name not in self._fields.keys():
|
||||||
raise NameError("No such field in %s : %s" % (self.__class__.__name__, field_name))
|
raise NameError("No such field in %s : %s" % (self.__class__.__name__, field_name))
|
||||||
if not self.initialized and field_name not in self.__initialized:
|
if not self.initialized and field_name not in self.__initialized:
|
||||||
raise RuntimeError("The field %s is not initialized yet (and have no value)" % field_name)
|
raise RuntimeError(
|
||||||
|
"The field %s is not initialized yet (and have no value)" % field_name)
|
||||||
return self.__datas[field_name]
|
return self.__datas[field_name]
|
||||||
|
|
||||||
##@brief Read only access to all datas
|
# @brief Read only access to all datas
|
||||||
#@return a dict representing datas of current instance
|
#@return a dict representing datas of current instance
|
||||||
def datas(self, internal=False):
|
def datas(self, internal=False):
|
||||||
return {fname: self.data(fname) for fname in self.fieldnames(internal)}
|
return {fname: self.data(fname) for fname in self.fieldnames(internal)}
|
||||||
|
|
||||||
|
# @brief Datas setter
|
||||||
##@brief Datas setter
|
|
||||||
# @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
|
# @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
|
||||||
# @param fname str : field name
|
# @param fname str : field name
|
||||||
# @param fval * : field value
|
# @param fval * : field value
|
||||||
|
|
@ -349,7 +353,7 @@ class LeObject(object):
|
||||||
else:
|
else:
|
||||||
self.__datas[fname] = val
|
self.__datas[fname] = val
|
||||||
|
|
||||||
##@brief Update the __initialized attribute according to LeObject internal state
|
# @brief Update the __initialized attribute according to LeObject internal state
|
||||||
#
|
#
|
||||||
# Check the list of initialized fields and set __initialized to True if all fields initialized
|
# Check the list of initialized fields and set __initialized to True if all fields initialized
|
||||||
def __set_initialized(self):
|
def __set_initialized(self):
|
||||||
|
|
@ -358,7 +362,7 @@ class LeObject(object):
|
||||||
if set(expected_fields) == set(self.__initialized):
|
if set(expected_fields) == set(self.__initialized):
|
||||||
self.__is_initialized = True
|
self.__is_initialized = True
|
||||||
|
|
||||||
##@brief Designed to be called when datas are modified
|
# @brief Designed to be called when datas are modified
|
||||||
#
|
#
|
||||||
# Make different checks on the LeObject given it's state (fully initialized or not)
|
# Make different checks on the LeObject given it's state (fully initialized or not)
|
||||||
# @return None if checks succeded else return an exception list
|
# @return None if checks succeded else return an exception list
|
||||||
|
|
@ -405,7 +409,7 @@ class LeObject(object):
|
||||||
# Other methods #
|
# Other methods #
|
||||||
#--------------------#
|
#--------------------#
|
||||||
|
|
||||||
##@brief Temporary method to set private fields attribute at dynamic code generation
|
# @brief Temporary method to set private fields attribute at dynamic code generation
|
||||||
#
|
#
|
||||||
# This method is used in the generated dynamic code to set the _fields attribute
|
# This method is used in the generated dynamic code to set the _fields attribute
|
||||||
# at the end of the dyncode parse
|
# at the end of the dyncode parse
|
||||||
|
|
@ -416,7 +420,7 @@ class LeObject(object):
|
||||||
def _set__fields(cls, field_list):
|
def _set__fields(cls, field_list):
|
||||||
cls._fields = field_list
|
cls._fields = field_list
|
||||||
|
|
||||||
## @brief Check that datas are valid for this type
|
# @brief Check that datas are valid for this type
|
||||||
# @param datas dict : key == field name value are field values
|
# @param datas dict : key == field name value are field values
|
||||||
# @param complete bool : if True expect that datas provide values for all non internal fields
|
# @param complete bool : if True expect that datas provide values for all non internal fields
|
||||||
# @param allow_internal bool : if True don't raise an error if a field is internal
|
# @param allow_internal bool : if True don't raise an error if a field is internal
|
||||||
|
|
@ -455,7 +459,7 @@ class LeObject(object):
|
||||||
raise LeApiDataCheckErrors("Error while checking datas", err_l)
|
raise LeApiDataCheckErrors("Error while checking datas", err_l)
|
||||||
return checked_datas
|
return checked_datas
|
||||||
|
|
||||||
##@brief Check and prepare datas
|
# @brief Check and prepare datas
|
||||||
#
|
#
|
||||||
# @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
|
# @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
|
||||||
#
|
#
|
||||||
|
|
@ -478,7 +482,7 @@ construction and consitency when datas are not complete\n")
|
||||||
cls._check_datas_consistency(ret_datas)
|
cls._check_datas_consistency(ret_datas)
|
||||||
return ret_datas
|
return ret_datas
|
||||||
|
|
||||||
## @brief Construct datas values
|
# @brief Construct datas values
|
||||||
#
|
#
|
||||||
# @param cls
|
# @param cls
|
||||||
# @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
|
# @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
|
||||||
|
|
@ -494,7 +498,7 @@ construction and consitency when datas are not complete\n")
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
## @brief Check datas consistency
|
# @brief Check datas consistency
|
||||||
#
|
#
|
||||||
# @warning assert that datas is complete
|
# @warning assert that datas is complete
|
||||||
# @param cls
|
# @param cls
|
||||||
|
|
@ -512,7 +516,7 @@ construction and consitency when datas are not complete\n")
|
||||||
if len(err_l) > 0:
|
if len(err_l) > 0:
|
||||||
raise LeApiDataCheckError("Datas consistency checks fails", err_l)
|
raise LeApiDataCheckError("Datas consistency checks fails", err_l)
|
||||||
|
|
||||||
## @brief Check datas consistency
|
# @brief Check datas consistency
|
||||||
#
|
#
|
||||||
# @warning assert that datas is complete
|
# @warning assert that datas is complete
|
||||||
# @param cls
|
# @param cls
|
||||||
|
|
@ -523,14 +527,14 @@ construction and consitency when datas are not complete\n")
|
||||||
for fname, dh in cls._fields.items():
|
for fname, dh in cls._fields.items():
|
||||||
ret = dh.make_consistency(fname, datas, type_query)
|
ret = dh.make_consistency(fname, datas, type_query)
|
||||||
|
|
||||||
## @brief Add a new instance of LeObject
|
# @brief Add a new instance of LeObject
|
||||||
# @return a new uid en case of success, False otherwise
|
# @return a new uid en case of success, False otherwise
|
||||||
@classmethod
|
@classmethod
|
||||||
def insert(cls, datas):
|
def insert(cls, datas):
|
||||||
query = LeInsertQuery(cls)
|
query = LeInsertQuery(cls)
|
||||||
return query.execute(datas)
|
return query.execute(datas)
|
||||||
|
|
||||||
## @brief Update an instance of LeObject
|
# @brief Update an instance of LeObject
|
||||||
#
|
#
|
||||||
#@param datas : list of new datas
|
#@param datas : list of new datas
|
||||||
def update(self, datas=None):
|
def update(self, datas=None):
|
||||||
|
|
@ -551,7 +555,7 @@ construction and consitency when datas are not complete\n")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
## @brief Delete an instance of LeObject
|
# @brief Delete an instance of LeObject
|
||||||
#
|
#
|
||||||
#@return 1 if the objet has been deleted
|
#@return 1 if the objet has been deleted
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
|
@ -566,7 +570,7 @@ construction and consitency when datas are not complete\n")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
## @brief Delete instances of LeObject
|
# @brief Delete instances of LeObject
|
||||||
#@param query_filters list
|
#@param query_filters list
|
||||||
#@returns the number of deleted items
|
#@returns the number of deleted items
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -585,7 +589,7 @@ construction and consitency when datas are not complete\n")
|
||||||
deleted += result
|
deleted += result
|
||||||
return deleted
|
return deleted
|
||||||
|
|
||||||
## @brief Get instances of LeObject
|
# @brief Get instances of LeObject
|
||||||
#
|
#
|
||||||
#@param query_filters dict : (filters, relational filters), with filters is a list of tuples : (FIELD, OPERATOR, VALUE) )
|
#@param query_filters dict : (filters, relational filters), with filters is a list of tuples : (FIELD, OPERATOR, VALUE) )
|
||||||
#@param field_list list|None : list of string representing fields see
|
#@param field_list list|None : list of string representing fields see
|
||||||
|
|
@ -624,7 +628,7 @@ construction and consitency when datas are not complete\n")
|
||||||
|
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
##@brief Retrieve an object given an UID
|
# @brief Retrieve an object given an UID
|
||||||
#@todo broken multiple UID
|
#@todo broken multiple UID
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_from_uid(cls, uid):
|
def get_from_uid(cls, uid):
|
||||||
|
|
@ -651,7 +655,7 @@ object ! For class %s with uid value = %s" % (cls, uid))
|
||||||
return None
|
return None
|
||||||
return res[0]
|
return res[0]
|
||||||
|
|
||||||
##@brief Checks if an object exists
|
# @brief Checks if an object exists
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_exist(cls, uid):
|
def is_exist(cls, uid):
|
||||||
if cls.uid_fieldname() is None:
|
if cls.uid_fieldname() is None:
|
||||||
|
|
@ -659,4 +663,3 @@ object ! For class %s with uid value = %s" % (cls, uid))
|
||||||
"No uid defined for class %s" % cls.__name__)
|
"No uid defined for class %s" % cls.__name__)
|
||||||
from .query import is_exist
|
from .query import is_exist
|
||||||
return is_exist(cls, uid)
|
return is_exist(cls, uid)
|
||||||
|
|
||||||
|
|
|
||||||
2
lodel/mlnamedobject/Makefile.am
Normal file
2
lodel/mlnamedobject/Makefile.am
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
mlnamedobject_PYTHON= *.py
|
||||||
|
mlnamedobjectdir=$(pkgpythondir)/mlnamedobject
|
||||||
0
lodel/mlnamedobject/__init__.py
Normal file
0
lodel/mlnamedobject/__init__.py
Normal file
18
lodel/mlnamedobject/mlnamedobject.py
Normal file
18
lodel/mlnamedobject/mlnamedobject.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from lodel.context import LodelContext
|
||||||
|
LodelContext.expose_modules(globals(), {
|
||||||
|
'lodel.utils.mlstring': ['MlString']})
|
||||||
|
|
||||||
|
# @package lodel.mlnamedobject Lodel2 description of objects module
|
||||||
|
#
|
||||||
|
# Display name and Description of a lodel2 object
|
||||||
|
|
||||||
|
# @brief Class allows display name and help text for lodel2 objects and fields
|
||||||
|
|
||||||
|
|
||||||
|
class MlNamedObject(object):
|
||||||
|
|
||||||
|
def __init__(self, display_name=None, help_text=None):
|
||||||
|
self.display_name = None if display_name is None else MlString(display_name)
|
||||||
|
self.help_text = None if help_text is None else MlString(help_text)
|
||||||
|
|
@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.plugins': ['Plugin'],
|
'lodel.plugin.plugins': ['Plugin'],
|
||||||
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
||||||
'LodelScriptError', 'DatasourcePluginError'],
|
'LodelScriptError', 'DatasourcePluginError'],
|
||||||
'lodel.settings.validator': ['SettingValidator'],
|
'lodel.validator.validator': ['Validator'],
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||||
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ class DatasourcePlugin(Plugin):
|
||||||
'section': 'lodel2',
|
'section': 'lodel2',
|
||||||
'key': 'datasource_connectors',
|
'key': 'datasource_connectors',
|
||||||
'default': 'dummy_datasource',
|
'default': 'dummy_datasource',
|
||||||
'validator': SettingValidator(
|
'validator': Validator(
|
||||||
'custom_list', none_is_valid = False,
|
'custom_list', none_is_valid = False,
|
||||||
validator_name = 'plugin', validator_kwargs = {
|
validator_name = 'plugin', validator_kwargs = {
|
||||||
'ptype': _glob_typename,
|
'ptype': _glob_typename,
|
||||||
|
|
@ -280,13 +280,13 @@ but %s is a %s" % (ds_name, pinstance.__class__.__name__))
|
||||||
#CONFSPEC = {
|
#CONFSPEC = {
|
||||||
# 'lodel2.datasource.mysql.*' : {
|
# 'lodel2.datasource.mysql.*' : {
|
||||||
# 'host': ( 'localhost',
|
# 'host': ( 'localhost',
|
||||||
# SettingValidator('host')),
|
# Validator('host')),
|
||||||
# 'db_name': ( 'lodel',
|
# 'db_name': ( 'lodel',
|
||||||
# SettingValidator('string')),
|
# Validator('string')),
|
||||||
# 'username': ( None,
|
# 'username': ( None,
|
||||||
# SettingValidator('string')),
|
# Validator('string')),
|
||||||
# 'password': ( None,
|
# 'password': ( None,
|
||||||
# SettingValidator('string')),
|
# Validator('string')),
|
||||||
# }
|
# }
|
||||||
#}
|
#}
|
||||||
#</pre>
|
#</pre>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.plugins': ['Plugin'],
|
'lodel.plugin.plugins': ['Plugin'],
|
||||||
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
||||||
'LodelScriptError', 'DatasourcePluginError'],
|
'LodelScriptError', 'DatasourcePluginError'],
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
_glob_typename = 'extension'
|
_glob_typename = 'extension'
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ class Extension(Plugin):
|
||||||
'section': 'lodel2',
|
'section': 'lodel2',
|
||||||
'key': 'extensions',
|
'key': 'extensions',
|
||||||
'default': None,
|
'default': None,
|
||||||
'validator': SettingValidator(
|
'validator': Validator(
|
||||||
'custom_list', none_is_valid = True,
|
'custom_list', none_is_valid = True,
|
||||||
validator_name = 'plugin', validator_kwargs = {
|
validator_name = 'plugin', validator_kwargs = {
|
||||||
'ptype': _glob_typename,
|
'ptype': _glob_typename,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.plugins': ['Plugin'],
|
'lodel.plugin.plugins': ['Plugin'],
|
||||||
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
||||||
'LodelScriptError', 'DatasourcePluginError'],
|
'LodelScriptError', 'DatasourcePluginError'],
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
_glob_typename = 'ui'
|
_glob_typename = 'ui'
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ class InterfacePlugin(Plugin):
|
||||||
'section': 'lodel2',
|
'section': 'lodel2',
|
||||||
'key': 'interface',
|
'key': 'interface',
|
||||||
'default': None,
|
'default': None,
|
||||||
'validator': SettingValidator(
|
'validator': Validator(
|
||||||
'plugin', none_is_valid = True, ptype = _glob_typename)}
|
'plugin', none_is_valid = True, ptype = _glob_typename)}
|
||||||
|
|
||||||
_type_conf_name = _glob_typename
|
_type_conf_name = _glob_typename
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.plugins': ['Plugin', 'MetaPlugType'],
|
'lodel.plugin.plugins': ['Plugin', 'MetaPlugType'],
|
||||||
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
||||||
'LodelScriptError', 'DatasourcePluginError'],
|
'LodelScriptError', 'DatasourcePluginError'],
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
|
|
||||||
##@brief SessionHandlerPlugin metaclass designed to implements a wrapper
|
##@brief SessionHandlerPlugin metaclass designed to implements a wrapper
|
||||||
|
|
@ -53,7 +53,7 @@ class SessionHandlerPlugin(Plugin, metaclass=SessionPluginWrapper):
|
||||||
'section': 'lodel2',
|
'section': 'lodel2',
|
||||||
'key': 'session_handler',
|
'key': 'session_handler',
|
||||||
'default': None,
|
'default': None,
|
||||||
'validator': SettingValidator(
|
'validator': Validator(
|
||||||
'plugin', none_is_valid=False,ptype = _glob_typename)}
|
'plugin', none_is_valid=False,ptype = _glob_typename)}
|
||||||
|
|
||||||
_type_conf_name = _glob_typename
|
_type_conf_name = _glob_typename
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
__plugin_name__ = "dummy"
|
__plugin_name__ = "dummy"
|
||||||
__version__ = '0.0.1' #or __version__ = [0,0,1]
|
__version__ = '0.0.1' #or __version__ = [0,0,1]
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.section1': {
|
'lodel2.section1': {
|
||||||
'key1': ( None,
|
'key1': ( None,
|
||||||
SettingValidator('dummy'))
|
Validator('dummy'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
from .datasource import DummyDatasource as Datasource
|
from .datasource import DummyDatasource as Datasource
|
||||||
|
|
||||||
__plugin_type__ = 'datasource'
|
__plugin_type__ = 'datasource'
|
||||||
|
|
@ -12,7 +12,7 @@ __plugin_deps__ = []
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.datasource.dummy_datasource.*' : {
|
'lodel2.datasource.dummy_datasource.*' : {
|
||||||
'dummy': ( None,
|
'dummy': ( None,
|
||||||
SettingValidator('dummy'))}
|
Validator('dummy'))}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
__plugin_name__ = 'filesystem_session'
|
__plugin_name__ = 'filesystem_session'
|
||||||
__version__ = [0,0,1]
|
__version__ = [0,0,1]
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.sessions':{
|
'lodel2.sessions':{
|
||||||
'directory': ('/tmp/', SettingValidator('path')),
|
'directory': ('/tmp/', Validator('path')),
|
||||||
'expiration': (900, SettingValidator('int')),
|
'expiration': (900, Validator('int')),
|
||||||
'file_template': ('lodel2_%s.sess', SettingValidator('dummy'))
|
'file_template': ('lodel2_%s.sess', Validator('dummy'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
##@brief Mongodb datasource plugin confspec
|
##@brief Mongodb datasource plugin confspec
|
||||||
#@ingroup plugin_mongodb_datasource
|
#@ingroup plugin_mongodb_datasource
|
||||||
|
|
@ -10,11 +10,11 @@ LodelContext.expose_modules(globals(), {
|
||||||
#Describe mongodb plugin configuration. Keys are :
|
#Describe mongodb plugin configuration. Keys are :
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.datasource.mongodb_datasource.*':{
|
'lodel2.datasource.mongodb_datasource.*':{
|
||||||
'read_only': (False, SettingValidator('bool')),
|
'read_only': (False, Validator('bool')),
|
||||||
'host': ('localhost', SettingValidator('host')),
|
'host': ('localhost', Validator('host')),
|
||||||
'port': (None, SettingValidator('string', none_is_valid = True)),
|
'port': (None, Validator('string', none_is_valid = True)),
|
||||||
'db_name':('lodel', SettingValidator('string')),
|
'db_name':('lodel', Validator('string')),
|
||||||
'username': (None, SettingValidator('string')),
|
'username': (None, Validator('string')),
|
||||||
'password': (None, SettingValidator('string'))
|
'password': (None, Validator('string'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from lodel.context import LodelContext, ContextError
|
from lodel.context import LodelContext, ContextError
|
||||||
try:
|
try:
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
__plugin_name__ = "multisite"
|
__plugin_name__ = "multisite"
|
||||||
__version__ = '0.0.1' #or __version__ = [0,0,1]
|
__version__ = '0.0.1' #or __version__ = [0,0,1]
|
||||||
|
|
@ -13,8 +13,8 @@ try:
|
||||||
|
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.server': {
|
'lodel2.server': {
|
||||||
'port': (80,SettingValidator('int')),
|
'port': (80,Validator('int')),
|
||||||
'listen_addr': ('', SettingValidator('string')),
|
'listen_addr': ('', Validator('string')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
#Define a minimal confspec used by multisite loader
|
#Define a minimal confspec used by multisite loader
|
||||||
LODEL2_CONFSPECS = {
|
LODEL2_CONFSPECS = {
|
||||||
'lodel2': {
|
'lodel2': {
|
||||||
'debug': (True, SettingValidator('bool'))
|
'debug': (True, Validator('bool'))
|
||||||
},
|
},
|
||||||
'lodel2.server': {
|
'lodel2.server': {
|
||||||
'listen_address': ('127.0.0.1', SettingValidator('dummy')),
|
'listen_address': ('127.0.0.1', Validator('dummy')),
|
||||||
#'listen_address': ('', SettingValidator('ip')), #<-- not implemented
|
#'listen_address': ('', Validator('ip')), #<-- not implemented
|
||||||
'listen_port': ( 1337, SettingValidator('int')),
|
'listen_port': ( 1337, Validator('int')),
|
||||||
'uwsgi_workers': (8, SettingValidator('int')),
|
'uwsgi_workers': (8, Validator('int')),
|
||||||
'uwsgicmd': ('/usr/bin/uwsgi', SettingValidator('dummy')),
|
'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')),
|
||||||
'virtualenv': (None, SettingValidator('path', none_is_valid = True)),
|
'virtualenv': (None, Validator('path', none_is_valid = True)),
|
||||||
},
|
},
|
||||||
'lodel2.logging.*' : {
|
'lodel2.logging.*' : {
|
||||||
'level': ( 'ERROR',
|
'level': ( 'ERROR',
|
||||||
SettingValidator('loglevel')),
|
Validator('loglevel')),
|
||||||
'context': ( False,
|
'context': ( False,
|
||||||
SettingValidator('bool')),
|
Validator('bool')),
|
||||||
'filename': ( None,
|
'filename': ( None,
|
||||||
SettingValidator('errfile', none_is_valid = True)),
|
Validator('errfile', none_is_valid = True)),
|
||||||
'backupcount': ( 10,
|
'backupcount': ( 10,
|
||||||
SettingValidator('int', none_is_valid = False)),
|
Validator('int', none_is_valid = False)),
|
||||||
'maxbytes': ( 1024*10,
|
'maxbytes': ( 1024*10,
|
||||||
SettingValidator('int', none_is_valid = False)),
|
Validator('int', none_is_valid = False)),
|
||||||
},
|
},
|
||||||
'lodel2.datasources.*': {
|
'lodel2.datasources.*': {
|
||||||
'read_only': (False, SettingValidator('bool')),
|
'read_only': (False, Validator('bool')),
|
||||||
'identifier': ( None, SettingValidator('string')),
|
'identifier': ( None, Validator('string')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
__plugin_name__ = 'ram_sessions'
|
__plugin_name__ = 'ram_sessions'
|
||||||
__version__ = [0,0,1]
|
__version__ = [0,0,1]
|
||||||
|
|
@ -11,7 +11,7 @@ __fullname__ = "RAM Session Store Plugin"
|
||||||
|
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.sessions':{
|
'lodel2.sessions':{
|
||||||
'expiration': (900, SettingValidator('int')),
|
'expiration': (900, Validator('int')),
|
||||||
'tokensize': (512, SettingValidator('int')),
|
'tokensize': (512, Validator('int')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings.validator': ['SettingValidator']})
|
'lodel.validator.validator': ['Validator']})
|
||||||
|
|
||||||
CONFSPEC = {
|
CONFSPEC = {
|
||||||
'lodel2.webui': {
|
'lodel2.webui': {
|
||||||
'standalone': ( 'False',
|
'standalone': ( 'False',
|
||||||
SettingValidator('string')),
|
Validator('string')),
|
||||||
'listen_address': ( '127.0.0.1',
|
'listen_address': ( '127.0.0.1',
|
||||||
SettingValidator('dummy')),
|
Validator('dummy')),
|
||||||
'listen_port': ( '9090',
|
'listen_port': ( '9090',
|
||||||
SettingValidator('int')),
|
Validator('int')),
|
||||||
'static_url': ( 'http://127.0.0.1/static/',
|
'static_url': ( 'http://127.0.0.1/static/',
|
||||||
SettingValidator('regex', pattern = r'^https?://[^/].*$')),
|
Validator('regex', pattern = r'^https?://[^/].*$')),
|
||||||
'virtualenv': (None,
|
'virtualenv': (None,
|
||||||
SettingValidator('path', none_is_valid=True)),
|
Validator('path', none_is_valid=True)),
|
||||||
'uwsgicmd': ('/usr/bin/uwsgi', SettingValidator('dummy')),
|
'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')),
|
||||||
'cookie_secret_key': ('ConfigureYourOwnCookieSecretKey', SettingValidator('dummy')),
|
'cookie_secret_key': ('ConfigureYourOwnCookieSecretKey', Validator('dummy')),
|
||||||
'cookie_session_id': ('lodel', SettingValidator('dummy')),
|
'cookie_session_id': ('lodel', Validator('dummy')),
|
||||||
'uwsgi_workers': (2, SettingValidator('int'))
|
'uwsgi_workers': (2, Validator('int'))
|
||||||
},
|
},
|
||||||
'lodel2.webui.sessions': {
|
'lodel2.webui.sessions': {
|
||||||
'directory': ( '/tmp',
|
'directory': ( '/tmp',
|
||||||
SettingValidator('path')),
|
Validator('path')),
|
||||||
'expiration': ( 900,
|
'expiration': ( 900,
|
||||||
SettingValidator('int')),
|
Validator('int')),
|
||||||
'file_template': ( 'lodel2_%s.sess',
|
'file_template': ( 'lodel2_%s.sess',
|
||||||
SettingValidator('dummy')),
|
Validator('dummy')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@
|
||||||
<h1 class="h1_lodel">Issue {{ obj.data('title') }} </h1>
|
<h1 class="h1_lodel">Issue {{ obj.data('title') }} </h1>
|
||||||
<h2>{{ obj.data('subtitle') }}</h2>
|
<h2>{{ obj.data('subtitle') }}</h2>
|
||||||
{% set directors=person_class.get(("%s in (%s)") % (person_class.uid_fieldname()[0], obj.data('linked_directors')|join(','))) %}
|
{% set directors=person_class.get(("%s in (%s)") % (person_class.uid_fieldname()[0], obj.data('linked_directors')|join(','))) %}
|
||||||
<p><strong>Directors : </strong>{% for director in directors %} <a href="/{{ root_url }}/show_object?classname=Person&lodel_id={{ director.uid() }} " target="_blank" >{{ director.data('firstname')}} {{ director.data('lastname')}}</a> ; {% endfor %}</p>
|
<p><strong>Directors : </strong>{% for director in directors %} <a href="/{{ root_url }}/show_object?classname=Person&lodel_id={{ director.uid() }} " >{{ director.data('firstname')}} {{ director.data('lastname')}}</a> ; {% endfor %}</p>
|
||||||
{% set texts=my_classes.Text.get(("%s in (%s)") % (my_classes.Text.uid_fieldname()[0], obj.data('linked_texts')|join(','))) %}
|
{% set texts=my_classes.Text.get(("%s in (%s)") % (my_classes.Text.uid_fieldname()[0], obj.data('linked_texts')|join(','))) %}
|
||||||
{% set parts=my_classes.Part.get(("%s in (%s)") % (my_classes.Part.uid_fieldname()[0], obj.data('linked_parts')|join(','))) %}
|
{% set parts=my_classes.Part.get(("%s in (%s)") % (my_classes.Part.uid_fieldname()[0], obj.data('linked_parts')|join(','))) %}
|
||||||
{% if texts is not none: %}
|
{% if texts is not none: %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for text in texts %}
|
{% for text in texts %}
|
||||||
<li>
|
<li>
|
||||||
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" target="_blank" > {{ text.data('title') }}</a></h3>
|
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" > {{ text.data('title') }}</a></h3>
|
||||||
<h4>{{ text.data('subtitle') }}</h4>
|
<h4>{{ text.data('subtitle') }}</h4>
|
||||||
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
||||||
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for part in parts %}
|
{% for part in parts %}
|
||||||
<li>
|
<li>
|
||||||
<h3><a href="/{{ root_url }}/show_object?classname={{ part.data('classname') }}&lodel_id={{ part.uid() }}" target="_blank"> {{ part.data('title') }}</a></h3>
|
<h3><a href="/{{ root_url }}/show_object?classname={{ part.data('classname') }}&lodel_id={{ part.uid() }}"> {{ part.data('title') }}</a></h3>
|
||||||
<h4>{{ part.data('subtitle') }}</h4>
|
<h4>{{ part.data('subtitle') }}</h4>
|
||||||
{% set directors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], part.data('linked_directors')|join(','))) %}
|
{% set directors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], part.data('linked_directors')|join(','))) %}
|
||||||
<p>Directors : {% for director in directors %} {{ director.data('firstname')}} {{ director.data('lastname')}} ; {% endfor %} </p>
|
<p>Directors : {% for director in directors %} {{ director.data('firstname')}} {{ director.data('lastname')}} ; {% endfor %} </p>
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
<ul style="margin-left:20px">
|
<ul style="margin-left:20px">
|
||||||
{% for text in p_texts %}
|
{% for text in p_texts %}
|
||||||
<li>
|
<li>
|
||||||
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" target="_blank"> {{ text.data('title') }}</a></h3>
|
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" > {{ text.data('title') }}</a></h3>
|
||||||
<h4>{{ text.data('subtitle') }}</h4>
|
<h4>{{ text.data('subtitle') }}</h4>
|
||||||
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
||||||
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for part in ss_parts %}
|
{% for part in ss_parts %}
|
||||||
<li>
|
<li>
|
||||||
<h3><a href="/{{ root_url }}/show_object?classname={{ part.data('classname') }}&lodel_id={{ part.uid() }}" target="_blank"> {{ part.data('title') }}</a></h3>
|
<h3><a href="/{{ root_url }}/show_object?classname={{ part.data('classname') }}&lodel_id={{ part.uid() }}" > {{ part.data('title') }}</a></h3>
|
||||||
<h4>{{ part.data('subtitle') }}</h4>
|
<h4>{{ part.data('subtitle') }}</h4>
|
||||||
{% set directors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], part.data('linked_directors')|join(','))) %}
|
{% set directors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], part.data('linked_directors')|join(','))) %}
|
||||||
<p>Directors : {% for director in directors %} {{ director.data('firstname')}} {{ director.data('lastname')}} ; {% endfor %} </p>
|
<p>Directors : {% for director in directors %} {{ director.data('firstname')}} {{ director.data('lastname')}} ; {% endfor %} </p>
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
<ul style="margin-left:20px">
|
<ul style="margin-left:20px">
|
||||||
{% for text in sp_texts %}
|
{% for text in sp_texts %}
|
||||||
<li>
|
<li>
|
||||||
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" target="_blank"> {{ text.data('title') }}</a></h3>
|
<h3><a href="/{{ root_url }}/show_object?classname={{ text.data('classname') }}&lodel_id={{ text.uid() }}" > {{ text.data('title') }}</a></h3>
|
||||||
<h4>{{ text.data('subtitle') }}</h4>
|
<h4>{{ text.data('subtitle') }}</h4>
|
||||||
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
{% set authors = my_classes.Person.get(("%s in (%s)") % (person_class.uid_fieldname()[0], text.data('linked_persons')|join(','))) %}
|
||||||
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
<p>Authors : {% for author in authors %} {{ author.data('firstname')}} {{ author.data('lastname')}} ; {% endfor %} </p>
|
||||||
|
|
|
||||||
|
|
@ -13,28 +13,30 @@ from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.logger': 'logger',
|
'lodel.logger': 'logger',
|
||||||
'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
|
'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
|
||||||
'lodel.settings.validator': ['SettingValidator', 'LODEL2_CONF_SPECS',
|
'lodel.validator.validator': ['Validator', 'LODEL2_CONF_SPECS',
|
||||||
'confspec_append'],
|
'confspec_append'],
|
||||||
'lodel.settings.settings_loader': ['SettingsLoader']})
|
'lodel.settings.settings_loader': ['SettingsLoader']})
|
||||||
|
|
||||||
|
|
||||||
## @package lodel.settings.settings Lodel2 settings module
|
# @package lodel.settings.settings Lodel2 settings module
|
||||||
#
|
#
|
||||||
# Contains the class that handles the namedtuple tree of settings
|
# Contains the class that handles the namedtuple tree of settings
|
||||||
|
|
||||||
##@brief A default python system lib path
|
# @brief A default python system lib path
|
||||||
PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format(
|
PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format(
|
||||||
|
|
||||||
major=sys.version_info.major,
|
major=sys.version_info.major,
|
||||||
minor=sys.version_info.minor)
|
minor=sys.version_info.minor)
|
||||||
|
|
||||||
|
|
||||||
class MetaSettings(type):
|
class MetaSettings(type):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def s(self):
|
def s(self):
|
||||||
self.singleton_assert(True)
|
self.singleton_assert(True)
|
||||||
return self.instance.settings
|
return self.instance.settings
|
||||||
|
|
||||||
##@brief Handles configuration load etc.
|
# @brief Handles configuration load etc.
|
||||||
#
|
#
|
||||||
# To see howto bootstrap Settings and use it in lodel instance see
|
# To see howto bootstrap Settings and use it in lodel instance see
|
||||||
# @ref lodel.settings
|
# @ref lodel.settings
|
||||||
|
|
@ -73,34 +75,36 @@ class MetaSettings(type):
|
||||||
# @todo delete the first stage, the lib path HAVE TO BE HARDCODED. In fact
|
# @todo delete the first stage, the lib path HAVE TO BE HARDCODED. In fact
|
||||||
# when we will run lodel in production the lodel2 lib will be in the python path
|
# when we will run lodel in production the lodel2 lib will be in the python path
|
||||||
#@todo add log messages (now we can)
|
#@todo add log messages (now we can)
|
||||||
|
|
||||||
|
|
||||||
class Settings(object, metaclass=MetaSettings):
|
class Settings(object, metaclass=MetaSettings):
|
||||||
|
|
||||||
## @brief Stores the singleton instance
|
# @brief Stores the singleton instance
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
## @brief Instanciate the Settings singleton
|
# @brief Instanciate the Settings singleton
|
||||||
# @param conf_dir str : The configuration directory
|
# @param conf_dir str : The configuration directory
|
||||||
#@param custom_confspecs None | dict : if given overwrite default lodel2
|
#@param custom_confspecs None | dict : if given overwrite default lodel2
|
||||||
# confspecs
|
# confspecs
|
||||||
def __init__(self, conf_dir, custom_confspecs=None):
|
def __init__(self, conf_dir, custom_confspecs=None):
|
||||||
self.singleton_assert() # check that it is the only instance
|
self.singleton_assert() # check that it is the only instance
|
||||||
Settings.instance = self
|
Settings.instance = self
|
||||||
## @brief Configuration specification
|
# @brief Configuration specification
|
||||||
#
|
#
|
||||||
# Initialized by Settings.__bootstrap() method
|
# Initialized by Settings.__bootstrap() method
|
||||||
self.__conf_specs = custom_confspecs
|
self.__conf_specs = custom_confspecs
|
||||||
## @brief Stores the configurations in namedtuple tree
|
# @brief Stores the configurations in namedtuple tree
|
||||||
self.__confs = None
|
self.__confs = None
|
||||||
self.__conf_dir = conf_dir
|
self.__conf_dir = conf_dir
|
||||||
self.__started = False
|
self.__started = False
|
||||||
self.__bootstrap()
|
self.__bootstrap()
|
||||||
|
|
||||||
## @brief Get the named tuple representing configuration
|
# @brief Get the named tuple representing configuration
|
||||||
@property
|
@property
|
||||||
def settings(self):
|
def settings(self):
|
||||||
return self.__confs.lodel2
|
return self.__confs.lodel2
|
||||||
|
|
||||||
## @brief Delete the singleton instance
|
# @brief Delete the singleton instance
|
||||||
@classmethod
|
@classmethod
|
||||||
def stop(cls):
|
def stop(cls):
|
||||||
del(cls.instance)
|
del(cls.instance)
|
||||||
|
|
@ -110,7 +114,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
def started(cls):
|
def started(cls):
|
||||||
return cls.instance is not None and cls.instance.__started
|
return cls.instance is not None and cls.instance.__started
|
||||||
|
|
||||||
##@brief An utility method that raises if the singleton is not in a good
|
# @brief An utility method that raises if the singleton is not in a good
|
||||||
# state
|
# state
|
||||||
#@param expect_instanciated bool : if True we expect that the class is
|
#@param expect_instanciated bool : if True we expect that the class is
|
||||||
# allready instanciated, else not
|
# allready instanciated, else not
|
||||||
|
|
@ -124,7 +128,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
if cls.started():
|
if cls.started():
|
||||||
raise RuntimeError("The Settings class is already started")
|
raise RuntimeError("The Settings class is already started")
|
||||||
|
|
||||||
##@brief Saves a new configuration for section confname
|
# @brief Saves a new configuration for section confname
|
||||||
#@param confname is the name of the modified section
|
#@param confname is the name of the modified section
|
||||||
#@param confvalue is a dict with variables to save
|
#@param confvalue is a dict with variables to save
|
||||||
#@param validator is a dict with adapted validator
|
#@param validator is a dict with adapted validator
|
||||||
|
|
@ -134,7 +138,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
confkey = confname.rpartition('.')
|
confkey = confname.rpartition('.')
|
||||||
loader.setoption(confkey[0], confkey[2], confvalue, validator)
|
loader.setoption(confkey[0], confkey[2], confvalue, validator)
|
||||||
|
|
||||||
##@brief This method handles Settings instance bootstraping
|
# @brief This method handles Settings instance bootstraping
|
||||||
def __bootstrap(self):
|
def __bootstrap(self):
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.plugins': ['Plugin', 'PluginError']})
|
'lodel.plugin.plugins': ['Plugin', 'PluginError']})
|
||||||
|
|
@ -165,10 +169,12 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
# Checking confspecs
|
# Checking confspecs
|
||||||
for section in lodel2_specs:
|
for section in lodel2_specs:
|
||||||
if section.lower() != section:
|
if section.lower() != section:
|
||||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
raise SettingsError(
|
||||||
|
"Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||||
for kname in lodel2_specs[section]:
|
for kname in lodel2_specs[section]:
|
||||||
if kname.lower() != kname:
|
if kname.lower() != kname:
|
||||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
raise SettingsError(
|
||||||
|
"Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||||
|
|
||||||
# Starting the Plugins class
|
# Starting the Plugins class
|
||||||
logger.debug("Starting lodel.plugin.Plugin class")
|
logger.debug("Starting lodel.plugin.Plugin class")
|
||||||
|
|
@ -187,7 +193,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
self.__populate_from_specs(self.__conf_specs, loader)
|
self.__populate_from_specs(self.__conf_specs, loader)
|
||||||
self.__started = True
|
self.__started = True
|
||||||
|
|
||||||
##@brief Produce a configuration specification dict by merging all specifications
|
# @brief Produce a configuration specification dict by merging all specifications
|
||||||
#
|
#
|
||||||
# Merges global lodel2 conf spec from @ref lodel.settings.validator.LODEL2_CONF_SPECS
|
# Merges global lodel2 conf spec from @ref lodel.settings.validator.LODEL2_CONF_SPECS
|
||||||
# and configuration specifications from loaded plugins
|
# and configuration specifications from loaded plugins
|
||||||
|
|
@ -198,18 +204,21 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
for section in spec:
|
for section in spec:
|
||||||
if section.lower() != section:
|
if section.lower() != section:
|
||||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
raise SettingsError(
|
||||||
|
"Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||||
if section not in res:
|
if section not in res:
|
||||||
res[section] = dict()
|
res[section] = dict()
|
||||||
for kname in spec[section]:
|
for kname in spec[section]:
|
||||||
if kname.lower() != kname:
|
if kname.lower() != kname:
|
||||||
raise SettingsError("Only lower case are allowed in section name (thank's ConfigParser...)")
|
raise SettingsError(
|
||||||
|
"Only lower case are allowed in section name (thank's ConfigParser...)")
|
||||||
if kname in res[section]:
|
if kname in res[section]:
|
||||||
raise SettingsError("Duplicated key '%s' in section '%s'" % (kname, section))
|
raise SettingsError("Duplicated key '%s' in section '%s'" %
|
||||||
|
(kname, section))
|
||||||
res[section.lower()][kname] = copy.copy(spec[section][kname])
|
res[section.lower()][kname] = copy.copy(spec[section][kname])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Populate the Settings instance with options values fetched with the loader from merged specs
|
# @brief Populate the Settings instance with options values fetched with the loader from merged specs
|
||||||
#
|
#
|
||||||
# Populate the __confs attribute
|
# Populate the __confs attribute
|
||||||
# @param specs dict : Settings specification dictionnary as returned by __merge_specs
|
# @param specs dict : Settings specification dictionnary as returned by __merge_specs
|
||||||
|
|
@ -222,7 +231,8 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
variable_sections = [section for section in specs if section.endswith('.*')]
|
variable_sections = [section for section in specs if section.endswith('.*')]
|
||||||
for vsec in variable_sections:
|
for vsec in variable_sections:
|
||||||
preffix = vsec[:-2]
|
preffix = vsec[:-2]
|
||||||
for section in loader.getsection(preffix, 'default'): #WARNING : hardcoded default section
|
# WARNING : hardcoded default section
|
||||||
|
for section in loader.getsection(preffix, 'default'):
|
||||||
specs[section] = copy.copy(specs[vsec])
|
specs[section] = copy.copy(specs[vsec])
|
||||||
del(specs[vsec])
|
del(specs[vsec])
|
||||||
# Fetching values for sections
|
# Fetching values for sections
|
||||||
|
|
@ -239,7 +249,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
self.__confs_to_namedtuple()
|
self.__confs_to_namedtuple()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
##@brief Transform the __confs attribute into imbricated namedtuple
|
# @brief Transform the __confs attribute into imbricated namedtuple
|
||||||
#
|
#
|
||||||
# For example an option named "foo" in a section named "hello.world" will
|
# For example an option named "foo" in a section named "hello.world" will
|
||||||
# be acessible with self.__confs.hello.world.foo
|
# be acessible with self.__confs.hello.world.foo
|
||||||
|
|
@ -295,7 +305,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
path.append((curname, cur))
|
path.append((curname, cur))
|
||||||
nodename += '.' + curname.title()
|
nodename += '.' + curname.title()
|
||||||
|
|
||||||
##@brief Forge a named tuple given a conftree node
|
# @brief Forge a named tuple given a conftree node
|
||||||
# @param conftree dict : A conftree node
|
# @param conftree dict : A conftree node
|
||||||
# @param name str
|
# @param name str
|
||||||
# @return a named tuple with fieldnames corresponding to conftree keys
|
# @return a named tuple with fieldnames corresponding to conftree keys
|
||||||
|
|
@ -303,11 +313,13 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
ResNamedTuple = namedtuple(name, conftree.keys())
|
ResNamedTuple = namedtuple(name, conftree.keys())
|
||||||
return ResNamedTuple(**conftree)
|
return ResNamedTuple(**conftree)
|
||||||
|
|
||||||
|
|
||||||
class MetaSettingsRO(type):
|
class MetaSettingsRO(type):
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(Settings.s, name)
|
return getattr(Settings.s, name)
|
||||||
|
|
||||||
|
|
||||||
## @brief A class that provide . notation read only access to configurations
|
# @brief A class that provide . notation read only access to configurations
|
||||||
class SettingsRO(object, metaclass=MetaSettingsRO):
|
class SettingsRO(object, metaclass=MetaSettingsRO):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ from lodel.context import LodelContext
|
||||||
|
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.logger': 'logger',
|
'lodel.logger': 'logger',
|
||||||
'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
|
'lodel.settings.utils': ['SettingsError', 'SettingsErrors']})
|
||||||
'lodel.settings.validator': ['SettingsValidationError']})
|
|
||||||
|
|
||||||
##@brief Merges and loads configuration files
|
##@brief Merges and loads configuration files
|
||||||
class SettingsLoader(object):
|
class SettingsLoader(object):
|
||||||
|
|
@ -51,11 +50,9 @@ class SettingsLoader(object):
|
||||||
conf[section][param]['file'] = f_ini
|
conf[section][param]['file'] = f_ini
|
||||||
self.__conf_sv[section + ':' + param] = f_ini
|
self.__conf_sv[section + ':' + param] = f_ini
|
||||||
else:
|
else:
|
||||||
raise SettingsError("Error redeclaration of key %s in section %s. Found in %s and %s" % (
|
raise SettingsError("Error redeclaration of key %s \
|
||||||
section,
|
in section %s. Found in %s and %s" % (\
|
||||||
param,
|
section, param, f_ini, conf[section][param]['file']))
|
||||||
f_ini,
|
|
||||||
conf[section][param]['file']))
|
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
##@brief Returns option if exists default_value else and validates
|
##@brief Returns option if exists default_value else and validates
|
||||||
|
|
@ -85,8 +82,7 @@ class SettingsLoader(object):
|
||||||
if result is None:
|
if result is None:
|
||||||
if default_value is None and mandatory:
|
if default_value is None and mandatory:
|
||||||
msg = "Default value mandatory for option %s" % keyname
|
msg = "Default value mandatory for option %s" % keyname
|
||||||
expt = SettingsError( msg = msg,
|
expt = SettingsError(msg=msg, key_id=section+'.'+keyname, \
|
||||||
key_id = section+'.'+keyname,
|
|
||||||
filename=sec[keyname]['file'])
|
filename=sec[keyname]['file'])
|
||||||
self.__errors_list.append(expt)
|
self.__errors_list.append(expt)
|
||||||
return
|
return
|
||||||
|
|
@ -95,24 +91,21 @@ class SettingsLoader(object):
|
||||||
sec[keyname]['value'] = default_value
|
sec[keyname]['value'] = default_value
|
||||||
sec[keyname]['file'] = SettingsLoader.DEFAULT_FILENAME
|
sec[keyname]['file'] = SettingsLoader.DEFAULT_FILENAME
|
||||||
result = default_value
|
result = default_value
|
||||||
logger.debug("Using default value for configuration key %s:%s" % (
|
logger.debug("Using default value for configuration key %s:%s" \
|
||||||
section, keyname))
|
% (section, keyname))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return validator(result)
|
return validator(result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Generating nice exceptions
|
# Generating nice exceptions
|
||||||
if False and sec[keyname]['file'] == SettingsLoader.DEFAULT_FILENAME:
|
if False and sec[keyname]['file'] == SettingsLoader.DEFAULT_FILENAME:
|
||||||
expt = SettingsError( msg = 'Mandatory settings not found',
|
expt = SettingsError(msg='Mandatory settings not found', \
|
||||||
key_id=section+'.'+keyname)
|
key_id=section+'.'+keyname)
|
||||||
self.__errors_list.append(expt)
|
self.__errors_list.append(expt)
|
||||||
else:
|
else:
|
||||||
expt = SettingsValidationError(
|
#expt = ValidationError("For %s.%s : %s" % (section, keyname, e))
|
||||||
"For %s.%s : %s" %
|
expt2 = SettingsError(msg=str(expt), \
|
||||||
(section, keyname,e)
|
key_id=section+'.'+keyname, \
|
||||||
)
|
|
||||||
expt2 = SettingsError( msg = str(expt),
|
|
||||||
key_id = section+'.'+keyname,
|
|
||||||
filename=sec[keyname]['file'])
|
filename=sec[keyname]['file'])
|
||||||
self.__errors_list.append(expt2)
|
self.__errors_list.append(expt2)
|
||||||
return
|
return
|
||||||
|
|
@ -185,11 +178,10 @@ class SettingsLoader(object):
|
||||||
remains = self.getremains()
|
remains = self.getremains()
|
||||||
err_l = self.__errors_list
|
err_l = self.__errors_list
|
||||||
for key_id, filename in remains.items():
|
for key_id, filename in remains.items():
|
||||||
err_l.append(SettingsError( msg = "Invalid configuration key",
|
err_l.append(SettingsError(msg="Invalid configuration key", \
|
||||||
key_id = key_id,
|
key_id=key_id, \
|
||||||
filename =filename))
|
filename =filename))
|
||||||
if len(err_l) > 0:
|
if len(err_l) > 0:
|
||||||
raise SettingsErrors(err_l)
|
raise SettingsErrors(err_l)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import hashlib
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
##@brief Stores multilangage string
|
# @brief Stores multilangage string
|
||||||
class MlString(object):
|
class MlString(object):
|
||||||
|
|
||||||
__default_lang = 'eng'
|
__default_lang = 'eng'
|
||||||
|
|
@ -17,7 +17,7 @@ class MlString(object):
|
||||||
'esp',
|
'esp',
|
||||||
]
|
]
|
||||||
|
|
||||||
##@brief Create a new MlString instance
|
# @brief Create a new MlString instance
|
||||||
# @param arg str | dict : Can be a json string, a string or a dict. It could be also a MlString object
|
# @param arg str | dict : Can be a json string, a string or a dict. It could be also a MlString object
|
||||||
def __init__(self, arg):
|
def __init__(self, arg):
|
||||||
self.values = dict()
|
self.values = dict()
|
||||||
|
|
@ -31,9 +31,10 @@ class MlString(object):
|
||||||
elif isinstance(arg, MlString):
|
elif isinstance(arg, MlString):
|
||||||
self.values = copy.copy(arg.values)
|
self.values = copy.copy(arg.values)
|
||||||
else:
|
else:
|
||||||
raise ValueError('<class str>, <class dict> or <class MlString> expected, but %s found' % type(arg))
|
raise ValueError(
|
||||||
|
'<class str>, <class dict> or <class MlString> expected, but %s found' % type(arg))
|
||||||
|
|
||||||
##@brief Return a translation given a lang
|
# @brief Return a translation given a lang
|
||||||
# @param lang str | None : If None return default lang translation
|
# @param lang str | None : If None return default lang translation
|
||||||
def get(self, lang=None):
|
def get(self, lang=None):
|
||||||
lang = self.__default_lang if lang is None else lang
|
lang = self.__default_lang if lang is None else lang
|
||||||
|
|
@ -44,7 +45,7 @@ class MlString(object):
|
||||||
else:
|
else:
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
##@brief Set a translation
|
# @brief Set a translation
|
||||||
# @param lang str : the lang
|
# @param lang str : the lang
|
||||||
# @param val str | None: the translation if None delete the translation
|
# @param val str | None: the translation if None delete the translation
|
||||||
def set(self, lang, val):
|
def set(self, lang, val):
|
||||||
|
|
@ -57,7 +58,7 @@ class MlString(object):
|
||||||
else:
|
else:
|
||||||
self.values[lang] = val
|
self.values[lang] = val
|
||||||
|
|
||||||
##@brief Checks that given lang is valid
|
# @brief Checks that given lang is valid
|
||||||
# @param lang str : the lang
|
# @param lang str : the lang
|
||||||
@classmethod
|
@classmethod
|
||||||
def lang_is_valid(cls, lang):
|
def lang_is_valid(cls, lang):
|
||||||
|
|
@ -65,7 +66,7 @@ class MlString(object):
|
||||||
raise ValueError('Invalid value for lang. Str expected but %s found' % type(lang))
|
raise ValueError('Invalid value for lang. Str expected but %s found' % type(lang))
|
||||||
return lang in cls.langs
|
return lang in cls.langs
|
||||||
|
|
||||||
##@brief Get or set the default lang
|
# @brief Get or set the default lang
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_lang(cls, lang=None):
|
def default_lang(cls, lang=None):
|
||||||
if lang is None:
|
if lang is None:
|
||||||
|
|
@ -74,7 +75,7 @@ class MlString(object):
|
||||||
raise ValueError('lang "%s" is not valid"' % lang)
|
raise ValueError('lang "%s" is not valid"' % lang)
|
||||||
cls.__default_lang = lang
|
cls.__default_lang = lang
|
||||||
|
|
||||||
##@brief Return a mlstring loaded from a json string
|
# @brief Return a mlstring loaded from a json string
|
||||||
# @param json_str str : Json string
|
# @param json_str str : Json string
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json_str):
|
def from_json(cls, json_str):
|
||||||
|
|
@ -95,7 +96,7 @@ class MlString(object):
|
||||||
def __eq__(self, a):
|
def __eq__(self, a):
|
||||||
return hash(self) == hash(a)
|
return hash(self) == hash(a)
|
||||||
|
|
||||||
## @return The default langage translation or any available translation
|
# @return The default langage translation or any available translation
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.__default_lang in self.values:
|
if self.__default_lang in self.values:
|
||||||
return self.values[self.__default_lang]
|
return self.values[self.__default_lang]
|
||||||
|
|
|
||||||
2
lodel/validator/Makefile.am
Normal file
2
lodel/validator/Makefile.am
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
validator_PYTHON=*.py
|
||||||
|
validatordir=$(pkgpythondir)/validator
|
||||||
6
lodel/validator/__init__.py
Normal file
6
lodel/validator/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
## @package lodel.validator Lodel2 validator package
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -9,55 +9,63 @@ import copy
|
||||||
|
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
|
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||||
'LodelFatalError', 'FieldValidationError']})
|
'LodelFatalError', 'FieldValidationError']})
|
||||||
|
|
||||||
## @package lodel.settings.validator Lodel2 settings validators/cast module
|
# @package lodel.settings.validator Lodel2 settings validators/cast module
|
||||||
#
|
#
|
||||||
# Validator are registered in the SettingValidator class.
|
# Validator are registered in the Validator class.
|
||||||
# @note to get a list of registered default validators just run
|
# @note to get a list of registered default validators just run
|
||||||
# <pre>$ python scripts/settings_validator.py</pre>
|
# <pre>$ python scripts/settings_validator.py</pre>
|
||||||
|
|
||||||
##@brief Exception class that should be raised when a validation fails
|
# @brief Exception class that should be raised when a validation fails
|
||||||
class SettingsValidationError(Exception):
|
|
||||||
|
|
||||||
|
class ValidationError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
##@brief Handles settings validators
|
# @brief Handles settings validators
|
||||||
#
|
#
|
||||||
# Class instance are callable objects that takes a value argument (the value to validate). It raises
|
# Class instance are callable objects that takes a value argument (the value to validate). It raises
|
||||||
# a SettingsValidationError if validation fails, else it returns a properly
|
# a ValidationError if validation fails, else it returns a properly
|
||||||
# casted value.
|
# casted value.
|
||||||
#@todo implement an IP validator and use it in multisite confspec
|
#@todo implement an IP validator and use it in multisite confspec
|
||||||
class SettingValidator(object):
|
|
||||||
|
|
||||||
|
class Validator(MlNamedObject):
|
||||||
|
|
||||||
_validators = dict()
|
_validators = dict()
|
||||||
_description = dict()
|
_description = dict()
|
||||||
|
|
||||||
##@brief Instanciate a validator
|
# @brief Instanciate a validator
|
||||||
#@param name str : validator name
|
#@param name str : validator name
|
||||||
#@param none_is_valid bool : if True None will be validated
|
#@param none_is_valid bool : if True None will be validated
|
||||||
#@param **kwargs : more arguement for the validator
|
#@param **kwargs : more arguement for the validator
|
||||||
def __init__(self, name, none_is_valid = False, **kwargs):
|
def __init__(self, name, none_is_valid=False, display_name=None, help_text=None, **kwargs):
|
||||||
if name is not None and name not in self._validators:
|
if name is not None and name not in self._validators:
|
||||||
raise LodelFatalError("No validator named '%s'" % name)
|
raise LodelFatalError("No validator named '%s'" % name)
|
||||||
self.__none_is_valid = none_is_valid
|
self.__none_is_valid = none_is_valid
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self._opt_args = kwargs
|
self._opt_args = kwargs
|
||||||
|
if display_name is None:
|
||||||
|
display_name = name
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
##@brief Call the validator
|
# @brief Call the validator
|
||||||
# @param value *
|
# @param value *
|
||||||
# @return properly casted value
|
# @return properly casted value
|
||||||
# @throw SettingsValidationError
|
# @throw ValidationError
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
if self.__none_is_valid and value is None:
|
if self.__none_is_valid and value is None:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
ret = self._validators[self.__name](value, **self._opt_args)
|
ret = self._validators[self.__name](value, **self._opt_args)
|
||||||
return ret
|
return ret
|
||||||
except Exception as e:
|
except Exception as exp:
|
||||||
raise SettingsValidationError(e)
|
raise ValidationError(exp)
|
||||||
|
|
||||||
##@brief Register a new validator
|
# @brief Register a new validator
|
||||||
# @param name str : validator name
|
# @param name str : validator name
|
||||||
# @param callback callable : the function that will validate a value
|
# @param callback callable : the function that will validate a value
|
||||||
# @param description str
|
# @param description str
|
||||||
|
|
@ -71,75 +79,67 @@ class SettingValidator(object):
|
||||||
cls._validators[name] = callback
|
cls._validators[name] = callback
|
||||||
cls._description[name] = description
|
cls._description[name] = description
|
||||||
|
|
||||||
##@brief Get the validator list associated with description
|
# @brief Get the validator list associated with description
|
||||||
@classmethod
|
@classmethod
|
||||||
def validators_list(cls):
|
def validators_list(cls):
|
||||||
return copy.copy(cls._description)
|
return copy.copy(cls._description)
|
||||||
|
|
||||||
##@brief Create and register a list validator
|
# @brief Create and register a list validator
|
||||||
# @param elt_validator callable : The validator that will be used for validate each elt value
|
# @param elt_validator callable : The validator that will be used for validate each elt value
|
||||||
# @param validator_name str
|
# @param validator_name str
|
||||||
# @param description None | str
|
# @param description None | str
|
||||||
# @param separator str : The element separator
|
# @param separator str : The element separator
|
||||||
# @return A SettingValidator instance
|
# @return A Validator instance
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
||||||
def list_validator(value):
|
def list_validator(value):
|
||||||
res = list()
|
res = list()
|
||||||
errors = list()
|
|
||||||
for elt in value.split(separator):
|
for elt in value.split(separator):
|
||||||
elt = elt_validator(elt)
|
elt = elt_validator(elt)
|
||||||
if len(elt) > 0:
|
if len(elt) > 0:
|
||||||
res.append(elt)
|
res.append(elt)
|
||||||
return res
|
return res
|
||||||
description = "Convert value to an array" if description is None else description
|
description = "Convert value to an array" if description is None else description
|
||||||
cls.register_validator(
|
cls.register_validator(validator_name, list_validator, description)
|
||||||
validator_name,
|
|
||||||
list_validator,
|
|
||||||
description)
|
|
||||||
return cls(validator_name)
|
return cls(validator_name)
|
||||||
|
|
||||||
##@brief Create and register a list validator which reads an array and returns a string
|
# @brief Create and register a list validator which reads an array and returns a string
|
||||||
# @param elt_validator callable : The validator that will be used for validate each elt value
|
# @param elt_validator callable : The validator that will be used for validate each elt value
|
||||||
# @param validator_name str
|
# @param validator_name str
|
||||||
# @param description None | str
|
# @param description None | str
|
||||||
# @param separator str : The element separator
|
# @param separator str : The element separator
|
||||||
# @return A SettingValidator instance
|
# @return A Validator instance
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
||||||
def write_list_validator(value):
|
def write_list_validator(value):
|
||||||
res = ''
|
res = ''
|
||||||
errors = list()
|
|
||||||
for elt in value:
|
for elt in value:
|
||||||
res += elt_validator(elt) + ','
|
res += elt_validator(elt) + ','
|
||||||
return res[:len(res) - 1]
|
return res[:len(res) - 1]
|
||||||
description = "Convert value to a string" if description is None else description
|
description = "Convert value to a string" if description is None else description
|
||||||
cls.register_validator(
|
cls.register_validator(validator_name, write_list_validator, description)
|
||||||
validator_name,
|
|
||||||
write_list_validator,
|
|
||||||
description)
|
|
||||||
return cls(validator_name)
|
return cls(validator_name)
|
||||||
|
|
||||||
##@brief Create and register a regular expression validator
|
# @brief Create and register a regular expression validator
|
||||||
# @param pattern str : regex pattern
|
# @param pattern str : regex pattern
|
||||||
# @param validator_name str : The validator name
|
# @param validator_name str : The validator name
|
||||||
# @param description str : Validator description
|
# @param description str : Validator description
|
||||||
# @return a SettingValidator instance
|
# @return a Validator instance
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_re_validator(cls, pattern, validator_name, description=None):
|
def create_re_validator(cls, pattern, validator_name, description=None):
|
||||||
def re_validator(value):
|
def re_validator(value):
|
||||||
if not re.match(pattern, value):
|
if not re.match(pattern, value):
|
||||||
raise SettingsValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
|
raise ValidationError(
|
||||||
|
"The value '%s' doesn't match the following pattern '%s'"
|
||||||
|
% pattern)
|
||||||
return value
|
return value
|
||||||
# registering the validator
|
# registering the validator
|
||||||
cls.register_validator(
|
cls.register_validator(validator_name, re_validator,
|
||||||
validator_name,
|
("Match value to '%s'" % pattern)
|
||||||
re_validator,
|
if description is None else description)
|
||||||
("Match value to '%s'" % pattern) if description is None else description)
|
|
||||||
return cls(validator_name)
|
return cls(validator_name)
|
||||||
|
|
||||||
|
# @return a list of registered validators
|
||||||
## @return a list of registered validators
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validators_list_str(cls):
|
def validators_list_str(cls):
|
||||||
result = ''
|
result = ''
|
||||||
|
|
@ -150,20 +150,26 @@ class SettingValidator(object):
|
||||||
result += "\n"
|
result += "\n"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
##@brief Integer value validator callback
|
# @brief Integer value validator callback
|
||||||
|
|
||||||
|
|
||||||
def int_val(value):
|
def int_val(value):
|
||||||
return int(value)
|
return int(value)
|
||||||
|
|
||||||
##@brief Output file validator callback
|
# @brief Output file validator callback
|
||||||
# @return A file object (if filename is '-' return sys.stderr)
|
# @return A file object (if filename is '-' return sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def file_err_output(value):
|
def file_err_output(value):
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise SettingsValidationError("A string was expected but got '%s' " % value)
|
raise ValidationError("A string was expected but got '%s' " % value)
|
||||||
if value == '-':
|
if value == '-':
|
||||||
return None
|
return None
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Boolean value validator callback
|
# @brief Boolean value validator callback
|
||||||
|
|
||||||
|
|
||||||
def boolean_val(value):
|
def boolean_val(value):
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return value
|
return value
|
||||||
|
|
@ -172,52 +178,66 @@ def boolean_val(value):
|
||||||
elif value.strip().lower() == 'false' or value.strip() == '0':
|
elif value.strip().lower() == 'false' or value.strip() == '0':
|
||||||
value = False
|
value = False
|
||||||
else:
|
else:
|
||||||
raise SettingsValidationError("A boolean was expected but got '%s' " % value)
|
raise ValidationError("A boolean was expected but got '%s' " % value)
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
|
||||||
##@brief Validate a directory path
|
# @brief Validate a directory path
|
||||||
|
|
||||||
|
|
||||||
def directory_val(value):
|
def directory_val(value):
|
||||||
res = SettingValidator('strip')(value)
|
res = Validator('strip')(value)
|
||||||
if not os.path.isdir(res):
|
if not os.path.isdir(res):
|
||||||
raise SettingsValidationError("Folowing path don't exists or is not a directory : '%s'"%res)
|
raise ValidationError("Following path don't exists or is not a directory : '%s'" % res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
##@brief Validate a loglevel value
|
# @brief Validate a loglevel value
|
||||||
|
|
||||||
|
|
||||||
def loglevel_val(value):
|
def loglevel_val(value):
|
||||||
valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
|
valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
|
||||||
if value.upper() not in valids:
|
if value.upper() not in valids:
|
||||||
raise SettingsValidationError(
|
raise ValidationError(
|
||||||
"The value '%s' is not a valid loglevel" % value)
|
"The value '%s' is not a valid loglevel" % value)
|
||||||
return value.upper()
|
return value.upper()
|
||||||
|
|
||||||
##@brief Validate a path
|
# @brief Validate a path
|
||||||
|
|
||||||
|
|
||||||
def path_val(value):
|
def path_val(value):
|
||||||
if value is None or not os.path.exists(value):
|
if value is None or not os.path.exists(value):
|
||||||
raise SettingsValidationError(
|
raise ValidationError(
|
||||||
"path '%s' doesn't exists" % value)
|
"path '%s' doesn't exists" % value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Validate None
|
# @brief Validate None
|
||||||
|
|
||||||
|
|
||||||
def none_val(value):
|
def none_val(value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
raise SettingsValidationError("This settings cannot be set in configuration file")
|
raise ValidationError("This settings cannot be set in configuration file")
|
||||||
|
|
||||||
|
# @brief Validate a string
|
||||||
|
|
||||||
|
|
||||||
##@brief Validate a string
|
|
||||||
def str_val(value):
|
def str_val(value):
|
||||||
try:
|
try:
|
||||||
return str(value)
|
return str(value)
|
||||||
except Exception as e:
|
except Exception as exp:
|
||||||
raise SettingsValidationError("Not able to convert value to string : " + str(e))
|
raise ValidationError("Can't to convert value to string: " + str(exp))
|
||||||
|
|
||||||
|
# @brief Validate using a regex
|
||||||
|
|
||||||
|
|
||||||
##@brief Validate using a regex
|
|
||||||
def regex_val(value, pattern):
|
def regex_val(value, pattern):
|
||||||
if re.match(pattern, value) is None:
|
if re.match(pattern, value) is None:
|
||||||
raise SettingsValidationError("The value '%s' is not validated by : \
|
raise ValidationError("The value '%s' is not validated by : \
|
||||||
r\"%s\"" % (value, pattern))
|
r\"%s\"" % (value, pattern))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Validate a hostname (ipv4 or ipv6)
|
# @brief Validate a hostname (ipv4 or ipv6)
|
||||||
|
|
||||||
|
|
||||||
def host_val(value):
|
def host_val(value):
|
||||||
if value == 'localhost':
|
if value == 'localhost':
|
||||||
return value
|
return value
|
||||||
|
|
@ -237,21 +257,87 @@ def host_val(value):
|
||||||
return value
|
return value
|
||||||
except (TypeError, socket.gaierror):
|
except (TypeError, socket.gaierror):
|
||||||
msg = "The value '%s' is not a valid host"
|
msg = "The value '%s' is not a valid host"
|
||||||
raise SettingsValidationError(msg % value)
|
raise ValidationError(msg % value)
|
||||||
|
|
||||||
##@brief Validator for Editorial model component
|
|
||||||
|
def custom_list_validator(value, validator_name, validator_kwargs=None):
|
||||||
|
validator_kwargs = dict() if validator_kwargs is None else validator_kwargs
|
||||||
|
validator = Validator(validator_name, **validator_kwargs)
|
||||||
|
for item in value.split():
|
||||||
|
validator(item)
|
||||||
|
return value.split()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default validators registration
|
||||||
|
#
|
||||||
|
|
||||||
|
Validator.register_validator('custom_list', custom_list_validator,
|
||||||
|
'A list validator that takes a "validator_name" as argument')
|
||||||
|
|
||||||
|
Validator.register_validator('dummy', lambda value: value, 'Validate anything')
|
||||||
|
|
||||||
|
Validator.register_validator('none', none_val, 'Validate None')
|
||||||
|
|
||||||
|
Validator.register_validator('string', str_val, 'Validate string values')
|
||||||
|
|
||||||
|
Validator.register_validator('strip', str.strip, 'String trim')
|
||||||
|
|
||||||
|
Validator.register_validator('int', int_val, 'Integer value validator')
|
||||||
|
|
||||||
|
Validator.register_validator('bool', boolean_val, 'Boolean value validator')
|
||||||
|
|
||||||
|
Validator.register_validator('errfile', file_err_output,
|
||||||
|
'Error output file validator (return stderr if filename is "-")')
|
||||||
|
|
||||||
|
Validator.register_validator('directory', directory_val,
|
||||||
|
'Directory path validator')
|
||||||
|
|
||||||
|
Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator')
|
||||||
|
|
||||||
|
Validator.register_validator('path', path_val, 'path validator')
|
||||||
|
|
||||||
|
Validator.register_validator('host', host_val, 'host validator')
|
||||||
|
|
||||||
|
Validator.register_validator('regex', regex_val,
|
||||||
|
'RegEx name validator (take re as argument)')
|
||||||
|
|
||||||
|
Validator.create_list_validator('list', Validator('strip'), description="Simple list validator. Validate a list of values separated by ','",
|
||||||
|
separator=',')
|
||||||
|
|
||||||
|
Validator.create_list_validator(
|
||||||
|
'directory_list',
|
||||||
|
Validator('directory'),
|
||||||
|
description="Validator for a list of directory path separated with ','",
|
||||||
|
separator=',')
|
||||||
|
|
||||||
|
Validator.create_write_list_validator(
|
||||||
|
'write_list',
|
||||||
|
Validator('directory'),
|
||||||
|
description="Validator for an array of values \
|
||||||
|
which will be set in a string, separated by ','",
|
||||||
|
separator=',')
|
||||||
|
|
||||||
|
Validator.create_re_validator(
|
||||||
|
r'^https?://[^\./]+.[^\./]+/?.*$',
|
||||||
|
'http_url',
|
||||||
|
'Url validator')
|
||||||
|
|
||||||
|
# @brief Validator for Editorial model component
|
||||||
#
|
#
|
||||||
# Designed to validate a conf that indicate a class.field in an EM
|
# Designed to validate a conf that indicate a class.field in an EM
|
||||||
#@todo modified the hardcoded dyncode import (it's a warning)
|
#@todo modified the hardcoded dyncode import (it's a warning)
|
||||||
|
|
||||||
|
|
||||||
def emfield_val(value):
|
def emfield_val(value):
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(),
|
||||||
'lodel.plugin.hooks': ['LodelHook']})
|
{'lodel.plugin.hooks': ['LodelHook']})
|
||||||
spl = value.split('.')
|
spl = value.split('.')
|
||||||
if len(spl) != 2:
|
if len(spl) != 2:
|
||||||
msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s"
|
msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s"
|
||||||
raise SettingsValidationError(msg % value)
|
raise SettingsValidationError(msg % value)
|
||||||
value = tuple(spl)
|
value = tuple(spl)
|
||||||
# Late validation hook
|
# Late validation hook
|
||||||
|
|
||||||
@LodelHook('lodel2_dyncode_bootstraped')
|
@LodelHook('lodel2_dyncode_bootstraped')
|
||||||
def emfield_conf_check(hookname, caller, payload):
|
def emfield_conf_check(hookname, caller, payload):
|
||||||
import leapi_dyncode as dyncode # <-- dirty & quick
|
import leapi_dyncode as dyncode # <-- dirty & quick
|
||||||
|
|
@ -265,13 +351,16 @@ def emfield_val(value):
|
||||||
raise SettingsValidationError(msg % value)
|
raise SettingsValidationError(msg % value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Validator for plugin name & optionnaly type
|
# @brief Validator for plugin name & optionnaly type
|
||||||
#
|
#
|
||||||
# Able to check that the value is a plugin and if it is of a specific type
|
# Able to check that the value is a plugin and if it is of a specific type
|
||||||
|
|
||||||
|
|
||||||
def plugin_validator(value, ptype=None):
|
def plugin_validator(value, ptype=None):
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.plugin.hooks': ['LodelHook']})
|
'lodel.plugin.hooks': ['LodelHook']})
|
||||||
value = copy.copy(value)
|
value = copy.copy(value)
|
||||||
|
|
||||||
@LodelHook('lodel2_dyncode_bootstraped')
|
@LodelHook('lodel2_dyncode_bootstraped')
|
||||||
def plugin_type_checker(hookname, caller, payload):
|
def plugin_type_checker(hookname, caller, payload):
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
|
|
@ -284,133 +373,39 @@ def plugin_validator(value, ptype = None):
|
||||||
except PluginError:
|
except PluginError:
|
||||||
msg = "No plugin named %s found"
|
msg = "No plugin named %s found"
|
||||||
msg %= value
|
msg %= value
|
||||||
raise SettingsValidationError(msg)
|
raise ValidationError(msg)
|
||||||
if plugin._type_conf_name.lower() != ptype.lower():
|
if plugin._type_conf_name.lower() != ptype.lower():
|
||||||
msg = "A plugin of type '%s' was expected but found a plugin \
|
msg = "A plugin of type '%s' was expected but found a plugin \
|
||||||
named '%s' that is a '%s' plugin"
|
named '%s' that is a '%s' plugin"
|
||||||
msg %= (ptype, value, plugin._type_conf_name)
|
msg %= (ptype, value, plugin._type_conf_name)
|
||||||
raise SettingsValidationError(msg)
|
raise ValidationError(msg)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def custom_list_validator(value, validator_name, validator_kwargs = None):
|
|
||||||
validator_kwargs = dict() if validator_kwargs is None else validator_kwargs
|
|
||||||
validator = SettingValidator(validator_name, **validator_kwargs)
|
|
||||||
for item in value.split():
|
|
||||||
validator(item)
|
|
||||||
return value.split()
|
|
||||||
|
|
||||||
#
|
Validator.register_validator(
|
||||||
# Default validators registration
|
|
||||||
#
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'plugin',
|
'plugin',
|
||||||
plugin_validator,
|
plugin_validator,
|
||||||
'plugin name & type validator')
|
'plugin name & type validator')
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
Validator.register_validator(
|
||||||
'custom_list',
|
|
||||||
custom_list_validator,
|
|
||||||
'A list validator that takes a "validator_name" as argument')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'dummy',
|
|
||||||
lambda value:value,
|
|
||||||
'Validate anything')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'none',
|
|
||||||
none_val,
|
|
||||||
'Validate None')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'string',
|
|
||||||
str_val,
|
|
||||||
'Validate string values')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'strip',
|
|
||||||
str.strip,
|
|
||||||
'String trim')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'int',
|
|
||||||
int_val,
|
|
||||||
'Integer value validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'bool',
|
|
||||||
boolean_val,
|
|
||||||
'Boolean value validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'errfile',
|
|
||||||
file_err_output,
|
|
||||||
'Error output file validator (return stderr if filename is "-")')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'directory',
|
|
||||||
directory_val,
|
|
||||||
'Directory path validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'loglevel',
|
|
||||||
loglevel_val,
|
|
||||||
'Loglevel validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'path',
|
|
||||||
path_val,
|
|
||||||
'path validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'host',
|
|
||||||
host_val,
|
|
||||||
'host validator')
|
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'emfield',
|
'emfield',
|
||||||
emfield_val,
|
emfield_val,
|
||||||
'EmField name validator')
|
'EmField name validator')
|
||||||
|
|
||||||
SettingValidator.register_validator(
|
|
||||||
'regex',
|
|
||||||
regex_val,
|
|
||||||
'RegEx name validator (take re as argument)')
|
|
||||||
|
|
||||||
SettingValidator.create_list_validator(
|
|
||||||
'list',
|
|
||||||
SettingValidator('strip'),
|
|
||||||
description = "Simple list validator. Validate a list of values separated by ','",
|
|
||||||
separator = ',')
|
|
||||||
|
|
||||||
SettingValidator.create_list_validator(
|
|
||||||
'directory_list',
|
|
||||||
SettingValidator('directory'),
|
|
||||||
description = "Validator for a list of directory path separated with ','",
|
|
||||||
separator = ',')
|
|
||||||
SettingValidator.create_write_list_validator(
|
|
||||||
'write_list',
|
|
||||||
SettingValidator('directory'),
|
|
||||||
description = "Validator for an array of values which will be set in a string, separated by ','",
|
|
||||||
separator = ',')
|
|
||||||
SettingValidator.create_re_validator(
|
|
||||||
r'^https?://[^\./]+.[^\./]+/?.*$',
|
|
||||||
'http_url',
|
|
||||||
'Url validator')
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Lodel 2 configuration specification
|
# Lodel 2 configuration specification
|
||||||
#
|
#
|
||||||
|
|
||||||
##@brief Append a piece of confspec
|
# @brief Append a piece of confspec
|
||||||
#@note orig is modified during the process
|
#@note orig is modified during the process
|
||||||
#@param orig dict : the confspec to update
|
#@param orig dict : the confspec to update
|
||||||
#@param section str : section name
|
#@param section str : section name
|
||||||
#@param key str
|
#@param key str
|
||||||
#@param validator SettingValidator : the validator to use to check this configuration key's value
|
#@param validator Validator : the validator to use to check this configuration key's value
|
||||||
#@param default
|
#@param default
|
||||||
#@return new confspec
|
#@return new confspec
|
||||||
|
|
||||||
|
|
||||||
def confspec_append(orig, section, key, validator, default):
|
def confspec_append(orig, section, key, validator, default):
|
||||||
if section not in orig:
|
if section not in orig:
|
||||||
orig[section] = dict()
|
orig[section] = dict()
|
||||||
|
|
@ -418,41 +413,33 @@ def confspec_append(orig, section, key, validator, default):
|
||||||
orig[section][key] = (default, validator)
|
orig[section][key] = (default, validator)
|
||||||
return orig
|
return orig
|
||||||
|
|
||||||
##@brief Global specifications for lodel2 settings
|
# @brief Global specifications for lodel2 settings
|
||||||
LODEL2_CONF_SPECS = {
|
LODEL2_CONF_SPECS = {
|
||||||
'lodel2': {
|
'lodel2': {
|
||||||
'debug': ( True,
|
'debug': (True, Validator('bool')),
|
||||||
SettingValidator('bool')),
|
'sitename': ('noname', Validator('strip')),
|
||||||
'sitename': ( 'noname',
|
'runtest': (False, Validator('bool')),
|
||||||
SettingValidator('strip')),
|
|
||||||
'runtest': ( False,
|
|
||||||
SettingValidator('bool')),
|
|
||||||
},
|
},
|
||||||
'lodel2.logging.*': {
|
'lodel2.logging.*': {
|
||||||
'level': ( 'ERROR',
|
'level': ('ERROR', Validator('loglevel')),
|
||||||
SettingValidator('loglevel')),
|
'context': (False, Validator('bool')),
|
||||||
'context': ( False,
|
'filename': ("-", Validator('errfile', none_is_valid=False)),
|
||||||
SettingValidator('bool')),
|
'backupcount': (5, Validator('int', none_is_valid=False)),
|
||||||
'filename': ( "-",
|
'maxbytes': (1024 * 10, Validator('int', none_is_valid=False)),
|
||||||
SettingValidator('errfile', none_is_valid = False)),
|
|
||||||
'backupcount': ( 5,
|
|
||||||
SettingValidator('int', none_is_valid = False)),
|
|
||||||
'maxbytes': ( 1024*10,
|
|
||||||
SettingValidator('int', none_is_valid = False)),
|
|
||||||
},
|
},
|
||||||
'lodel2.editorialmodel': {
|
'lodel2.editorialmodel': {
|
||||||
'emfile': ( 'em.pickle', SettingValidator('strip')),
|
'emfile': ('em.pickle', Validator('strip')),
|
||||||
'emtranslator': ( 'picklefile', SettingValidator('strip')),
|
'emtranslator': ('picklefile', Validator('strip')),
|
||||||
'dyncode': ( 'leapi_dyncode.py', SettingValidator('strip')),
|
'dyncode': ('leapi_dyncode.py', Validator('strip')),
|
||||||
'groups': ( '', SettingValidator('list')),
|
'groups': ('', Validator('list')),
|
||||||
'editormode': ( False, SettingValidator('bool')),
|
'editormode': (False, Validator('bool')),
|
||||||
},
|
},
|
||||||
'lodel2.datasources.*': {
|
'lodel2.datasources.*': {
|
||||||
'read_only': (False, SettingValidator('bool')),
|
'read_only': (False, Validator('bool')),
|
||||||
'identifier': ( None, SettingValidator('string')),
|
'identifier': (None, Validator('string')),
|
||||||
},
|
},
|
||||||
'lodel2.auth': {
|
'lodel2.auth': {
|
||||||
'login_classfield': ('user.login', SettingValidator('emfield')),
|
'login_classfield': ('user.login', Validator('emfield')),
|
||||||
'pass_classfield': ('user.password', SettingValidator('emfield')),
|
'pass_classfield': ('user.password', Validator('emfield')),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#-*- coding: utf-8 -*-
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
##@brief Loader for tests which do not need an lodel installation
|
# @brief Loader for tests which do not need an lodel installation
|
||||||
#
|
#
|
||||||
# Options
|
# Options
|
||||||
################
|
################
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, os, os.path
|
import sys
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
import sys
|
import sys
|
||||||
import os, os.path
|
import os, os.path
|
||||||
sys.path.append(os.path.dirname(os.getcwd()+'/..'))
|
sys.path.append(os.path.dirname(os.getcwd()+'/..'))
|
||||||
from lodel.settings.validator import SettingValidator
|
from lodel.validator.validator import Validator
|
||||||
print(SettingValidator.validators_list_str())
|
print(Validator.validators_list_str())
|
||||||
|
|
|
||||||
|
|
@ -5,52 +5,52 @@ from unittest import mock
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from lodel.exceptions import *
|
from lodel.exceptions import *
|
||||||
from lodel.settings.validator import *
|
from lodel.validator.validator import *
|
||||||
|
|
||||||
class SettingValidatorTestCase(unittest.TestCase):
|
class ValidatorTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_init_basic(self):
|
def test_init_basic(self):
|
||||||
""" Testing the SettingsValidator class instanciation"""
|
""" Testing the SettingsValidator class instanciation"""
|
||||||
valid = SettingValidator('string')
|
valid = Validator('string')
|
||||||
#trying to call it
|
#trying to call it
|
||||||
valid('test')
|
valid('test')
|
||||||
|
|
||||||
def test_init_badname(self):
|
def test_init_badname(self):
|
||||||
""" Testing SettingValidator instanciation with non existing validator
|
""" Testing Validator instanciation with non existing validator
|
||||||
name"""
|
name"""
|
||||||
with self.assertRaises(LodelFatalError):
|
with self.assertRaises(LodelFatalError):
|
||||||
SettingValidator('qklfhsdufgsdyfugigsdfsdlcknsdp')
|
Validator('qklfhsdufgsdyfugigsdfsdlcknsdp')
|
||||||
|
|
||||||
def test_noneswitch(self):
|
def test_noneswitch(self):
|
||||||
""" Testing the none_is_valid switch given at validator instanciation
|
""" Testing the none_is_valid switch given at validator instanciation
|
||||||
"""
|
"""
|
||||||
none_invalid = SettingValidator('int')
|
none_invalid = Validator('int')
|
||||||
none_valid = SettingValidator('int', none_is_valid = True)
|
none_valid = Validator('int', none_is_valid = True)
|
||||||
|
|
||||||
none_valid(None)
|
none_valid(None)
|
||||||
with self.assertRaises(SettingsValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
none_invalid(None)
|
none_invalid(None)
|
||||||
|
|
||||||
def test_validator_registration(self):
|
def test_validator_registration(self):
|
||||||
""" Testing the register_validator method of SettingValidator """
|
""" Testing the register_validator method of Validator """
|
||||||
mockfun = mock.MagicMock()
|
mockfun = mock.MagicMock()
|
||||||
vname = 'lfkjdshfkuhsdygsuuyfsduyf'
|
vname = 'lfkjdshfkuhsdygsuuyfsduyf'
|
||||||
testval = 'foo'
|
testval = 'foo'
|
||||||
SettingValidator.register_validator(vname, mockfun, 'test validator')
|
Validator.register_validator(vname, mockfun, 'test validator')
|
||||||
#Using registered validator
|
#Using registered validator
|
||||||
valid = SettingValidator(vname)
|
valid = Validator(vname)
|
||||||
valid(testval)
|
valid(testval)
|
||||||
mockfun.assert_called_once_with(testval)
|
mockfun.assert_called_once_with(testval)
|
||||||
|
|
||||||
def test_validator_optargs_forwarding(self):
|
def test_validator_optargs_forwarding(self):
|
||||||
""" Testing the ability for SettingValidator to forward optional
|
""" Testing the ability for Validator to forward optional
|
||||||
arguments """
|
arguments """
|
||||||
mockfun = mock.MagicMock()
|
mockfun = mock.MagicMock()
|
||||||
vname = 'lkjdsfhsdiufhisduguig'
|
vname = 'lkjdsfhsdiufhisduguig'
|
||||||
testval = 'azertyuiop'
|
testval = 'azertyuiop'
|
||||||
SettingValidator.register_validator(vname, mockfun, 'test validator')
|
Validator.register_validator(vname, mockfun, 'test validator')
|
||||||
#Using registered validator with more arguments
|
#Using registered validator with more arguments
|
||||||
valid = SettingValidator(vname,
|
valid = Validator(vname,
|
||||||
arga = 'a', argb = 42, argc = '1337')
|
arga = 'a', argb = 42, argc = '1337')
|
||||||
valid(testval)
|
valid(testval)
|
||||||
mockfun.assert_called_once_with(
|
mockfun.assert_called_once_with(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue