mirror of
https://github.com/yweber/lodel2.git
synced 2025-11-02 04:20:55 +01:00
Only indentation and stuff like that
This commit is contained in:
parent
a1afe3b48d
commit
3b4494cb7a
8 changed files with 406 additions and 349 deletions
|
|
@ -1,6 +1,6 @@
|
|||
#-*- coding: utf-8 -*-
|
||||
|
||||
##@package lodel.editorial_model.components
|
||||
# @package lodel.editorial_model.components
|
||||
#@brief Defines all @ref lodel2_em "EM" components
|
||||
#@ingroup lodel2_em
|
||||
|
||||
|
|
@ -17,23 +17,25 @@ LodelContext.expose_modules(globals(), {
|
|||
'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'],
|
||||
'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
|
||||
# @todo forbid '.' in uid
|
||||
#@ingroup lodel2_em
|
||||
|
||||
|
||||
class EmComponent(MlNamedObject):
|
||||
|
||||
##@brief Instanciate an EmComponent
|
||||
|
||||
# @brief Instanciate an EmComponent
|
||||
# @param uid str : uniq identifier
|
||||
# @param display_name MlString|str|dict : component display_name
|
||||
# @param help_text MlString|str|dict : help_text
|
||||
def __init__(self, uid, display_name = None, help_text = None, group = None):
|
||||
def __init__(self, uid, display_name=None, help_text=None, group=None):
|
||||
if self.__class__ == EmComponent:
|
||||
raise NotImplementedError('EmComponent is an abstract class')
|
||||
self.uid = uid
|
||||
self.group = group
|
||||
super().__init__(display_name, help_text)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
if self.display_name is None:
|
||||
return str(self.uid)
|
||||
|
|
@ -42,34 +44,34 @@ class EmComponent(MlNamedObject):
|
|||
def d_hash(self):
|
||||
m = hashlib.md5()
|
||||
for data in (
|
||||
self.uid,
|
||||
'NODISPNAME' if self.display_name is None else str(self.display_name.d_hash()),
|
||||
'NOHELP' if self.help_text is None else str(self.help_text.d_hash()),
|
||||
'NOGROUP' if self.group is None else str(self.group.d_hash()),
|
||||
self.uid,
|
||||
'NODISPNAME' if self.display_name is None else str(self.display_name.d_hash()),
|
||||
'NOHELP' if self.help_text is None else str(self.help_text.d_hash()),
|
||||
'NOGROUP' if self.group is None else str(self.group.d_hash()),
|
||||
):
|
||||
m.update(bytes(data, 'utf-8'))
|
||||
return int.from_bytes(m.digest(), byteorder='big')
|
||||
|
||||
|
||||
##@brief Handles editorial model objects classes
|
||||
# @brief Handles editorial model objects classes
|
||||
#@ingroup lodel2_em
|
||||
class EmClass(EmComponent):
|
||||
|
||||
##@brief Instanciate a new EmClass
|
||||
|
||||
# @brief Instanciate a new EmClass
|
||||
#@param uid str : uniq identifier
|
||||
#@param display_name MlString|str|dict : component display_name
|
||||
#@param abstract bool : set the class as asbtract if True
|
||||
#@param pure_abstract bool : if True the EmClass will not be represented in
|
||||
#leapi dyncode
|
||||
# leapi dyncode
|
||||
#@param parents list: parent EmClass list or uid list
|
||||
#@param help_text MlString|str|dict : help_text
|
||||
#@param datasources str|tuple|list : The datasource name ( see
|
||||
#@param datasources str|tuple|list : The datasource name ( see
|
||||
#@ref lodel2_datasources ) or two names (first is read_only datasource the
|
||||
#second is read write)
|
||||
# second is read write)
|
||||
def __init__(
|
||||
self, uid, display_name = None, help_text = None, abstract = False,
|
||||
parents = None, group = None, pure_abstract = False,
|
||||
datasources = 'default'):
|
||||
self, uid, display_name=None, help_text=None, abstract=False,
|
||||
parents=None, group=None, pure_abstract=False,
|
||||
datasources='default'):
|
||||
|
||||
super().__init__(uid, display_name, help_text, group)
|
||||
self.abstract = bool(abstract)
|
||||
|
|
@ -85,49 +87,50 @@ class EmClass(EmComponent):
|
|||
parents = [parents]
|
||||
for parent in parents:
|
||||
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:
|
||||
parents = list()
|
||||
self.parents = parents
|
||||
##@brief Stores EmFields instances indexed by field uid
|
||||
self.__fields = dict()
|
||||
|
||||
# @brief Stores EmFields instances indexed by field uid
|
||||
self.__fields = dict()
|
||||
|
||||
self.group = group
|
||||
if group is None:
|
||||
warnings.warn("NO GROUP FOR EMCLASS %s" % uid)
|
||||
else:
|
||||
group.add_components([self])
|
||||
|
||||
#Adding common field
|
||||
|
||||
# Adding common field
|
||||
if not self.abstract:
|
||||
self.new_field(
|
||||
CLASS_ID_FIELDNAME,
|
||||
display_name = {
|
||||
display_name={
|
||||
'eng': "LeObject subclass identifier",
|
||||
'fre': "Identifiant de la class fille de LeObject"},
|
||||
help_text = {
|
||||
help_text={
|
||||
'eng': "Allow to create instance of the good class when\
|
||||
fetching arbitrary datas from DB"},
|
||||
data_handler = 'LeobjectSubclassIdentifier',
|
||||
internal = True,
|
||||
group = group)
|
||||
data_handler='LeobjectSubclassIdentifier',
|
||||
internal=True,
|
||||
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
|
||||
@property
|
||||
def __all_fields(self):
|
||||
res = dict()
|
||||
for pfields in [ p.__all_fields for p in self.parents]:
|
||||
for pfields in [p.__all_fields for p in self.parents]:
|
||||
res.update(pfields)
|
||||
res.update(self.__fields)
|
||||
return res
|
||||
|
||||
##@brief RO access to datasource attribute
|
||||
|
||||
# @brief RO access to datasource attribute
|
||||
@property
|
||||
def datasource(self):
|
||||
return self.__datasource
|
||||
|
||||
##@brief Return the list of all dependencies
|
||||
# @brief Return the list of all dependencies
|
||||
#
|
||||
# Reccursive parents listing
|
||||
@property
|
||||
|
|
@ -140,29 +143,29 @@ class EmClass(EmComponent):
|
|||
res |= parent.parents_recc
|
||||
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 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
|
||||
# @todo use Settings.editorialmodel.groups to determine wich fields should be returned
|
||||
def fields(self, uid = None, no_parents = False):
|
||||
def fields(self, uid=None, no_parents=False):
|
||||
fields = self.__fields if no_parents else self.__all_fields
|
||||
try:
|
||||
return list(fields.values()) if uid is None else fields[uid]
|
||||
except KeyError:
|
||||
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):
|
||||
if not Settings.editorialmodel.editormode:
|
||||
active_fields = []
|
||||
for grp_name, agrp in active_groups.items():
|
||||
active_fields += [ emc for emc in agrp.components()
|
||||
if isinstance(emc, EmField)]
|
||||
self.__fields = { fname:fdh for fname, fdh in self.__fields.items()
|
||||
if fdh in active_fields }
|
||||
active_fields += [emc for emc in agrp.components()
|
||||
if isinstance(emc, EmField)]
|
||||
self.__fields = {fname: fdh for fname, fdh in self.__fields.items()
|
||||
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
|
||||
# @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)
|
||||
|
|
@ -170,38 +173,40 @@ class EmClass(EmComponent):
|
|||
def add_field(self, emfield):
|
||||
assert_edit()
|
||||
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
|
||||
if emfield.uid in self.__all_fields:
|
||||
parent_field = self.__all_fields[emfield.uid]
|
||||
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
|
||||
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 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__() )
|
||||
def new_field(self, uid, data_handler, **field_kwargs):
|
||||
assert_edit()
|
||||
return self.add_field(EmField(uid, data_handler, self, **field_kwargs))
|
||||
|
||||
def d_hash(self):
|
||||
m = hashlib.md5()
|
||||
payload = str(super().d_hash()) + ("1" if self.abstract else "0")
|
||||
payload = str(super().d_hash()) + ("1" if self.abstract else "0")
|
||||
|
||||
for p in sorted(self.parents):
|
||||
payload += str(p.d_hash())
|
||||
for fuid in sorted(self.__fields.keys()):
|
||||
payload += str(self.__fields[fuid].d_hash())
|
||||
|
||||
|
||||
m.update(bytes(payload, 'utf-8'))
|
||||
return int.from_bytes(m.digest(), byteorder='big')
|
||||
|
||||
def __str__(self):
|
||||
return "<class EmClass %s>" % self.uid
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
if not self.abstract:
|
||||
abstract = ''
|
||||
|
|
@ -209,80 +214,82 @@ class EmClass(EmComponent):
|
|||
abstract = 'PureAbstract'
|
||||
else:
|
||||
abstract = 'Abstract'
|
||||
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
|
||||
class EmField(EmComponent):
|
||||
|
||||
##@brief Instanciate a new EmField
|
||||
# @brief Instanciate a new EmField
|
||||
# @param uid str : uniq identifier
|
||||
# @param display_name MlString|str|dict : field display_name
|
||||
# @param data_handler str : A DataHandler name
|
||||
# @param help_text MlString|str|dict : help text
|
||||
# @param group EmGroup :
|
||||
# @param **handler_kwargs : data handler arguments
|
||||
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
|
||||
super().__init__(uid, display_name, help_text, group)
|
||||
##@brief The data handler name
|
||||
# @brief The data handler name
|
||||
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)
|
||||
##@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)
|
||||
##@brief Stores data handler instanciation options
|
||||
# @brief Stores data handler instanciation options
|
||||
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
|
||||
if self._emclass is None:
|
||||
warnings.warn("No EmClass for field %s" %uid)
|
||||
warnings.warn("No EmClass for field %s" % uid)
|
||||
if group is None:
|
||||
warnings.warn("No EmGroup for field %s" % uid)
|
||||
else:
|
||||
group.add_components([self])
|
||||
|
||||
##@brief Returns data_handler_name attribute
|
||||
# @brief Returns data_handler_name attribute
|
||||
def get_data_handler_name(self):
|
||||
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):
|
||||
return copy.copy(self.data_handler_cls)
|
||||
|
||||
|
||||
##@brief Returne the uid of the emclass which contains this field
|
||||
def get_emclass_uid(self):
|
||||
return self._emclass.uid
|
||||
|
||||
|
||||
# @warning Not complete !
|
||||
# @todo Complete the hash when data handlers becomes available
|
||||
def d_hash(self):
|
||||
return int.from_bytes(hashlib.md5(
|
||||
bytes(
|
||||
"%s%s%s" % ( super().d_hash(),
|
||||
self.data_handler_name,
|
||||
self.data_handler_options),
|
||||
'utf-8')
|
||||
bytes(
|
||||
"%s%s%s" % (super().d_hash(),
|
||||
self.data_handler_name,
|
||||
self.data_handler_options),
|
||||
'utf-8')
|
||||
).digest(), byteorder='big')
|
||||
|
||||
##@brief Handles functionnal group of EmComponents
|
||||
# @brief Handles functionnal group of EmComponents
|
||||
#@ingroup lodel2_em
|
||||
|
||||
|
||||
class EmGroup(MlNamedObject):
|
||||
|
||||
##@brief Create a new EmGroup
|
||||
|
||||
# @brief Create a new EmGroup
|
||||
# @note you should NEVER call the constructor yourself. Use Model.add_group instead
|
||||
# @param uid str : Uniq identifier
|
||||
# @param depends list : A list of EmGroup dependencies
|
||||
# @param display_name MlString|str :
|
||||
# @param help_text MlString|str :
|
||||
def __init__(self, uid, depends = None, display_name = None, help_text = None):
|
||||
# @param display_name MlString|str :
|
||||
# @param help_text MlString|str :
|
||||
def __init__(self, uid, depends=None, display_name=None, help_text=None):
|
||||
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()
|
||||
##@brief Stores the list of dependencies (EmGroup) indexed by uid
|
||||
# @brief Stores the list of dependencies (EmGroup) indexed by uid
|
||||
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()
|
||||
super().__init__(display_name, help_text)
|
||||
|
||||
|
|
@ -291,11 +298,11 @@ class EmGroup(MlNamedObject):
|
|||
if not isinstance(grp, EmGroup):
|
||||
raise ValueError("EmGroup expected in depends argument but %s found" % grp)
|
||||
self.add_dependencie(grp)
|
||||
|
||||
##@brief Returns EmGroup dependencie
|
||||
|
||||
# @brief Returns EmGroup dependencie
|
||||
# @param recursive bool : if True return all dependencies and their dependencies
|
||||
# @return a dict of EmGroup identified by uid
|
||||
def dependencies(self, recursive = False):
|
||||
def dependencies(self, recursive=False):
|
||||
res = copy.copy(self.require)
|
||||
if not recursive:
|
||||
return res
|
||||
|
|
@ -307,11 +314,11 @@ class EmGroup(MlNamedObject):
|
|||
to_scan.append(new_dep)
|
||||
res[new_dep.uid] = new_dep
|
||||
return res
|
||||
|
||||
##@brief Returns EmGroup applicants
|
||||
|
||||
# @brief Returns EmGroup applicants
|
||||
# @param recursive bool : if True return all dependencies and their dependencies
|
||||
# @returns a dict of EmGroup identified by uid
|
||||
def applicants(self, recursive = False):
|
||||
def applicants(self, recursive=False):
|
||||
res = copy.copy(self.required_by)
|
||||
if not recursive:
|
||||
return res
|
||||
|
|
@ -323,29 +330,31 @@ class EmGroup(MlNamedObject):
|
|||
to_scan.append(new_app)
|
||||
res[new_app.uid] = new_app
|
||||
return res
|
||||
|
||||
##@brief Returns EmGroup components
|
||||
|
||||
# @brief Returns EmGroup components
|
||||
# @returns a copy of the set of components
|
||||
def components(self):
|
||||
return (self.__components).copy()
|
||||
|
||||
##@brief Returns EmGroup display_name
|
||||
# @brief Returns EmGroup display_name
|
||||
# @param lang str | None : If None return default lang translation
|
||||
# @returns None if display_name is None, a str for display_name else
|
||||
def get_display_name(self, lang=None):
|
||||
name=self.display_name
|
||||
if name is None : return None
|
||||
return name.get(lang);
|
||||
name = self.display_name
|
||||
if name is None:
|
||||
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
|
||||
# @returns None if display_name is None, a str for display_name else
|
||||
def get_help_text(self, lang=None):
|
||||
help=self.help_text
|
||||
if help is None : return None
|
||||
return help.get(lang);
|
||||
|
||||
##@brief Add components in a group
|
||||
help = self.help_text
|
||||
if help is None:
|
||||
return None
|
||||
return help.get(lang)
|
||||
|
||||
# @brief Add components in a group
|
||||
# @param components list : EmComponent instances list
|
||||
def add_components(self, components):
|
||||
assert_edit()
|
||||
|
|
@ -356,10 +365,11 @@ class EmGroup(MlNamedObject):
|
|||
msg %= (component, self)
|
||||
warnings.warn(msg)
|
||||
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)
|
||||
|
||||
##@brief Add a dependencie
|
||||
# @brief Add a dependencie
|
||||
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
||||
def add_dependencie(self, grp):
|
||||
assert_edit()
|
||||
|
|
@ -367,16 +377,17 @@ class EmGroup(MlNamedObject):
|
|||
for group in grp:
|
||||
self.add_dependencie(group)
|
||||
return
|
||||
except TypeError: pass
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
if grp.uid in self.require:
|
||||
return
|
||||
if self.__circular_dependencie(grp):
|
||||
raise EditorialModelError("Circular dependencie detected, cannot add dependencie")
|
||||
self.require[grp.uid] = grp
|
||||
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
|
||||
# Useless ???
|
||||
def add_applicant(self, grp):
|
||||
|
|
@ -385,26 +396,27 @@ class EmGroup(MlNamedObject):
|
|||
for group in grp:
|
||||
self.add_applicant(group)
|
||||
return
|
||||
except TypeError: pass
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
if grp.uid in self.required_by:
|
||||
return
|
||||
if self.__circular_applicant(grp):
|
||||
raise EditorialModelError("Circular applicant detected, cannot add applicant")
|
||||
self.required_by[grp.uid] = grp
|
||||
grp.require[self.uid] = self
|
||||
|
||||
##@brief Search for circular dependencie
|
||||
|
||||
# @brief Search for circular dependencie
|
||||
# @return True if circular dep found else False
|
||||
def __circular_dependencie(self, new_dep):
|
||||
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
|
||||
def __circular_applicant(self, new_app):
|
||||
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
|
||||
def __str__(self):
|
||||
if self.display_name is None:
|
||||
|
|
@ -413,11 +425,11 @@ class EmGroup(MlNamedObject):
|
|||
return self.display_name.get()
|
||||
|
||||
def d_hash(self):
|
||||
|
||||
|
||||
payload = "%s%s%s" % (
|
||||
self.uid,
|
||||
'NODNAME' if self.display_name is None else self.display_name.d_hash(),
|
||||
'NOHELP' if self.help_text is None else self.help_text.d_hash()
|
||||
self.uid,
|
||||
'NODNAME' if self.display_name is None else self.display_name.d_hash(),
|
||||
'NOHELP' if self.help_text is None else self.help_text.d_hash()
|
||||
)
|
||||
for recurs in (False, True):
|
||||
deps = self.dependencies(recurs)
|
||||
|
|
@ -426,11 +438,11 @@ class EmGroup(MlNamedObject):
|
|||
for req_by_uid in self.required_by:
|
||||
payload += req_by_uid
|
||||
return int.from_bytes(
|
||||
bytes(payload, 'utf-8'),
|
||||
byteorder = 'big'
|
||||
bytes(payload, 'utf-8'),
|
||||
byteorder='big'
|
||||
)
|
||||
|
||||
##@brief Complete string representation of an EmGroup
|
||||
|
||||
# @brief Complete string representation of an EmGroup
|
||||
# @return a string
|
||||
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)]))
|
||||
|
|
|
|||
|
|
@ -15,23 +15,23 @@ LodelContext.expose_modules(globals(), {
|
|||
'lodel.editorial_model.components': ['EmClass', 'EmField', 'EmGroup']})
|
||||
|
||||
|
||||
##@brief Describe an editorial model
|
||||
# @brief Describe an editorial model
|
||||
#@ingroup lodel2_em
|
||||
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 description MlString|str|dict : the editorial model description
|
||||
def __init__(self, name, description = None, display_name = None, help_text = None):
|
||||
def __init__(self, name, description=None, display_name=None, help_text=None):
|
||||
self.name = MlString(name)
|
||||
self.description = MlString(description)
|
||||
##@brief Stores all groups indexed by id
|
||||
# @brief Stores all groups indexed by id
|
||||
self.__groups = dict()
|
||||
##@brief Stores all classes indexed by id
|
||||
# @brief Stores all classes indexed by id
|
||||
self.__classes = dict()
|
||||
## @brief Stores all activated groups indexed by id
|
||||
# @brief Stores all activated groups indexed by id
|
||||
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.__set_actives()
|
||||
if display_name is None:
|
||||
|
|
@ -39,10 +39,10 @@ class EditorialModel(MlNamedObject):
|
|||
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
|
||||
def all_classes(self, uid = None):
|
||||
def all_classes(self, uid=None):
|
||||
if uid is None:
|
||||
return copy.copy(self.__classes)
|
||||
else:
|
||||
|
|
@ -50,8 +50,8 @@ class EditorialModel(MlNamedObject):
|
|||
return copy.copy(self.__classes[uid])
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmClass not found : '%s'" % uid)
|
||||
|
||||
def all_classes_ref(self, uid = None):
|
||||
|
||||
def all_classes_ref(self, uid=None):
|
||||
if uid is None:
|
||||
return self.__classes
|
||||
else:
|
||||
|
|
@ -59,16 +59,15 @@ class EditorialModel(MlNamedObject):
|
|||
return self.__classes[uid]
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||
|
||||
##@brief active EmClass uids accessor
|
||||
|
||||
# @brief active EmClass uids accessor
|
||||
#@return a list of class uids
|
||||
def active_classes_uids(self):
|
||||
return list(self.__active_classes.keys())
|
||||
|
||||
|
||||
##@brief EmGroups accessor
|
||||
return list(self.__active_classes.keys())
|
||||
|
||||
# @brief EmGroups accessor
|
||||
#@return a dict of groups
|
||||
def all_groups(self, uid = None):
|
||||
def all_groups(self, uid=None):
|
||||
if uid is None:
|
||||
return copy.copy(self.__groups)
|
||||
else:
|
||||
|
|
@ -76,10 +75,10 @@ class EditorialModel(MlNamedObject):
|
|||
return copy.copy(self.__groups[uid])
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||
|
||||
##@brief EmGroups accessor
|
||||
|
||||
# @brief EmGroups accessor
|
||||
#@return a dict of groups
|
||||
def all_groups_ref(self, uid = None):
|
||||
def all_groups_ref(self, uid=None):
|
||||
if uid is None:
|
||||
return self.__groups
|
||||
else:
|
||||
|
|
@ -87,26 +86,26 @@ class EditorialModel(MlNamedObject):
|
|||
return self.__groups[uid]
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||
|
||||
##@brief active EmClass uids accessor
|
||||
|
||||
# @brief active EmClass uids accessor
|
||||
#@return a list of class uids
|
||||
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
|
||||
#@return if uid is given returns an EmClass else returns an EmClass
|
||||
# iterator
|
||||
#@todo use Settings.editorialmodel.groups to determine wich classes should
|
||||
# be returned
|
||||
def classes(self, uid = None):
|
||||
def classes(self, uid=None):
|
||||
try:
|
||||
return self.__elt_getter( self.__active_classes,
|
||||
uid)
|
||||
return self.__elt_getter(self.__active_classes,
|
||||
uid)
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmClass not found : '%s'" % uid)
|
||||
|
||||
##@brief EmClass child list accessor
|
||||
|
||||
# @brief EmClass child list accessor
|
||||
#@param uid str : the EmClass uid
|
||||
#@return a set of EmClass
|
||||
def get_class_childs(self, uid):
|
||||
|
|
@ -117,24 +116,23 @@ class EditorialModel(MlNamedObject):
|
|||
res.append(cls)
|
||||
return set(res)
|
||||
|
||||
|
||||
##@brief EmGroup getter
|
||||
# @brief EmGroup getter
|
||||
# @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
|
||||
def groups(self, uid = None):
|
||||
def groups(self, uid=None):
|
||||
try:
|
||||
return self.__elt_getter( self.__active_groups,
|
||||
uid)
|
||||
return self.__elt_getter(self.__active_groups,
|
||||
uid)
|
||||
except KeyError:
|
||||
raise EditorialModelException("EmGroup not found : '%s'" % uid)
|
||||
|
||||
##@brief Private getter for __groups or __classes
|
||||
|
||||
# @brief Private getter for __groups or __classes
|
||||
# @see classes() groups()
|
||||
def __elt_getter(self, elts, uid):
|
||||
return list(elts.values()) if uid is None else elts[uid]
|
||||
|
||||
##@brief Update the EditorialModel.__active_groups and
|
||||
#EditorialModel.__active_classes attibutes
|
||||
|
||||
# @brief Update the EditorialModel.__active_groups and
|
||||
# EditorialModel.__active_classes attibutes
|
||||
def __set_actives(self):
|
||||
if Settings.editorialmodel.editormode:
|
||||
logger.warning("All EM groups active because editormode in ON")
|
||||
|
|
@ -142,7 +140,7 @@ class EditorialModel(MlNamedObject):
|
|||
self.__active_groups = self.__groups
|
||||
self.__active_classes = self.__classes
|
||||
else:
|
||||
#determine groups first
|
||||
# determine groups first
|
||||
self.__active_groups = dict()
|
||||
self.__active_classes = dict()
|
||||
for agrp in Settings.editorialmodel.groups:
|
||||
|
|
@ -159,13 +157,13 @@ class EditorialModel(MlNamedObject):
|
|||
raise RuntimeError("No active class found. Abording")
|
||||
for clsname, acls in self.__active_classes.items():
|
||||
acls._set_active_fields(self.__active_groups)
|
||||
|
||||
##@brief EmField getter
|
||||
|
||||
# @brief EmField getter
|
||||
# @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
|
||||
# @return Fals or an EmField instance
|
||||
#
|
||||
# @todo delete it, useless...
|
||||
def field(self, uid = None):
|
||||
def field(self, uid=None):
|
||||
spl = uid.split('.')
|
||||
if len(spl) != 2:
|
||||
raise ValueError("Malformed EmField identifier : '%s'" % uid)
|
||||
|
|
@ -181,7 +179,7 @@ class EditorialModel(MlNamedObject):
|
|||
pass
|
||||
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
|
||||
# @return emclass
|
||||
def add_class(self, emclass):
|
||||
|
|
@ -193,7 +191,7 @@ class EditorialModel(MlNamedObject):
|
|||
self.__classes[emclass.uid] = 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
|
||||
# @return emgroup
|
||||
def add_group(self, emgroup):
|
||||
|
|
@ -205,15 +203,15 @@ class EditorialModel(MlNamedObject):
|
|||
self.__groups[emgroup.uid] = 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 **kwargs : EmClass constructor options (
|
||||
#@param **kwargs : EmClass constructor options (
|
||||
# see @ref lodel.editorial_model.component.EmClass.__init__() )
|
||||
def new_class(self, uid, **kwargs):
|
||||
assert_edit()
|
||||
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 *kwargs : EmGroup constructor keywords arguments (
|
||||
# see @ref lodel.editorial_model.component.EmGroup.__init__() )
|
||||
|
|
@ -221,7 +219,7 @@ class EditorialModel(MlNamedObject):
|
|||
assert_edit()
|
||||
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_args
|
||||
def save(self, translator, **translator_kwargs):
|
||||
|
|
@ -229,14 +227,15 @@ class EditorialModel(MlNamedObject):
|
|||
if isinstance(translator, str):
|
||||
translator = self.translator_from_name(translator)
|
||||
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
|
||||
def raise_if_ro():
|
||||
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_args
|
||||
@classmethod
|
||||
|
|
@ -247,7 +246,7 @@ class EditorialModel(MlNamedObject):
|
|||
res.__set_actives()
|
||||
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
|
||||
# @return the translator python module
|
||||
# @throw NameError if the translator does not exists
|
||||
|
|
@ -259,12 +258,12 @@ class EditorialModel(MlNamedObject):
|
|||
except ImportError:
|
||||
raise NameError("No translator named %s")
|
||||
return mod
|
||||
|
||||
##@brief Lodel hash
|
||||
|
||||
# @brief Lodel hash
|
||||
def d_hash(self):
|
||||
payload = "%s%s" % (
|
||||
self.name,
|
||||
'NODESC' if self.description is None else self.description.d_hash()
|
||||
self.name,
|
||||
'NODESC' if self.description is None else self.description.d_hash()
|
||||
)
|
||||
for guid in sorted(self.__groups):
|
||||
payload += str(self.__groups[guid].d_hash())
|
||||
|
|
@ -273,7 +272,6 @@ class EditorialModel(MlNamedObject):
|
|||
payload += str(self.__classes[cuid].d_hash())
|
||||
|
||||
return int.from_bytes(
|
||||
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
||||
byteorder='big'
|
||||
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
||||
byteorder='big'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ from lodel.context import LodelContext
|
|||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.utils.mlstring': ['MlString']})
|
||||
|
||||
## @package lodel.mlnamedobject Lodel2 description of objects module
|
||||
# @package lodel.mlnamedobject Lodel2 description of objects module
|
||||
#
|
||||
# Display name and Description of a lodel2 object
|
||||
|
||||
##@brief Class allows dislpay name and help text for lodel2 objects and fields
|
||||
# @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):
|
||||
|
|
|
|||
|
|
@ -5,40 +5,42 @@ import os
|
|||
import configparser
|
||||
import copy
|
||||
import warnings
|
||||
import types # for dynamic bindings
|
||||
import types # for dynamic bindings
|
||||
from collections import namedtuple
|
||||
|
||||
from lodel.context import LodelContext
|
||||
|
||||
LodelContext.expose_modules(globals(),{
|
||||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.logger': 'logger',
|
||||
'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
|
||||
'lodel.validator.validator': ['Validator', 'LODEL2_CONF_SPECS',
|
||||
'confspec_append'],
|
||||
'lodel.settings.settings_loader':['SettingsLoader']})
|
||||
|
||||
'confspec_append'],
|
||||
'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
|
||||
|
||||
##@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(
|
||||
|
||||
major = sys.version_info.major,
|
||||
minor = sys.version_info.minor)
|
||||
major=sys.version_info.major,
|
||||
minor=sys.version_info.minor)
|
||||
|
||||
|
||||
class MetaSettings(type):
|
||||
|
||||
@property
|
||||
def s(self):
|
||||
self.singleton_assert(True)
|
||||
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
|
||||
#
|
||||
#
|
||||
# @par Basic instance usage
|
||||
# For example if a file defines confs like :
|
||||
# <pre>
|
||||
|
|
@ -50,15 +52,15 @@ class MetaSettings(type):
|
|||
#
|
||||
# @par Init sequence
|
||||
# The initialization sequence is a bit tricky. In fact, plugins adds allowed
|
||||
# configuration sections/values, but the list of plugins to load are in... the
|
||||
# configuration sections/values, but the list of plugins to load are in... the
|
||||
# settings.
|
||||
# Here is the conceptual presentation of Settings class initialization stages :
|
||||
# -# Preloading (sets values like lodel2 library path or the plugins path)
|
||||
# -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all
|
||||
#configurations files
|
||||
# -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all
|
||||
# configurations files
|
||||
# -# Fetch the list of plugins in the loaded settings
|
||||
# -# Merge plugins settings specification with the global lodel settings
|
||||
#specs ( see @ref lodel.plugin )
|
||||
# -# Merge plugins settings specification with the global lodel settings
|
||||
# specs ( see @ref lodel.plugin )
|
||||
# -# Fetch all settings from the merged settings specs
|
||||
#
|
||||
# @par Init sequence in practical
|
||||
|
|
@ -68,39 +70,41 @@ class MetaSettings(type):
|
|||
# -# @ref Settings.__populate_from_specs() (step 5)
|
||||
# -# And finally @ref Settings.__confs_to_namedtuple()
|
||||
#
|
||||
# @todo handles default sections for variable sections (sections ending with
|
||||
# @todo handles default sections for variable sections (sections ending with
|
||||
# '.*')
|
||||
# @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)
|
||||
|
||||
|
||||
class Settings(object, metaclass=MetaSettings):
|
||||
|
||||
## @brief Stores the singleton instance
|
||||
# @brief Stores the singleton instance
|
||||
instance = None
|
||||
|
||||
## @brief Instanciate the Settings singleton
|
||||
|
||||
# @brief Instanciate the Settings singleton
|
||||
# @param conf_dir str : The configuration directory
|
||||
#@param custom_confspecs None | dict : if given overwrite default lodel2
|
||||
#confspecs
|
||||
def __init__(self, conf_dir, custom_confspecs = None):
|
||||
self.singleton_assert() # check that it is the only instance
|
||||
# confspecs
|
||||
def __init__(self, conf_dir, custom_confspecs=None):
|
||||
self.singleton_assert() # check that it is the only instance
|
||||
Settings.instance = self
|
||||
## @brief Configuration specification
|
||||
# @brief Configuration specification
|
||||
#
|
||||
# Initialized by Settings.__bootstrap() method
|
||||
self.__conf_specs = custom_confspecs
|
||||
## @brief Stores the configurations in namedtuple tree
|
||||
# @brief Stores the configurations in namedtuple tree
|
||||
self.__confs = None
|
||||
self.__conf_dir = conf_dir
|
||||
self.__started = False
|
||||
self.__bootstrap()
|
||||
|
||||
## @brief Get the named tuple representing configuration
|
||||
|
||||
# @brief Get the named tuple representing configuration
|
||||
@property
|
||||
def settings(self):
|
||||
return self.__confs.lodel2
|
||||
|
||||
## @brief Delete the singleton instance
|
||||
|
||||
# @brief Delete the singleton instance
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
del(cls.instance)
|
||||
|
|
@ -110,7 +114,7 @@ class Settings(object, metaclass=MetaSettings):
|
|||
def started(cls):
|
||||
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
|
||||
#@param expect_instanciated bool : if True we expect that the class is
|
||||
# allready instanciated, else not
|
||||
|
|
@ -124,17 +128,17 @@ class Settings(object, metaclass=MetaSettings):
|
|||
if cls.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 confvalue is a dict with variables to save
|
||||
#@param validator is a dict with adapted validator
|
||||
@classmethod
|
||||
def set(cls, confname, confvalue,validator):
|
||||
def set(cls, confname, confvalue, validator):
|
||||
loader = SettingsLoader(cls.instance.__conf_dir)
|
||||
confkey=confname.rpartition('.')
|
||||
confkey = confname.rpartition('.')
|
||||
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):
|
||||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.plugin.plugins': ['Plugin', 'PluginError']})
|
||||
|
|
@ -144,9 +148,9 @@ class Settings(object, metaclass=MetaSettings):
|
|||
else:
|
||||
lodel2_specs = self.__conf_specs
|
||||
self.__conf_specs = None
|
||||
loader = SettingsLoader(self.__conf_dir)
|
||||
loader = SettingsLoader(self.__conf_dir)
|
||||
plugin_list = []
|
||||
for ptype_name,ptype in Plugin.plugin_types().items():
|
||||
for ptype_name, ptype in Plugin.plugin_types().items():
|
||||
pls = ptype.plist_confspecs()
|
||||
lodel2_specs = confspec_append(lodel2_specs, **pls)
|
||||
cur_list = loader.getoption(
|
||||
|
|
@ -162,13 +166,15 @@ class Settings(object, metaclass=MetaSettings):
|
|||
plugin_list += cur_list
|
||||
except TypeError:
|
||||
plugin_list += [cur_list]
|
||||
#Checking confspecs
|
||||
# Checking confspecs
|
||||
for section in lodel2_specs:
|
||||
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]:
|
||||
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
|
||||
logger.debug("Starting lodel.plugin.Plugin class")
|
||||
|
|
@ -181,13 +187,13 @@ class Settings(object, metaclass=MetaSettings):
|
|||
specs.append(Plugin.get(plugin_name).confspecs)
|
||||
except PluginError as e:
|
||||
errors.append(SettingsError(msg=str(e)))
|
||||
if len(errors) > 0: #Raise all plugins import errors
|
||||
if len(errors) > 0: # Raise all plugins import errors
|
||||
raise SettingsErrors(errors)
|
||||
self.__conf_specs = self.__merge_specs(specs)
|
||||
self.__populate_from_specs(self.__conf_specs, loader)
|
||||
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
|
||||
# and configuration specifications from loaded plugins
|
||||
|
|
@ -198,31 +204,35 @@ class Settings(object, metaclass=MetaSettings):
|
|||
for spec in specs:
|
||||
for section in spec:
|
||||
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:
|
||||
res[section] = dict()
|
||||
for kname in spec[section]:
|
||||
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]:
|
||||
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])
|
||||
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
|
||||
# @param specs dict : Settings specification dictionnary as returned by __merge_specs
|
||||
# @param loader SettingsLoader : A SettingsLoader instance
|
||||
def __populate_from_specs(self, specs, loader):
|
||||
self.__confs = dict()
|
||||
specs = copy.copy(specs) #Avoid destroying original specs dict (may be useless)
|
||||
specs = copy.copy(specs) # Avoid destroying original specs dict (may be useless)
|
||||
# Construct final specs dict replacing variable sections
|
||||
# by the actual existing sections
|
||||
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:
|
||||
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])
|
||||
del(specs[vsec])
|
||||
# Fetching values for sections
|
||||
|
|
@ -238,8 +248,8 @@ class Settings(object, metaclass=MetaSettings):
|
|||
|
||||
self.__confs_to_namedtuple()
|
||||
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
|
||||
# be acessible with self.__confs.hello.world.foo
|
||||
|
|
@ -257,7 +267,7 @@ class Settings(object, metaclass=MetaSettings):
|
|||
section_name = ""
|
||||
cur = section_tree
|
||||
for sec_part in spl:
|
||||
section_name += sec_part+'.'
|
||||
section_name += sec_part + '.'
|
||||
if sec_part not in cur:
|
||||
cur[sec_part] = dict()
|
||||
cur = cur[sec_part]
|
||||
|
|
@ -267,35 +277,35 @@ class Settings(object, metaclass=MetaSettings):
|
|||
raise SettingsError("Duplicated key for '%s.%s'" % (section_name, kname))
|
||||
cur[kname] = kval
|
||||
|
||||
path = [ ('root', section_tree) ]
|
||||
path = [('root', section_tree)]
|
||||
visited = set()
|
||||
|
||||
|
||||
curname = 'root'
|
||||
nodename = 'Lodel2Settings'
|
||||
cur = section_tree
|
||||
while True:
|
||||
visited.add(nodename)
|
||||
left = [ (kname, cur[kname])
|
||||
for kname in cur
|
||||
if nodename+'.'+kname.title() not in visited and isinstance(cur[kname], dict)
|
||||
left = [(kname, cur[kname])
|
||||
for kname in cur
|
||||
if nodename + '.' + kname.title() not in visited and isinstance(cur[kname], dict)
|
||||
]
|
||||
if len(left) == 0:
|
||||
name, leaf = path.pop()
|
||||
typename = nodename.replace('.', '')
|
||||
if len(path) == 0:
|
||||
# END
|
||||
self.__confs = self.__tree2namedtuple(leaf,typename)
|
||||
self.__confs = self.__tree2namedtuple(leaf, typename)
|
||||
break
|
||||
else:
|
||||
path[-1][1][name] = self.__tree2namedtuple(leaf,typename)
|
||||
path[-1][1][name] = self.__tree2namedtuple(leaf, typename)
|
||||
nodename = '.'.join(nodename.split('.')[:-1])
|
||||
cur = path[-1][1]
|
||||
else:
|
||||
curname, cur = left[0]
|
||||
path.append( (curname, cur) )
|
||||
path.append((curname, cur))
|
||||
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 name str
|
||||
# @return a named tuple with fieldnames corresponding to conftree keys
|
||||
|
|
@ -303,11 +313,13 @@ class Settings(object, metaclass=MetaSettings):
|
|||
ResNamedTuple = namedtuple(name, conftree.keys())
|
||||
return ResNamedTuple(**conftree)
|
||||
|
||||
|
||||
class MetaSettingsRO(type):
|
||||
|
||||
def __getattr__(self, 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):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ class SettingsLoader(object):
|
|||
for key_id, filename in remains.items():
|
||||
err_l.append(SettingsError(msg="Invalid configuration key", \
|
||||
key_id=key_id, \
|
||||
filename=filename))
|
||||
filename =filename))
|
||||
if len(err_l) > 0:
|
||||
raise SettingsErrors(err_l)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ def get_utc_timestamp():
|
|||
d = datetime.datetime.utcnow()
|
||||
epoch = datetime.datetime(1970, 1, 1)
|
||||
t = (d - epoch).total_seconds()
|
||||
return t
|
||||
return t
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import hashlib
|
|||
import json
|
||||
|
||||
|
||||
##@brief Stores multilangage string
|
||||
# @brief Stores multilangage string
|
||||
class MlString(object):
|
||||
|
||||
|
||||
__default_lang = 'eng'
|
||||
|
||||
langs = [
|
||||
|
|
@ -17,7 +17,7 @@ class MlString(object):
|
|||
'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
|
||||
def __init__(self, arg):
|
||||
self.values = dict()
|
||||
|
|
@ -31,11 +31,12 @@ class MlString(object):
|
|||
elif isinstance(arg, MlString):
|
||||
self.values = copy.copy(arg.values)
|
||||
else:
|
||||
raise ValueError('<class str>, <class dict> or <class MlString> expected, but %s found' % type(arg))
|
||||
|
||||
##@brief Return a translation given a lang
|
||||
raise ValueError(
|
||||
'<class str>, <class dict> or <class MlString> expected, but %s found' % type(arg))
|
||||
|
||||
# @brief Return a translation given a lang
|
||||
# @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
|
||||
if not self.lang_is_valid(lang):
|
||||
raise ValueError("Invalid lang : '%s'" % lang)
|
||||
|
|
@ -44,7 +45,7 @@ class MlString(object):
|
|||
else:
|
||||
return str(self)
|
||||
|
||||
##@brief Set a translation
|
||||
# @brief Set a translation
|
||||
# @param lang str : the lang
|
||||
# @param val str | None: the translation if None delete the translation
|
||||
def set(self, lang, val):
|
||||
|
|
@ -57,7 +58,7 @@ class MlString(object):
|
|||
else:
|
||||
self.values[lang] = val
|
||||
|
||||
##@brief Checks that given lang is valid
|
||||
# @brief Checks that given lang is valid
|
||||
# @param lang str : the lang
|
||||
@classmethod
|
||||
def lang_is_valid(cls, lang):
|
||||
|
|
@ -65,16 +66,16 @@ class MlString(object):
|
|||
raise ValueError('Invalid value for lang. Str expected but %s found' % type(lang))
|
||||
return lang in cls.langs
|
||||
|
||||
##@brief Get or set the default lang
|
||||
# @brief Get or set the default lang
|
||||
@classmethod
|
||||
def default_lang(cls, lang = None):
|
||||
def default_lang(cls, lang=None):
|
||||
if lang is None:
|
||||
return cls.__default_lang
|
||||
if not cls.lang_is_valid(lang):
|
||||
raise ValueError('lang "%s" is not valid"' % 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
|
||||
@classmethod
|
||||
def from_json(cls, json_str):
|
||||
|
|
@ -89,13 +90,13 @@ class MlString(object):
|
|||
def d_hash(self):
|
||||
m = hashlib.md5()
|
||||
for lang in sorted(list(self.values.keys())):
|
||||
m.update(bytes(lang+";"+self.values[lang], 'utf-8'))
|
||||
m.update(bytes(lang + ";" + self.values[lang], 'utf-8'))
|
||||
return int.from_bytes(m.digest(), byteorder='big')
|
||||
|
||||
def __eq__(self, 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):
|
||||
if self.__default_lang in self.values:
|
||||
return self.values[self.__default_lang]
|
||||
|
|
|
|||
|
|
@ -11,30 +11,34 @@ from lodel.context import LodelContext
|
|||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||
'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 Validator class.
|
||||
# @note to get a list of registered default validators just run
|
||||
# <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 ValidationError(Exception):
|
||||
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
|
||||
# a ValidationError if validation fails, else it returns a properly
|
||||
# casted value.
|
||||
#@todo implement an IP validator and use it in multisite confspec
|
||||
|
||||
|
||||
class Validator(MlNamedObject):
|
||||
|
||||
_validators = dict()
|
||||
_description = dict()
|
||||
|
||||
##@brief Instanciate a validator
|
||||
# @brief Instanciate a validator
|
||||
#@param name str : validator name
|
||||
#@param none_is_valid bool : if True None will be validated
|
||||
#@param **kwargs : more arguement for the validator
|
||||
|
|
@ -48,7 +52,7 @@ class Validator(MlNamedObject):
|
|||
display_name = name
|
||||
super().__init__(display_name, help_text)
|
||||
|
||||
##@brief Call the validator
|
||||
# @brief Call the validator
|
||||
# @param value *
|
||||
# @return properly casted value
|
||||
# @throw ValidationError
|
||||
|
|
@ -61,7 +65,7 @@ class Validator(MlNamedObject):
|
|||
except Exception as exp:
|
||||
raise ValidationError(exp)
|
||||
|
||||
##@brief Register a new validator
|
||||
# @brief Register a new validator
|
||||
# @param name str : validator name
|
||||
# @param callback callable : the function that will validate a value
|
||||
# @param description str
|
||||
|
|
@ -75,12 +79,12 @@ class Validator(MlNamedObject):
|
|||
cls._validators[name] = callback
|
||||
cls._description[name] = description
|
||||
|
||||
##@brief Get the validator list associated with description
|
||||
# @brief Get the validator list associated with description
|
||||
@classmethod
|
||||
def validators_list(cls):
|
||||
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 validator_name str
|
||||
# @param description None | str
|
||||
|
|
@ -99,7 +103,7 @@ class Validator(MlNamedObject):
|
|||
cls.register_validator(validator_name, list_validator, description)
|
||||
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 validator_name str
|
||||
# @param description None | str
|
||||
|
|
@ -111,12 +115,12 @@ class Validator(MlNamedObject):
|
|||
res = ''
|
||||
for elt in value:
|
||||
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
|
||||
cls.register_validator(validator_name, write_list_validator, description)
|
||||
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 validator_name str : The validator name
|
||||
# @param description str : Validator description
|
||||
|
|
@ -125,17 +129,17 @@ class Validator(MlNamedObject):
|
|||
def create_re_validator(cls, pattern, validator_name, description=None):
|
||||
def re_validator(value):
|
||||
if not re.match(pattern, value):
|
||||
raise ValidationError(\
|
||||
"The value '%s' doesn't match the following pattern '%s'" \
|
||||
raise ValidationError(
|
||||
"The value '%s' doesn't match the following pattern '%s'"
|
||||
% pattern)
|
||||
return value
|
||||
#registering the validator
|
||||
cls.register_validator(validator_name, re_validator, \
|
||||
("Match value to '%s'" % pattern) \
|
||||
if description is None else description)
|
||||
# registering the validator
|
||||
cls.register_validator(validator_name, re_validator,
|
||||
("Match value to '%s'" % pattern)
|
||||
if description is None else description)
|
||||
return cls(validator_name)
|
||||
|
||||
## @return a list of registered validators
|
||||
# @return a list of registered validators
|
||||
@classmethod
|
||||
def validators_list_str(cls):
|
||||
result = ''
|
||||
|
|
@ -146,12 +150,16 @@ class Validator(MlNamedObject):
|
|||
result += "\n"
|
||||
return result
|
||||
|
||||
##@brief Integer value validator callback
|
||||
# @brief Integer value validator callback
|
||||
|
||||
|
||||
def int_val(value):
|
||||
return int(value)
|
||||
|
||||
##@brief Output file validator callback
|
||||
# @brief Output file validator callback
|
||||
# @return A file object (if filename is '-' return sys.stderr)
|
||||
|
||||
|
||||
def file_err_output(value):
|
||||
if not isinstance(value, str):
|
||||
raise ValidationError("A string was expected but got '%s' " % value)
|
||||
|
|
@ -159,7 +167,9 @@ def file_err_output(value):
|
|||
return None
|
||||
return value
|
||||
|
||||
##@brief Boolean value validator callback
|
||||
# @brief Boolean value validator callback
|
||||
|
||||
|
||||
def boolean_val(value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
|
@ -171,49 +181,63 @@ def boolean_val(value):
|
|||
raise ValidationError("A boolean was expected but got '%s' " % value)
|
||||
return bool(value)
|
||||
|
||||
##@brief Validate a directory path
|
||||
# @brief Validate a directory path
|
||||
|
||||
|
||||
def directory_val(value):
|
||||
res = Validator('strip')(value)
|
||||
if not os.path.isdir(res):
|
||||
raise ValidationError("Following 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
|
||||
|
||||
##@brief Validate a loglevel value
|
||||
# @brief Validate a loglevel value
|
||||
|
||||
|
||||
def loglevel_val(value):
|
||||
valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
|
||||
if value.upper() not in valids:
|
||||
raise ValidationError( \
|
||||
raise ValidationError(
|
||||
"The value '%s' is not a valid loglevel" % value)
|
||||
return value.upper()
|
||||
|
||||
##@brief Validate a path
|
||||
# @brief Validate a path
|
||||
|
||||
|
||||
def path_val(value):
|
||||
if value is None or not os.path.exists(value):
|
||||
raise ValidationError( \
|
||||
raise ValidationError(
|
||||
"path '%s' doesn't exists" % value)
|
||||
return value
|
||||
|
||||
##@brief Validate None
|
||||
# @brief Validate None
|
||||
|
||||
|
||||
def none_val(value):
|
||||
if value is None:
|
||||
return None
|
||||
raise ValidationError("This settings cannot be set in configuration file")
|
||||
|
||||
##@brief Validate a string
|
||||
# @brief Validate a string
|
||||
|
||||
|
||||
def str_val(value):
|
||||
try:
|
||||
return str(value)
|
||||
except Exception as exp:
|
||||
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):
|
||||
if re.match(pattern, value) is None:
|
||||
raise ValidationError("The value '%s' is not validated by : \
|
||||
r\"%s\"" %(value, pattern))
|
||||
r\"%s\"" % (value, pattern))
|
||||
return value
|
||||
|
||||
##@brief Validate a hostname (ipv4 or ipv6)
|
||||
# @brief Validate a hostname (ipv4 or ipv6)
|
||||
|
||||
|
||||
def host_val(value):
|
||||
if value == 'localhost':
|
||||
return value
|
||||
|
|
@ -235,6 +259,7 @@ def host_val(value):
|
|||
msg = "The value '%s' is not a valid host"
|
||||
raise ValidationError(msg % value)
|
||||
|
||||
|
||||
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)
|
||||
|
|
@ -246,8 +271,8 @@ def custom_list_validator(value, validator_name, validator_kwargs=None):
|
|||
# Default validators registration
|
||||
#
|
||||
|
||||
Validator.register_validator('custom_list', custom_list_validator, \
|
||||
'A list validator that takes a "validator_name" as argument')
|
||||
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')
|
||||
|
||||
|
|
@ -261,11 +286,11 @@ 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('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('directory', directory_val,
|
||||
'Directory path validator')
|
||||
|
||||
Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator')
|
||||
|
||||
|
|
@ -273,48 +298,50 @@ 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.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 ','", \
|
||||
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_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'), \
|
||||
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', \
|
||||
Validator.create_re_validator(
|
||||
r'^https?://[^\./]+.[^\./]+/?.*$',
|
||||
'http_url',
|
||||
'Url validator')
|
||||
|
||||
##@brief Validator for Editorial model component
|
||||
# @brief Validator for Editorial model component
|
||||
#
|
||||
# Designed to validate a conf that indicate a class.field in an EM
|
||||
#@todo modified the hardcoded dyncode import (it's a warning)
|
||||
|
||||
|
||||
def emfield_val(value):
|
||||
LodelContext.expose_modules(globals(), \
|
||||
{'lodel.plugin.hooks': ['LodelHook']})
|
||||
LodelContext.expose_modules(globals(),
|
||||
{'lodel.plugin.hooks': ['LodelHook']})
|
||||
spl = value.split('.')
|
||||
if len(spl) != 2:
|
||||
msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s"
|
||||
raise SettingsValidationError(msg % value)
|
||||
value = tuple(spl)
|
||||
#Late validation hook
|
||||
# Late validation hook
|
||||
|
||||
@LodelHook('lodel2_dyncode_bootstraped')
|
||||
def emfield_conf_check(hookname, caller, payload):
|
||||
import leapi_dyncode as dyncode # <-- dirty & quick
|
||||
classnames = { cls.__name__.lower(): cls for cls in dyncode.dynclasses}
|
||||
import leapi_dyncode as dyncode # <-- dirty & quick
|
||||
classnames = {cls.__name__.lower(): cls for cls in dyncode.dynclasses}
|
||||
if value[0].lower() not in classnames:
|
||||
msg = "Following dynamic class do not exists in current EM : %s"
|
||||
raise SettingsValidationError(msg % value[0])
|
||||
|
|
@ -324,17 +351,20 @@ def emfield_val(value):
|
|||
raise SettingsValidationError(msg % 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):
|
||||
LodelContext.expose_modules(globals(), { \
|
||||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.plugin.hooks': ['LodelHook']})
|
||||
value = copy.copy(value)
|
||||
|
||||
@LodelHook('lodel2_dyncode_bootstraped')
|
||||
def plugin_type_checker(hookname, caller, payload):
|
||||
LodelContext.expose_modules(globals(), { \
|
||||
'lodel.plugin.plugins': ['Plugin'], \
|
||||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.plugin.plugins': ['Plugin'],
|
||||
'lodel.plugin.exceptions': ['PluginError']})
|
||||
if value is None:
|
||||
return
|
||||
|
|
@ -352,21 +382,21 @@ named '%s' that is a '%s' plugin"
|
|||
return value
|
||||
|
||||
|
||||
Validator.register_validator( \
|
||||
'plugin', \
|
||||
plugin_validator, \
|
||||
Validator.register_validator(
|
||||
'plugin',
|
||||
plugin_validator,
|
||||
'plugin name & type validator')
|
||||
|
||||
Validator.register_validator( \
|
||||
'emfield', \
|
||||
emfield_val, \
|
||||
Validator.register_validator(
|
||||
'emfield',
|
||||
emfield_val,
|
||||
'EmField name validator')
|
||||
|
||||
#
|
||||
# Lodel 2 configuration specification
|
||||
#
|
||||
|
||||
##@brief Append a piece of confspec
|
||||
# @brief Append a piece of confspec
|
||||
#@note orig is modified during the process
|
||||
#@param orig dict : the confspec to update
|
||||
#@param section str : section name
|
||||
|
|
@ -374,6 +404,8 @@ Validator.register_validator( \
|
|||
#@param validator Validator : the validator to use to check this configuration key's value
|
||||
#@param default
|
||||
#@return new confspec
|
||||
|
||||
|
||||
def confspec_append(orig, section, key, validator, default):
|
||||
if section not in orig:
|
||||
orig[section] = dict()
|
||||
|
|
@ -381,19 +413,19 @@ def confspec_append(orig, section, key, validator, default):
|
|||
orig[section][key] = (default, validator)
|
||||
return orig
|
||||
|
||||
##@brief Global specifications for lodel2 settings
|
||||
# @brief Global specifications for lodel2 settings
|
||||
LODEL2_CONF_SPECS = {
|
||||
'lodel2': {
|
||||
'debug': (True, Validator('bool')),
|
||||
'sitename': ('noname', Validator('strip')),
|
||||
'runtest': (False, Validator('bool')),
|
||||
},
|
||||
'lodel2.logging.*' : {
|
||||
'lodel2.logging.*': {
|
||||
'level': ('ERROR', Validator('loglevel')),
|
||||
'context': (False, Validator('bool')),
|
||||
'filename': ("-", Validator('errfile', none_is_valid=False)),
|
||||
'backupcount': (5, Validator('int', none_is_valid=False)),
|
||||
'maxbytes': (1024*10, Validator('int', none_is_valid=False)),
|
||||
'maxbytes': (1024 * 10, Validator('int', none_is_valid=False)),
|
||||
},
|
||||
'lodel2.editorialmodel': {
|
||||
'emfile': ('em.pickle', Validator('strip')),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue