mirror of
https://github.com/yweber/lodel2.git
synced 2026-02-14 05:50:12 +01: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 \
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ article.new_field( 'author_note',
|
||||||
group = editorial_group,
|
group = editorial_group,
|
||||||
data_handler = 'text'
|
data_handler = 'text'
|
||||||
)
|
)
|
||||||
# Classe Review
|
# Classe Review
|
||||||
review = em.new_class( 'review',
|
review = em.new_class( 'review',
|
||||||
display_name = 'Review',
|
display_name = 'Review',
|
||||||
group = editorial_group,
|
group = editorial_group,
|
||||||
|
|
@ -439,7 +439,7 @@ issue.new_field( 'print_pub_date',
|
||||||
},
|
},
|
||||||
data_handler = 'datetime',
|
data_handler = 'datetime',
|
||||||
group = editorial_group,
|
group = editorial_group,
|
||||||
)
|
)
|
||||||
issue.new_field( 'e_pub_date',
|
issue.new_field( 'e_pub_date',
|
||||||
display_name = {
|
display_name = {
|
||||||
'eng': 'Electronic publication date',
|
'eng': 'Electronic publication date',
|
||||||
|
|
@ -447,7 +447,7 @@ issue.new_field( 'e_pub_date',
|
||||||
},
|
},
|
||||||
data_handler = 'datetime',
|
data_handler = 'datetime',
|
||||||
group = editorial_group,
|
group = editorial_group,
|
||||||
)
|
)
|
||||||
issue.new_field( 'abstract',
|
issue.new_field( 'abstract',
|
||||||
display_name = {
|
display_name = {
|
||||||
'eng': 'Abstract',
|
'eng': 'Abstract',
|
||||||
|
|
@ -455,7 +455,7 @@ issue.new_field( 'abstract',
|
||||||
},
|
},
|
||||||
data_handler = 'text',
|
data_handler = 'text',
|
||||||
group = editorial_group,
|
group = editorial_group,
|
||||||
)
|
)
|
||||||
issue.new_field( 'collection',
|
issue.new_field( 'collection',
|
||||||
display_name = {
|
display_name = {
|
||||||
'eng': 'Collection',
|
'eng': 'Collection',
|
||||||
|
|
@ -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,28 +12,30 @@ 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
|
||||||
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:
|
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:
|
||||||
return str(self.uid)
|
return str(self.uid)
|
||||||
|
|
@ -42,34 +44,34 @@ class EmComponent(object):
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
for data in (
|
for data in (
|
||||||
self.uid,
|
self.uid,
|
||||||
'NODISPNAME' if self.display_name is None else str(self.display_name.d_hash()),
|
'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()),
|
'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()),
|
'NOGROUP' if self.group is None else str(self.group.d_hash()),
|
||||||
):
|
):
|
||||||
m.update(bytes(data, 'utf-8'))
|
m.update(bytes(data, 'utf-8'))
|
||||||
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
|
||||||
#@param pure_abstract bool : if True the EmClass will not be represented in
|
#@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 parents list: parent EmClass list or uid list
|
||||||
#@param help_text MlString|str|dict : help_text
|
#@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
|
#@ref lodel2_datasources ) or two names (first is read_only datasource the
|
||||||
#second is read write)
|
# second is read write)
|
||||||
def __init__(
|
def __init__(
|
||||||
self, uid, display_name = None, help_text = None, abstract = False,
|
self, uid, display_name=None, help_text=None, abstract=False,
|
||||||
parents = None, group = None, pure_abstract = False,
|
parents=None, group=None, pure_abstract=False,
|
||||||
datasources = 'default'):
|
datasources='default'):
|
||||||
|
|
||||||
super().__init__(uid, display_name, help_text, group)
|
super().__init__(uid, display_name, help_text, group)
|
||||||
self.abstract = bool(abstract)
|
self.abstract = bool(abstract)
|
||||||
|
|
@ -85,49 +87,50 @@ 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
|
||||||
if group is None:
|
if group is None:
|
||||||
warnings.warn("NO GROUP FOR EMCLASS %s" % uid)
|
warnings.warn("NO GROUP FOR EMCLASS %s" % uid)
|
||||||
else:
|
else:
|
||||||
group.add_components([self])
|
group.add_components([self])
|
||||||
|
|
||||||
#Adding common field
|
# Adding common field
|
||||||
if not self.abstract:
|
if not self.abstract:
|
||||||
self.new_field(
|
self.new_field(
|
||||||
CLASS_ID_FIELDNAME,
|
CLASS_ID_FIELDNAME,
|
||||||
display_name = {
|
display_name={
|
||||||
'eng': "LeObject subclass identifier",
|
'eng': "LeObject subclass identifier",
|
||||||
'fre': "Identifiant de la class fille de LeObject"},
|
'fre': "Identifiant de la class fille de LeObject"},
|
||||||
help_text = {
|
help_text={
|
||||||
'eng': "Allow to create instance of the good class when\
|
'eng': "Allow to create instance of the good class when\
|
||||||
fetching arbitrary datas from DB"},
|
fetching arbitrary datas from DB"},
|
||||||
data_handler = 'LeobjectSubclassIdentifier',
|
data_handler='LeobjectSubclassIdentifier',
|
||||||
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):
|
||||||
res = dict()
|
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(pfields)
|
||||||
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,29 +143,29 @@ 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
|
||||||
# @todo use Settings.editorialmodel.groups to determine wich fields should be returned
|
# @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
|
fields = self.__fields if no_parents else self.__all_fields
|
||||||
try:
|
try:
|
||||||
return list(fields.values()) if uid is None else fields[uid]
|
return list(fields.values()) if uid is None else fields[uid]
|
||||||
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 = []
|
||||||
for grp_name, agrp in active_groups.items():
|
for grp_name, agrp in active_groups.items():
|
||||||
active_fields += [ emc for emc in agrp.components()
|
active_fields += [emc for emc in agrp.components()
|
||||||
if isinstance(emc, EmField)]
|
if isinstance(emc, EmField)]
|
||||||
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,38 +173,40 @@ 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__() )
|
||||||
def new_field(self, uid, data_handler, **field_kwargs):
|
def new_field(self, uid, data_handler, **field_kwargs):
|
||||||
assert_edit()
|
assert_edit()
|
||||||
return self.add_field(EmField(uid, data_handler, self, **field_kwargs))
|
return self.add_field(EmField(uid, data_handler, self, **field_kwargs))
|
||||||
|
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
m = hashlib.md5()
|
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):
|
for p in sorted(self.parents):
|
||||||
payload += str(p.d_hash())
|
payload += str(p.d_hash())
|
||||||
for fuid in sorted(self.__fields.keys()):
|
for fuid in sorted(self.__fields.keys()):
|
||||||
payload += str(self.__fields[fuid].d_hash())
|
payload += str(self.__fields[fuid].d_hash())
|
||||||
|
|
||||||
m.update(bytes(payload, 'utf-8'))
|
m.update(bytes(payload, 'utf-8'))
|
||||||
return int.from_bytes(m.digest(), byteorder='big')
|
return int.from_bytes(m.digest(), byteorder='big')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<class EmClass %s>" % self.uid
|
return "<class EmClass %s>" % self.uid
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if not self.abstract:
|
if not self.abstract:
|
||||||
abstract = ''
|
abstract = ''
|
||||||
|
|
@ -209,94 +214,95 @@ class EmClass(EmComponent):
|
||||||
abstract = 'PureAbstract'
|
abstract = 'PureAbstract'
|
||||||
else:
|
else:
|
||||||
abstract = 'Abstract'
|
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
|
#@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
|
||||||
# @param help_text MlString|str|dict : help text
|
# @param help_text MlString|str|dict : help text
|
||||||
# @param group EmGroup :
|
# @param group EmGroup :
|
||||||
# @param **handler_kwargs : data handler arguments
|
# @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
|
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)
|
||||||
if group is None:
|
if group is None:
|
||||||
warnings.warn("No EmGroup for field %s" % uid)
|
warnings.warn("No EmGroup for field %s" % uid)
|
||||||
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):
|
||||||
return self._emclass.uid
|
return self._emclass.uid
|
||||||
|
|
||||||
# @warning Not complete !
|
# @warning Not complete !
|
||||||
# @todo Complete the hash when data handlers becomes available
|
# @todo Complete the hash when data handlers becomes available
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
return int.from_bytes(hashlib.md5(
|
return int.from_bytes(hashlib.md5(
|
||||||
bytes(
|
bytes(
|
||||||
"%s%s%s" % ( super().d_hash(),
|
"%s%s%s" % (super().d_hash(),
|
||||||
self.data_handler_name,
|
self.data_handler_name,
|
||||||
self.data_handler_options),
|
self.data_handler_options),
|
||||||
'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
|
||||||
# @param display_name MlString|str :
|
# @param display_name MlString|str :
|
||||||
# @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):
|
||||||
res = copy.copy(self.require)
|
res = copy.copy(self.require)
|
||||||
if not recursive:
|
if not recursive:
|
||||||
return res
|
return res
|
||||||
|
|
@ -308,11 +314,11 @@ class EmGroup(object):
|
||||||
to_scan.append(new_dep)
|
to_scan.append(new_dep)
|
||||||
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):
|
||||||
res = copy.copy(self.required_by)
|
res = copy.copy(self.required_by)
|
||||||
if not recursive:
|
if not recursive:
|
||||||
return res
|
return res
|
||||||
|
|
@ -324,29 +330,31 @@ class EmGroup(object):
|
||||||
to_scan.append(new_app)
|
to_scan.append(new_app)
|
||||||
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,16 +377,17 @@ 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
|
||||||
if self.__circular_dependencie(grp):
|
if self.__circular_dependencie(grp):
|
||||||
raise EditorialModelError("Circular dependencie detected, cannot add dependencie")
|
raise EditorialModelError("Circular dependencie detected, cannot add dependencie")
|
||||||
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,26 +396,27 @@ 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
|
||||||
if self.__circular_applicant(grp):
|
if self.__circular_applicant(grp):
|
||||||
raise EditorialModelError("Circular applicant detected, cannot add applicant")
|
raise EditorialModelError("Circular applicant detected, cannot add applicant")
|
||||||
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:
|
||||||
|
|
@ -414,11 +425,11 @@ class EmGroup(object):
|
||||||
return self.display_name.get()
|
return self.display_name.get()
|
||||||
|
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
|
|
||||||
payload = "%s%s%s" % (
|
payload = "%s%s%s" % (
|
||||||
self.uid,
|
self.uid,
|
||||||
'NODNAME' if self.display_name is None else self.display_name.d_hash(),
|
'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()
|
'NOHELP' if self.help_text is None else self.help_text.d_hash()
|
||||||
)
|
)
|
||||||
for recurs in (False, True):
|
for recurs in (False, True):
|
||||||
deps = self.dependencies(recurs)
|
deps = self.dependencies(recurs)
|
||||||
|
|
@ -427,11 +438,11 @@ class EmGroup(object):
|
||||||
for req_by_uid in self.required_by:
|
for req_by_uid in self.required_by:
|
||||||
payload += req_by_uid
|
payload += req_by_uid
|
||||||
return int.from_bytes(
|
return int.from_bytes(
|
||||||
bytes(payload, 'utf-8'),
|
bytes(payload, 'utf-8'),
|
||||||
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,29 +15,34 @@ 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:
|
||||||
##@brief EmClass uids accessor
|
display_name = name
|
||||||
|
if help_text is None:
|
||||||
|
help_text = description
|
||||||
|
super().__init__(display_name, help_text)
|
||||||
|
|
||||||
|
# @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:
|
||||||
return copy.copy(self.__classes)
|
return copy.copy(self.__classes)
|
||||||
else:
|
else:
|
||||||
|
|
@ -44,8 +50,8 @@ class EditorialModel(object):
|
||||||
return copy.copy(self.__classes[uid])
|
return copy.copy(self.__classes[uid])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise EditorialModelException("EmClass not found : '%s'" % uid)
|
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:
|
if uid is None:
|
||||||
return self.__classes
|
return self.__classes
|
||||||
else:
|
else:
|
||||||
|
|
@ -53,16 +59,15 @@ class EditorialModel(object):
|
||||||
return self.__classes[uid]
|
return self.__classes[uid]
|
||||||
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:
|
||||||
return copy.copy(self.__groups)
|
return copy.copy(self.__groups)
|
||||||
else:
|
else:
|
||||||
|
|
@ -70,10 +75,10 @@ class EditorialModel(object):
|
||||||
return copy.copy(self.__groups[uid])
|
return copy.copy(self.__groups[uid])
|
||||||
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:
|
||||||
return self.__groups
|
return self.__groups
|
||||||
else:
|
else:
|
||||||
|
|
@ -81,26 +86,26 @@ class EditorialModel(object):
|
||||||
return self.__groups[uid]
|
return self.__groups[uid]
|
||||||
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
|
||||||
#@todo use Settings.editorialmodel.groups to determine wich classes should
|
#@todo use Settings.editorialmodel.groups to determine wich classes should
|
||||||
# be returned
|
# be returned
|
||||||
def classes(self, uid = None):
|
def classes(self, uid=None):
|
||||||
try:
|
try:
|
||||||
return self.__elt_getter( self.__active_classes,
|
return self.__elt_getter(self.__active_classes,
|
||||||
uid)
|
uid)
|
||||||
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,24 +116,23 @@ 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):
|
||||||
try:
|
try:
|
||||||
return self.__elt_getter( self.__active_groups,
|
return self.__elt_getter(self.__active_groups,
|
||||||
uid)
|
uid)
|
||||||
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:
|
||||||
logger.warning("All EM groups active because editormode in ON")
|
logger.warning("All EM groups active because editormode in ON")
|
||||||
|
|
@ -136,7 +140,7 @@ class EditorialModel(object):
|
||||||
self.__active_groups = self.__groups
|
self.__active_groups = self.__groups
|
||||||
self.__active_classes = self.__classes
|
self.__active_classes = self.__classes
|
||||||
else:
|
else:
|
||||||
#determine groups first
|
# determine groups first
|
||||||
self.__active_groups = dict()
|
self.__active_groups = dict()
|
||||||
self.__active_classes = dict()
|
self.__active_classes = dict()
|
||||||
for agrp in Settings.editorialmodel.groups:
|
for agrp in Settings.editorialmodel.groups:
|
||||||
|
|
@ -153,13 +157,13 @@ class EditorialModel(object):
|
||||||
raise RuntimeError("No active class found. Abording")
|
raise RuntimeError("No active class found. Abording")
|
||||||
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
|
||||||
#
|
#
|
||||||
# @todo delete it, useless...
|
# @todo delete it, useless...
|
||||||
def field(self, uid = None):
|
def field(self, uid=None):
|
||||||
spl = uid.split('.')
|
spl = uid.split('.')
|
||||||
if len(spl) != 2:
|
if len(spl) != 2:
|
||||||
raise ValueError("Malformed EmField identifier : '%s'" % uid)
|
raise ValueError("Malformed EmField identifier : '%s'" % uid)
|
||||||
|
|
@ -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,15 +203,15 @@ 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__() )
|
||||||
def new_class(self, uid, **kwargs):
|
def new_class(self, uid, **kwargs):
|
||||||
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):
|
||||||
|
|
@ -223,14 +227,15 @@ class EditorialModel(object):
|
||||||
if isinstance(translator, str):
|
if isinstance(translator, str):
|
||||||
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
|
||||||
|
|
@ -253,12 +258,12 @@ class EditorialModel(object):
|
||||||
except ImportError:
|
except ImportError:
|
||||||
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,
|
||||||
'NODESC' if self.description is None else self.description.d_hash()
|
'NODESC' if self.description is None else self.description.d_hash()
|
||||||
)
|
)
|
||||||
for guid in sorted(self.__groups):
|
for guid in sorted(self.__groups):
|
||||||
payload += str(self.__groups[guid].d_hash())
|
payload += str(self.__groups[guid].d_hash())
|
||||||
|
|
@ -267,7 +272,6 @@ class EditorialModel(object):
|
||||||
payload += str(self.__classes[cuid].d_hash())
|
payload += str(self.__classes[cuid].d_hash())
|
||||||
|
|
||||||
return int.from_bytes(
|
return int.from_bytes(
|
||||||
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
hashlib.md5(bytes(payload, 'utf-8')).digest(),
|
||||||
byteorder='big'
|
byteorder='big'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,34 +12,51 @@ 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
|
||||||
_base_handlers = None
|
_base_handlers = None
|
||||||
##@brief Stores custom datahandlers classes indexed by name
|
## @brief Stores custom datahandlers classes indexed by name
|
||||||
# @todo do it ! (like plugins, register handlers... blablabla)
|
# @todo do it ! (like plugins, register handlers... blablabla)
|
||||||
__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
|
||||||
|
|
@ -74,15 +112,15 @@ class DataHandler(object):
|
||||||
def is_primary_key(self):
|
def is_primary_key(self):
|
||||||
return self.primary_key
|
return self.primary_key
|
||||||
|
|
||||||
##@brief checks if a fieldtype is internal
|
## @brief checks if a fieldtype is internal
|
||||||
# @return bool
|
# @return bool
|
||||||
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)
|
||||||
# @return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|
@ -91,9 +129,9 @@ class DataHandler(object):
|
||||||
raise DataNoneValid("None with a nullable. This exeption is allowed")
|
raise DataNoneValid("None with a nullable. This exeption is allowed")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief calls the data_field (defined in derived class) _check_data_value() method
|
## @brief calls the data_field (defined in derived class) _check_data_value() method
|
||||||
#@param value *
|
# @param value *
|
||||||
#@return tuple (value|None, None|error) value can be cast if NoneError
|
# @return tuple (value|None, None|error) value can be cast if NoneError
|
||||||
def check_data_value(self, value):
|
def check_data_value(self, value):
|
||||||
try:
|
try:
|
||||||
value = self._check_data_value(value)
|
value = self._check_data_value(value)
|
||||||
|
|
@ -103,7 +141,7 @@ class DataHandler(object):
|
||||||
return None, expt
|
return None, expt
|
||||||
return value, None
|
return value, None
|
||||||
|
|
||||||
##@brief checks if this class can override the given data handler
|
## @brief checks if this class can override the given data handler
|
||||||
# @param data_handler DataHandler
|
# @param data_handler DataHandler
|
||||||
# @return bool
|
# @return bool
|
||||||
def can_override(self, data_handler):
|
def can_override(self, data_handler):
|
||||||
|
|
@ -111,17 +149,17 @@ class DataHandler(object):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##@brief Build field value
|
## @brief Build field value
|
||||||
#@ingroup lodel2_dh_checks
|
# @ingroup lodel2_dh_checks
|
||||||
#@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
|
# @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
|
||||||
#@ref _construct_data() and @ref lodel2_dh_check_impl )
|
# @ref _construct_data() and @ref lodel2_dh_check_impl )
|
||||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
#@param fname str : The field name
|
# @param fname str : The field name
|
||||||
#@param datas dict : dict storing fields values (from the component)
|
# @param datas dict : dict storing fields values (from the component)
|
||||||
#@param cur_value : the value from the current field (identified by fieldname)
|
# @param cur_value : the value from the current field (identified by fieldname)
|
||||||
#@return the value
|
# @return the value
|
||||||
#@throw RunTimeError if data construction fails
|
# @throw RunTimeError if data construction fails
|
||||||
#@todo raise something else
|
# @todo raise something else
|
||||||
def construct_data(self, emcomponent, fname, datas, cur_value):
|
def construct_data(self, emcomponent, fname, datas, cur_value):
|
||||||
emcomponent_fields = emcomponent.fields()
|
emcomponent_fields = emcomponent.fields()
|
||||||
data_handler = None
|
data_handler = None
|
||||||
|
|
@ -136,41 +174,41 @@ class DataHandler(object):
|
||||||
new_val = None
|
new_val = None
|
||||||
return self._construct_data(emcomponent, fname, datas, new_val)
|
return self._construct_data(emcomponent, fname, datas, new_val)
|
||||||
|
|
||||||
##@brief Designed to be reimplemented by child classes
|
## @brief Designed to be reimplemented by child classes
|
||||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
#@param fname str : The field name
|
# @param fname str : The field name
|
||||||
#@param datas dict : dict storing fields values (from the component)
|
# @param datas dict : dict storing fields values (from the component)
|
||||||
#@param cur_value : the value from the current field (identified by fieldname)
|
# @param cur_value : the value from the current field (identified by fieldname)
|
||||||
#@return the value
|
# @return the value
|
||||||
#@see construct_data() lodel2_dh_check_impl
|
# @see construct_data() lodel2_dh_check_impl
|
||||||
def _construct_data(self, empcomponent, fname, datas, cur_value):
|
def _construct_data(self, empcomponent, fname, datas, cur_value):
|
||||||
return cur_value
|
return cur_value
|
||||||
|
|
||||||
##@brief Check datas consistency
|
## @brief Check datas consistency
|
||||||
#@ingroup lodel2_dh_checks
|
# @ingroup lodel2_dh_checks
|
||||||
#@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
|
# @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
|
||||||
#@ref _construct_data() and @ref lodel2_dh_check_impl )
|
# @ref _construct_data() and @ref lodel2_dh_check_impl )
|
||||||
#@warning the datas argument looks like a dict but is not a dict
|
# @warning the datas argument looks like a dict but is not a dict
|
||||||
#see @ref base_classes.DatasConstructor "DatasConstructor" and
|
# see @ref base_classes.DatasConstructor "DatasConstructor" and
|
||||||
#@ref lodel2_dh_datas_construction "Datas construction section"
|
# @ref lodel2_dh_datas_construction "Datas construction section"
|
||||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
#@param fname : the field name
|
# @param fname : the field name
|
||||||
#@param datas dict : dict storing fields values
|
# @param datas dict : dict storing fields values
|
||||||
#@return an Exception instance if fails else True
|
# @return an Exception instance if fails else True
|
||||||
#@todo A implémenter
|
# @todo A implémenter
|
||||||
def check_data_consistency(self, emcomponent, fname, datas):
|
def check_data_consistency(self, emcomponent, fname, datas):
|
||||||
return self._check_data_consistency(emcomponent, fname, datas)
|
return self._check_data_consistency(emcomponent, fname, datas)
|
||||||
|
|
||||||
##@brief Designed to be reimplemented by child classes
|
## @brief Designed to be reimplemented by child classes
|
||||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
#@param fname : the field name
|
# @param fname : the field name
|
||||||
#@param datas dict : dict storing fields values
|
# @param datas dict : dict storing fields values
|
||||||
#@return an Exception instance if fails else True
|
# @return an Exception instance if fails else True
|
||||||
#@see check_data_consistency() lodel2_dh_check_impl
|
# @see check_data_consistency() lodel2_dh_check_impl
|
||||||
def _check_data_consistency(self, emcomponent, fname, datas):
|
def _check_data_consistency(self, emcomponent, fname, datas):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##@brief make consistency after a query
|
## @brief make consistency after a query
|
||||||
# @param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
# @param fname : the field name
|
# @param fname : the field name
|
||||||
# @param datas dict : dict storing fields values
|
# @param datas dict : dict storing fields values
|
||||||
|
|
@ -179,7 +217,7 @@ class DataHandler(object):
|
||||||
def make_consistency(self, emcomponent, fname, datas):
|
def make_consistency(self, emcomponent, fname, datas):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
##@brief This method is use by plugins to register new data handlers
|
## @brief This method is use by plugins to register new data handlers
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_new_handler(cls, name, data_handler):
|
def register_new_handler(cls, name, data_handler):
|
||||||
if not inspect.isclass(data_handler):
|
if not inspect.isclass(data_handler):
|
||||||
|
|
@ -188,7 +226,7 @@ class DataHandler(object):
|
||||||
raise ValueError("A data handler HAS TO be a child class of DataHandler")
|
raise ValueError("A data handler HAS TO be a child class of DataHandler")
|
||||||
cls.__custom_handlers[name] = data_handler
|
cls.__custom_handlers[name] = data_handler
|
||||||
|
|
||||||
##@brief Load all datahandlers
|
## @brief Load all datahandlers
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_base_handlers(cls):
|
def load_base_handlers(cls):
|
||||||
if cls._base_handlers is None:
|
if cls._base_handlers is None:
|
||||||
|
|
@ -201,10 +239,11 @@ class DataHandler(object):
|
||||||
cls._base_handlers[name.lower()] = obj
|
cls._base_handlers[name.lower()] = obj
|
||||||
return copy.copy(cls._base_handlers)
|
return copy.copy(cls._base_handlers)
|
||||||
|
|
||||||
##@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,7 +253,24 @@ 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 Return the module name to import in order to use the datahandler
|
# @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
|
||||||
# @param data_handler_name str : Data handler name
|
# @param data_handler_name str : Data handler name
|
||||||
# @return a str
|
# @return a str
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -222,62 +278,64 @@ class DataHandler(object):
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
handler_class = cls.from_name(name)
|
handler_class = cls.from_name(name)
|
||||||
return '{module_name}.{class_name}'.format(
|
return '{module_name}.{class_name}'.format(
|
||||||
module_name=handler_class.__module__,
|
module_name=handler_class.__module__,
|
||||||
class_name=handler_class.__name__
|
class_name=handler_class.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
##@brief __hash__ implementation for fieldtypes
|
## @brief __hash__ implementation for fieldtypes
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
hash_dats = [self.__class__.__module__]
|
hash_dats = [self.__class__.__module__]
|
||||||
for kdic in sorted([k for k in self.__dict__.keys() if not k.startswith('_')]):
|
for kdic in sorted([k for k in self.__dict__.keys() if not k.startswith('_')]):
|
||||||
hash_dats.append((kdic, getattr(self, kdic)))
|
hash_dats.append((kdic, getattr(self, kdic)))
|
||||||
return hash(tuple(hash_dats))
|
return hash(tuple(hash_dats))
|
||||||
|
|
||||||
##@brief Base class for datas data handler (by opposition with references)
|
## @brief Base class for datas data handler (by opposition with references)
|
||||||
#@ingroup lodel2_datahandlers
|
# @ingroup lodel2_datahandlers
|
||||||
class DataField(DataHandler):
|
class DataField(DataHandler):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
##@brief Abstract class for all references
|
|
||||||
#@ingroup lodel2_datahandlers
|
## @brief Abstract class for all references
|
||||||
|
# @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"
|
||||||
|
|
||||||
##@brief Instanciation
|
## @brief Instanciation
|
||||||
# @param allowed_classes list | None : list of allowed em classes if None no restriction
|
# @param allowed_classes list | None : list of allowed em classes if None no restriction
|
||||||
# @param back_reference tuple | None : tuple containing (LeObject child class, fieldname)
|
# @param back_reference tuple | None : tuple containing (LeObject child class, fieldname)
|
||||||
# @param internal bool : if False, the field is not internal
|
# @param internal bool : if False, the field is not internal
|
||||||
# @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)
|
||||||
|
|
||||||
##@brief Method designed to return an empty value for this kind of
|
## @brief Method designed to return an empty value for this kind of
|
||||||
#multipleref
|
# multipleref
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
##@brief Property that takes value of a copy of the back_reference tuple
|
## @brief Property that takes value of a copy of the back_reference tuple
|
||||||
@property
|
@property
|
||||||
def back_reference(self):
|
def back_reference(self):
|
||||||
return copy.copy(self.__back_reference)
|
return copy.copy(self.__back_reference)
|
||||||
|
|
||||||
##@brief Property that takes value of datahandler of the backreference or
|
## @brief Property that takes value of datahandler of the backreference or
|
||||||
#None
|
# None
|
||||||
@property
|
@property
|
||||||
def back_ref_datahandler(self):
|
def back_ref_datahandler(self):
|
||||||
if self.__back_reference is None:
|
if self.__back_reference is None:
|
||||||
|
|
@ -288,15 +346,15 @@ class Reference(DataHandler):
|
||||||
def linked_classes(self):
|
def linked_classes(self):
|
||||||
return copy.copy(self.__allowed_classes)
|
return copy.copy(self.__allowed_classes)
|
||||||
|
|
||||||
##@brief Set the back reference for this field.
|
## @brief Set the back reference for this field.
|
||||||
def _set_back_reference(self, back_reference):
|
def _set_back_reference(self, back_reference):
|
||||||
self.__back_reference = back_reference
|
self.__back_reference = back_reference
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is an appropriate type
|
# @throw FieldValidationError if value is an appropriate type
|
||||||
#@return value
|
# @return value
|
||||||
#@todo implement the check when we have LeObject uid check value
|
# @todo implement the check when we have LeObject uid check value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
from lodel.leapi.leobject import LeObject
|
from lodel.leapi.leobject import LeObject
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
|
|
@ -311,13 +369,13 @@ class Reference(DataHandler):
|
||||||
raise FieldValidationError("Reference datahandler can not check this value %s if any allowed_class is allowed." % value)
|
raise FieldValidationError("Reference datahandler can not check this value %s if any allowed_class is allowed." % value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Check datas consistency
|
## @brief Check datas consistency
|
||||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||||
#@param fname : the field name
|
# @param fname : the field name
|
||||||
#@param datas dict : dict storing fields values
|
# @param datas dict : dict storing fields values
|
||||||
#@return an Exception instance if fails else True
|
# @return an Exception instance if fails else True
|
||||||
#@todo check for performance issue and check logics
|
# @todo check for performance issue and check logics
|
||||||
#@warning composed uid capabilities broken here
|
# @warning composed uid capabilities broken here
|
||||||
def check_data_consistency(self, emcomponent, fname, datas):
|
def check_data_consistency(self, emcomponent, fname, datas):
|
||||||
rep = super().check_data_consistency(emcomponent, fname, datas)
|
rep = super().check_data_consistency(emcomponent, fname, datas)
|
||||||
if isinstance(rep, Exception):
|
if isinstance(rep, Exception):
|
||||||
|
|
@ -333,21 +391,21 @@ class Reference(DataHandler):
|
||||||
if not target_class.is_exist(value):
|
if not target_class.is_exist(value):
|
||||||
logger.warning('Object referenced does not exist')
|
logger.warning('Object referenced does not exist')
|
||||||
return False
|
return False
|
||||||
#target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here
|
# target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here
|
||||||
#obj = target_class.get([(target_uidfield, '=', value)])
|
# obj = target_class.get([(target_uidfield, '=', value)])
|
||||||
#if len(obj) == 0:
|
# if len(obj) == 0:
|
||||||
# logger.warning('Object referenced does not exist')
|
# logger.warning('Object referenced does not exist')
|
||||||
# return False
|
# return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##@brief Utility method designed to fetch referenced objects
|
## @brief Utility method designed to fetch referenced objects
|
||||||
#@param value mixed : the field value
|
# @param value mixed : the field value
|
||||||
#@throw NotImplementedError
|
# @throw NotImplementedError
|
||||||
def get_referenced(self, value):
|
def get_referenced(self, value):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
##@brief This class represent a data_handler for single reference to another object
|
## @brief This class represent a data_handler for single reference to another object
|
||||||
#
|
#
|
||||||
# The fields using this data handlers are like "foreign key" on another object
|
# The fields using this data handlers are like "foreign key" on another object
|
||||||
class SingleRef(Reference):
|
class SingleRef(Reference):
|
||||||
|
|
@ -356,18 +414,18 @@ class SingleRef(Reference):
|
||||||
super().__init__(allowed_classes=allowed_classes, **kwargs)
|
super().__init__(allowed_classes=allowed_classes, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value: *
|
# @param value: *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
##@brief Utility method designed to fetch referenced objects
|
## @brief Utility method designed to fetch referenced objects
|
||||||
#@param value mixed : the field value
|
# @param value mixed : the field value
|
||||||
#@return A LeObject child class instance
|
# @return A LeObject child class instance
|
||||||
#@throw LodelDataHandlerConsistencyException if no referenced object found
|
# @throw LodelDataHandlerConsistencyException if no referenced object found
|
||||||
def get_referenced(self, value):
|
def get_referenced(self, value):
|
||||||
for leo_cls in self.linked_classes:
|
for leo_cls in self.linked_classes:
|
||||||
res = leo_cls.get_from_uid(value)
|
res = leo_cls.get_from_uid(value)
|
||||||
|
|
@ -377,30 +435,30 @@ class SingleRef(Reference):
|
||||||
referenced object with uid %s" % value)
|
referenced object with uid %s" % value)
|
||||||
|
|
||||||
|
|
||||||
##@brief This class represent a data_handler for multiple references to another object
|
## @brief This class represent a data_handler for multiple references to another object
|
||||||
#@ingroup lodel2_datahandlers
|
# @ingroup lodel2_datahandlers
|
||||||
#
|
#
|
||||||
# The fields using this data handlers are like SingleRef but can store multiple references in one field
|
# The fields using this data handlers are like SingleRef but can store multiple references in one field
|
||||||
# @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
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
##@brief Method designed to return an empty value for this kind of
|
## @brief Method designed to return an empty value for this kind of
|
||||||
#multipleref
|
# multipleref
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
#@TODO Writing test error for errors when stored multiple references in one field
|
# @TODO Writing test error for errors when stored multiple references in one field
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = DataHandler._check_data_value(self, value)
|
value = DataHandler._check_data_value(self, value)
|
||||||
if not hasattr(value, '__iter__'):
|
if not hasattr(value, '__iter__'):
|
||||||
|
|
@ -408,7 +466,7 @@ class MultipleRef(Reference):
|
||||||
if self.max_item is not None:
|
if self.max_item is not None:
|
||||||
if self.max_item < len(value):
|
if self.max_item < len(value):
|
||||||
raise FieldValidationError("Too many items")
|
raise FieldValidationError("Too many items")
|
||||||
new_val = list()
|
new_val = list()
|
||||||
error_list = list()
|
error_list = list()
|
||||||
for i, v in enumerate(value):
|
for i, v in enumerate(value):
|
||||||
try:
|
try:
|
||||||
|
|
@ -420,11 +478,11 @@ class MultipleRef(Reference):
|
||||||
raise FieldValidationError("MultipleRef have for invalid values [%s] :" % (",".join(error_list)))
|
raise FieldValidationError("MultipleRef have for invalid values [%s] :" % (",".join(error_list)))
|
||||||
return new_val
|
return new_val
|
||||||
|
|
||||||
##@brief Utility method designed to fetch referenced objects
|
## @brief Utility method designed to fetch referenced objects
|
||||||
#@param values mixed : the field values
|
# @param values mixed : the field values
|
||||||
#@return A list of LeObject child class instance
|
# @return A list of LeObject child class instance
|
||||||
#@throw LodelDataHandlerConsistencyException if some referenced objects
|
# @throw LodelDataHandlerConsistencyException if some referenced objects
|
||||||
#were not found
|
# were not found
|
||||||
def get_referenced(self, values):
|
def get_referenced(self, values):
|
||||||
if values is None or len(values) == 0:
|
if values is None or len(values) == 0:
|
||||||
return list()
|
return list()
|
||||||
|
|
@ -432,18 +490,19 @@ class MultipleRef(Reference):
|
||||||
values = set(values)
|
values = set(values)
|
||||||
res = list()
|
res = list()
|
||||||
for leo_cls in self.linked_classes:
|
for leo_cls in self.linked_classes:
|
||||||
uidname = leo_cls.uid_fieldname()[0] #MULTIPLE UID BROKEN HERE
|
uidname = leo_cls.uid_fieldname()[0] # MULTIPLE UID BROKEN HERE
|
||||||
tmp_res = leo_cls.get(('%s in (%s)' % (uidname, ','.join(
|
tmp_res = leo_cls.get(('%s in (%s)' % (uidname, ','.join(
|
||||||
[str(l) for l in left]))))
|
[str(l) for l in left]))))
|
||||||
left ^= set(( leo.uid() for leo in tmp_res))
|
left ^= set((leo.uid() for leo in tmp_res))
|
||||||
res += tmp_res
|
res += tmp_res
|
||||||
if len(left) == 0:
|
if len(left) == 0:
|
||||||
return res
|
return res
|
||||||
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
|
||||||
#
|
#
|
||||||
# This class is designed to allow automatic scheduling of construct_data calls.
|
# This class is designed to allow automatic scheduling of construct_data calls.
|
||||||
#
|
#
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ from lodel.context import LodelContext
|
||||||
|
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar',
|
'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar',
|
||||||
'DateTime', 'Text', 'File'],
|
'DateTime', 'Text', 'File'],
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||||
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
||||||
|
|
||||||
|
|
||||||
##@brief Data field designed to handle formated strings
|
##@brief Data field designed to handle formated strings
|
||||||
|
|
@ -26,7 +26,7 @@ build its content'
|
||||||
def __init__(self, format_string, field_list, **kwargs):
|
def __init__(self, format_string, field_list, **kwargs):
|
||||||
self._field_list = field_list
|
self._field_list = field_list
|
||||||
self._format_string = format_string
|
self._format_string = format_string
|
||||||
super().__init__(internal='automatic',**kwargs)
|
super().__init__(internal='automatic', **kwargs)
|
||||||
|
|
||||||
def _construct_data(self, emcomponent, fname, datas, cur_value):
|
def _construct_data(self, emcomponent, fname, datas, cur_value):
|
||||||
ret = self._format_string % tuple(
|
ret = self._format_string % tuple(
|
||||||
|
|
@ -49,7 +49,7 @@ max_length and regex'
|
||||||
# @param **kwargs
|
# @param **kwargs
|
||||||
def __init__(self, regex='', max_length=10, **kwargs):
|
def __init__(self, regex='', max_length=10, **kwargs):
|
||||||
self.regex = regex
|
self.regex = regex
|
||||||
self.compiled_re = re.compile(regex)#trigger an error if invalid regex
|
self.compiled_re = re.compile(regex) # trigger an error if invalid regex
|
||||||
super(self.__class__, self).__init__(max_length=max_length, **kwargs)
|
super(self.__class__, self).__init__(max_length=max_length, **kwargs)
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
##@brief Check and cast value in appropriate type
|
||||||
|
|
@ -106,7 +106,8 @@ be internal")
|
||||||
if not inspect.isclass(emcomponent):
|
if not inspect.isclass(emcomponent):
|
||||||
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'
|
||||||
|
|
@ -116,11 +117,11 @@ class Concat(FormatString):
|
||||||
# @param field_list list : List of field to use
|
# @param field_list list : List of field to use
|
||||||
# @param separator str
|
# @param separator str
|
||||||
# @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
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,33 @@ from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.leapi.datahandlers.base_classes': ['DataField'],
|
'lodel.leapi.datahandlers.base_classes': ['DataField'],
|
||||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||||
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
||||||
|
|
||||||
|
|
||||||
##@brief Data field designed to handle boolean values
|
## @brief Data field designed to handle boolean values
|
||||||
class Boolean(DataField):
|
class Boolean(DataField):
|
||||||
|
|
||||||
help = 'A basic boolean field'
|
help = 'A basic boolean field'
|
||||||
base_type = 'bool'
|
base_type = 'bool'
|
||||||
|
|
||||||
##@brief A boolean field
|
## @brief A boolean field
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
#if 'check_data_value' not in kwargs:
|
#if 'check_data_value' not in kwargs:
|
||||||
# kwargs['check_data_value'] = self._check_data_value
|
# kwargs['check_data_value'] = self._check_data_value
|
||||||
super().__init__(ftype='bool', **kwargs)
|
super().__init__(ftype='bool', **kwargs)
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if not isinstance(value, bool):
|
if not isinstance(value, bool):
|
||||||
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):
|
||||||
|
|
||||||
help = 'Basic integer field'
|
help = 'Basic integer field'
|
||||||
|
|
@ -41,14 +42,14 @@ class Integer(DataField):
|
||||||
cast_type = int
|
cast_type = int
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__( **kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
# @param value *
|
# @param value *
|
||||||
# @param strict bool : tells if the value must be an integer or a value that can be converted into an integer
|
# @param strict bool : tells if the value must be an integer or a value that can be converted into an integer
|
||||||
# @throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
# @return value
|
# @return value
|
||||||
def _check_data_value(self, value, strict = False):
|
def _check_data_value(self, value, strict=False):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if (strict and not isinstance(value, int)):
|
if (strict and not isinstance(value, int)):
|
||||||
raise FieldValidationError("The value '%s' is not a python type integer" % value)
|
raise FieldValidationError("The value '%s' is not a python type integer" % value)
|
||||||
|
|
@ -61,19 +62,20 @@ 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):
|
||||||
|
|
||||||
help = 'Basic string (varchar) field. Default size is 64 characters'
|
help = 'Basic string (varchar) field. Default size is 64 characters'
|
||||||
base_type = 'char'
|
base_type = 'char'
|
||||||
|
|
||||||
##@brief A string field
|
## @brief A string field
|
||||||
# @brief max_length int: The maximum length of this field
|
# @brief max_length int: The maximum length of this field
|
||||||
def __init__(self, max_length=64, **kwargs):
|
def __init__(self, max_length=64, **kwargs):
|
||||||
self.max_length = int(max_length)
|
self.max_length = int(max_length)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
##@brief checks if this class can override the given data handler
|
## @brief checks if this class can override the given data handler
|
||||||
# @param data_handler DataHandler
|
# @param data_handler DataHandler
|
||||||
# @return bool
|
# @return bool
|
||||||
def can_override(self, data_handler):
|
def can_override(self, data_handler):
|
||||||
|
|
@ -83,25 +85,26 @@ class Varchar(DataField):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise FieldValidationError("The value '%s' can't be a str" % value)
|
raise FieldValidationError("The value '%s' can't be a str" % value)
|
||||||
if len(value) > self.max_length:
|
if len(value) > self.max_length:
|
||||||
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):
|
||||||
|
|
||||||
help = 'A datetime field. Take two boolean options now_on_update and now_on_create'
|
help = 'A datetime field. Take two boolean options now_on_update and now_on_create'
|
||||||
base_type = 'datetime'
|
base_type = 'datetime'
|
||||||
|
|
||||||
##@brief A datetime field
|
## @brief A datetime field
|
||||||
# @param now_on_update bool : If true, the date is set to NOW on update
|
# @param now_on_update bool : If true, the date is set to NOW on update
|
||||||
# @param now_on_create bool : If true, the date is set to NEW on creation
|
# @param now_on_create bool : If true, the date is set to NEW on creation
|
||||||
# @param **kwargs
|
# @param **kwargs
|
||||||
|
|
@ -111,13 +114,13 @@ class DateTime(DataField):
|
||||||
self.datetime_format = '%Y-%m-%d' if 'format' not in kwargs else kwargs['format']
|
self.datetime_format = '%Y-%m-%d' if 'format' not in kwargs else kwargs['format']
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if isinstance(value,str):
|
if isinstance(value, str):
|
||||||
try:
|
try:
|
||||||
value = datetime.datetime.fromtimestamp(time.mktime(time.strptime(value, self.datetime_format)))
|
value = datetime.datetime.fromtimestamp(time.mktime(time.strptime(value, self.datetime_format)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
@ -131,7 +134,8 @@ 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)'
|
||||||
base_type = 'text'
|
base_type = 'text'
|
||||||
|
|
@ -139,22 +143,23 @@ class Text(DataField):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(self.__class__, self).__init__(ftype='text', **kwargs)
|
super(self.__class__, self).__init__(ftype='text', **kwargs)
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
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):
|
||||||
|
|
||||||
base_type = 'file'
|
base_type = 'file'
|
||||||
|
|
||||||
##@brief a file field
|
## @brief a file field
|
||||||
# @param upload_path str : None by default
|
# @param upload_path str : None by default
|
||||||
# @param **kwargs
|
# @param **kwargs
|
||||||
def __init__(self, upload_path=None, **kwargs):
|
def __init__(self, upload_path=None, **kwargs):
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -3,35 +3,37 @@
|
||||||
from lodel.context import LodelContext
|
from lodel.context import LodelContext
|
||||||
LodelContext.expose_modules(globals(), {
|
LodelContext.expose_modules(globals(), {
|
||||||
'lodel.leapi.datahandlers.base_classes': ['Reference', 'MultipleRef',
|
'lodel.leapi.datahandlers.base_classes': ['Reference', 'MultipleRef',
|
||||||
'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
|
||||||
|
|
||||||
|
|
||||||
##@brief Child class of MultipleRef where references are represented in the form of a python list
|
## @brief Child class of MultipleRef where references are represented in the form of a python list
|
||||||
class List(MultipleRef):
|
class List(MultipleRef):
|
||||||
|
|
||||||
##@brief instanciates a list reference
|
## @brief instanciates a list reference
|
||||||
# @param max_length int
|
# @param max_length int
|
||||||
# @param kwargs
|
# @param kwargs
|
||||||
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
||||||
# - internal bool
|
# - internal bool
|
||||||
|
|
||||||
def __init__(self, max_length = None, **kwargs):
|
def __init__(self, max_length=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return list()
|
return list()
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
try:
|
try:
|
||||||
|
|
@ -39,12 +41,12 @@ 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):
|
||||||
|
|
||||||
##@brief instanciates a set reference
|
## @brief instanciates a set reference
|
||||||
# @param kwargs : named arguments
|
# @param kwargs : named arguments
|
||||||
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
||||||
# - internal bool : if False, the field is not internal
|
# - internal bool : if False, the field is not internal
|
||||||
|
|
@ -55,10 +57,10 @@ class Set(MultipleRef):
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
try:
|
try:
|
||||||
|
|
@ -66,11 +68,12 @@ class Set(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 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):
|
||||||
|
|
||||||
##@brief instanciates a dict reference
|
## @brief instanciates a dict reference
|
||||||
# @param kwargs : named arguments
|
# @param kwargs : named arguments
|
||||||
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
# - allowed_classes list | None : list of allowed em classes if None no restriction
|
||||||
# - internal bool : if False, the field is not internal
|
# - internal bool : if False, the field is not internal
|
||||||
|
|
@ -81,38 +84,40 @@ class Map(MultipleRef):
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
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
|
||||||
# @param max_childs int | Nine : maximum number of childs by nodes
|
# @param max_childs int | Nine : maximum number of childs by nodes
|
||||||
def __init__(self, back_reference, max_depth = None, max_childs = None, **kwargs):
|
def __init__(self, back_reference, max_depth=None, max_childs=None, **kwargs):
|
||||||
super().__init__( back_reference = back_reference,
|
super().__init__(back_reference=back_reference,
|
||||||
max_depth = max_depth,
|
max_depth=max_depth,
|
||||||
max_childs = max_childs,
|
max_childs=max_childs,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return tuple()
|
return tuple()
|
||||||
|
|
||||||
##@brief Check and cast value in appropriate type
|
## @brief Check and cast value in appropriate type
|
||||||
#@param value *
|
# @param value *
|
||||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
# @throw FieldValidationError if value is unappropriate or can not be cast
|
||||||
#@return value
|
# @return value
|
||||||
def _check_data_value(self, value):
|
def _check_data_value(self, value):
|
||||||
value = super()._check_data_value(value)
|
value = super()._check_data_value(value)
|
||||||
if not (isinstance(value, list) or isinstance(value, str)):
|
if not (isinstance(value, list) or isinstance(value, str)):
|
||||||
|
|
|
||||||
|
|
@ -11,77 +11,79 @@ LodelContext.expose_modules(globals(), {
|
||||||
'lodel.settings': 'Settings',
|
'lodel.settings': 'Settings',
|
||||||
'lodel.settings.utils': 'SettingsError',
|
'lodel.settings.utils': 'SettingsError',
|
||||||
'lodel.leapi.query': ['LeInsertQuery', 'LeUpdateQuery', 'LeDeleteQuery',
|
'lodel.leapi.query': ['LeInsertQuery', 'LeUpdateQuery', 'LeDeleteQuery',
|
||||||
'LeGetQuery'],
|
'LeGetQuery'],
|
||||||
'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors',
|
'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors',
|
||||||
'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
|
'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
|
||||||
'LeApiQueryErrors'],
|
'LeApiQueryErrors'],
|
||||||
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError',
|
||||||
'LodelScriptError', 'DatasourcePluginError'],
|
'LodelScriptError', 'DatasourcePluginError'],
|
||||||
'lodel.exceptions': ['LodelFatalError'],
|
'lodel.exceptions': ['LodelFatalError'],
|
||||||
'lodel.plugin.hooks': ['LodelHook'],
|
'lodel.plugin.hooks': ['LodelHook'],
|
||||||
'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
|
||||||
def __init__(self, fieldnames_callback, set_callback, get_callback):
|
def __init__(self, fieldnames_callback, set_callback, get_callback):
|
||||||
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
|
||||||
def __setattribute__(self, fname, fval):
|
def __setattribute__(self, fname, fval):
|
||||||
setter = super().__getattribute__('_setter')
|
setter = super().__getattribute__('_setter')
|
||||||
return setter(fname, fval)
|
return setter(fname, fval)
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
@ -105,7 +108,7 @@ class LeObject(object):
|
||||||
self.__initialized.append(uid_name)
|
self.__initialized.append(uid_name)
|
||||||
|
|
||||||
# Processing given fields
|
# Processing given fields
|
||||||
allowed_fieldnames = self.fieldnames(include_ro = False)
|
allowed_fieldnames = self.fieldnames(include_ro=False)
|
||||||
err_list = dict()
|
err_list = dict()
|
||||||
for fieldname, fieldval in kwargs.items():
|
for fieldname, fieldval in kwargs.items():
|
||||||
if fieldname not in allowed_fieldnames:
|
if fieldname not in allowed_fieldnames:
|
||||||
|
|
@ -119,39 +122,39 @@ class LeObject(object):
|
||||||
self.__datas[fieldname] = fieldval
|
self.__datas[fieldname] = fieldval
|
||||||
self.__initialized.append(fieldname)
|
self.__initialized.append(fieldname)
|
||||||
if len(err_list) > 0:
|
if len(err_list) > 0:
|
||||||
raise LeApiErrors(msg = "Unable to __init__ %s" % self.__class__,
|
raise LeApiErrors(msg="Unable to __init__ %s" % self.__class__,
|
||||||
exceptions = err_list)
|
exceptions=err_list)
|
||||||
self.__set_initialized()
|
self.__set_initialized()
|
||||||
|
|
||||||
#-----------------------------------#
|
#-----------------------------------#
|
||||||
# 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
|
||||||
def fieldnames(cls, include_ro = False):
|
def fieldnames(cls, include_ro=False):
|
||||||
if not include_ro:
|
if not include_ro:
|
||||||
return [ fname for fname in cls._fields if not cls._fields[fname].is_internal() ]
|
return [fname for fname in cls._fields if not cls._fields[fname].is_internal()]
|
||||||
else:
|
else:
|
||||||
return list(cls._fields.keys())
|
return list(cls._fields.keys())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
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
|
||||||
|
|
@ -160,18 +163,18 @@ class LeObject(object):
|
||||||
if not fieldname in cls._fields:
|
if not fieldname in cls._fields:
|
||||||
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
|
||||||
|
|
@ -183,14 +186,14 @@ class LeObject(object):
|
||||||
mod = importlib.import_module(cls.__module__)
|
mod = importlib.import_module(cls.__module__)
|
||||||
try:
|
try:
|
||||||
return getattr(mod, leobject_name)
|
return getattr(mod, leobject_name)
|
||||||
except (AttributeError, TypeError) :
|
except (AttributeError, TypeError):
|
||||||
raise LeApiError("No LeObject named '%s'" % leobject_name)
|
raise LeApiError("No LeObject named '%s'" % leobject_name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
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
|
||||||
|
|
@ -199,20 +202,22 @@ class LeObject(object):
|
||||||
try:
|
try:
|
||||||
return cls._fields[fieldname]
|
return cls._fields[fieldname]
|
||||||
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...
|
||||||
#@param cls
|
#@param cls
|
||||||
#@warning multiple inheritance broken by this method
|
#@warning multiple inheritance broken by this method
|
||||||
#@return a list of LeObject child classes
|
#@return a list of LeObject child classes
|
||||||
|
|
@ -222,23 +227,22 @@ class LeObject(object):
|
||||||
res = [cls]
|
res = [cls]
|
||||||
cur = cls
|
cur = cls
|
||||||
while True:
|
while True:
|
||||||
cur = cur.__bases__[0] # Multiple inheritance broken HERE
|
cur = cur.__bases__[0] # Multiple inheritance broken HERE
|
||||||
if cur in (LeObject, object):
|
if cur in (LeObject, object):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
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
|
||||||
@classmethod
|
@classmethod
|
||||||
def uid_source(cls):
|
def uid_source(cls):
|
||||||
|
|
@ -246,19 +250,19 @@ class LeObject(object):
|
||||||
return False
|
return False
|
||||||
hierarch = cls.hierarch()
|
hierarch = cls.hierarch()
|
||||||
prev = hierarch[0]
|
prev = hierarch[0]
|
||||||
uid_handlers = set( cls._fields[name] for name in cls._uid )
|
uid_handlers = set(cls._fields[name] for name in cls._uid)
|
||||||
for pcls in cls.hierarch()[1:]:
|
for pcls in cls.hierarch()[1:]:
|
||||||
puid_handlers = set(cls._fields[name] for name in pcls._uid)
|
puid_handlers = set(cls._fields[name] for name in pcls._uid)
|
||||||
if set(pcls._uid) != set(prev._uid) \
|
if set(pcls._uid) != set(prev._uid) \
|
||||||
or puid_handlers != uid_handlers:
|
or puid_handlers != uid_handlers:
|
||||||
break
|
break
|
||||||
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
|
||||||
#@see LeObject::_init_datasource()
|
#@see LeObject::_init_datasource()
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init_datasources(cls):
|
def _init_datasources(cls):
|
||||||
|
|
@ -266,7 +270,7 @@ class LeObject(object):
|
||||||
rw_ds = ro_ds = cls._datasource_name
|
rw_ds = ro_ds = cls._datasource_name
|
||||||
else:
|
else:
|
||||||
ro_ds, rw_ds = cls._datasource_name
|
ro_ds, rw_ds = cls._datasource_name
|
||||||
#Read only datasource initialisation
|
# Read only datasource initialisation
|
||||||
cls._ro_datasource = DatasourcePlugin.init_datasource(ro_ds, True)
|
cls._ro_datasource = DatasourcePlugin.init_datasource(ro_ds, True)
|
||||||
if cls._ro_datasource is None:
|
if cls._ro_datasource is None:
|
||||||
log_msg = "No read only datasource set for LeObject %s"
|
log_msg = "No read only datasource set for LeObject %s"
|
||||||
|
|
@ -276,7 +280,7 @@ class LeObject(object):
|
||||||
log_msg = "Read only datasource '%s' initialized for LeObject %s"
|
log_msg = "Read only datasource '%s' initialized for LeObject %s"
|
||||||
log_msg %= (ro_ds, cls.__name__)
|
log_msg %= (ro_ds, cls.__name__)
|
||||||
logger.debug(log_msg)
|
logger.debug(log_msg)
|
||||||
#Read write datasource initialisation
|
# Read write datasource initialisation
|
||||||
cls._rw_datasource = DatasourcePlugin.init_datasource(rw_ds, False)
|
cls._rw_datasource = DatasourcePlugin.init_datasource(rw_ds, False)
|
||||||
if cls._ro_datasource is None:
|
if cls._ro_datasource is None:
|
||||||
log_msg = "No read/write datasource set for LeObject %s"
|
log_msg = "No read/write datasource set for LeObject %s"
|
||||||
|
|
@ -286,14 +290,14 @@ class LeObject(object):
|
||||||
log_msg = "Read/write datasource '%s' initialized for LeObject %s"
|
log_msg = "Read/write datasource '%s' initialized for LeObject %s"
|
||||||
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
|
||||||
|
|
@ -320,7 +324,7 @@ class LeObject(object):
|
||||||
# @throw NameError if fname is not valid
|
# @throw NameError if fname is not valid
|
||||||
# @throw AttributeError if the field is not writtable
|
# @throw AttributeError if the field is not writtable
|
||||||
def set_data(self, fname, fval):
|
def set_data(self, fname, fval):
|
||||||
if fname not in self.fieldnames(include_ro = False):
|
if fname not in self.fieldnames(include_ro=False):
|
||||||
if fname not in self._fields.keys():
|
if fname not in self._fields.keys():
|
||||||
raise NameError("No such field in %s : %s" % (self.__class__.__name__, fname))
|
raise NameError("No such field in %s : %s" % (self.__class__.__name__, fname))
|
||||||
else:
|
else:
|
||||||
|
|
@ -342,23 +346,23 @@ class LeObject(object):
|
||||||
# We skip full validation here because the LeObject is not fully initialized yet
|
# We skip full validation here because the LeObject is not fully initialized yet
|
||||||
val, err = self._fields[fname].check_data_value(fval)
|
val, err = self._fields[fname].check_data_value(fval)
|
||||||
if isinstance(err, Exception):
|
if isinstance(err, Exception):
|
||||||
#Revert change to be in valid state
|
# Revert change to be in valid state
|
||||||
del(self.__datas[fname])
|
del(self.__datas[fname])
|
||||||
del(self.__initialized[-1])
|
del(self.__initialized[-1])
|
||||||
raise LeApiErrors("Data check error", {fname:err})
|
raise LeApiErrors("Data check error", {fname: err})
|
||||||
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):
|
||||||
if isinstance(self.__initialized, list):
|
if isinstance(self.__initialized, list):
|
||||||
expected_fields = self.fieldnames(include_ro = False) + self._uid
|
expected_fields = self.fieldnames(include_ro=False) + self._uid
|
||||||
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
|
||||||
|
|
@ -366,7 +370,7 @@ class LeObject(object):
|
||||||
err_list = dict()
|
err_list = dict()
|
||||||
if self.__initialized is True:
|
if self.__initialized is True:
|
||||||
# Data value check
|
# Data value check
|
||||||
for fname in self.fieldnames(include_ro = False):
|
for fname in self.fieldnames(include_ro=False):
|
||||||
val, err = self._fields[fname].check_data_value(self.__datas[fname])
|
val, err = self._fields[fname].check_data_value(self.__datas[fname])
|
||||||
if err is not None:
|
if err is not None:
|
||||||
err_list[fname] = err
|
err_list[fname] = err
|
||||||
|
|
@ -374,19 +378,19 @@ class LeObject(object):
|
||||||
self.__datas[fname] = val
|
self.__datas[fname] = val
|
||||||
# Data construction
|
# Data construction
|
||||||
if len(err_list) == 0:
|
if len(err_list) == 0:
|
||||||
for fname in self.fieldnames(include_ro = True):
|
for fname in self.fieldnames(include_ro=True):
|
||||||
try:
|
try:
|
||||||
field = self._fields[fname]
|
field = self._fields[fname]
|
||||||
self.__datas[fname] = field.construct_data( self,
|
self.__datas[fname] = field.construct_data(self,
|
||||||
fname,
|
fname,
|
||||||
self.__datas,
|
self.__datas,
|
||||||
self.__datas[fname]
|
self.__datas[fname]
|
||||||
)
|
)
|
||||||
except Exception as exp:
|
except Exception as exp:
|
||||||
err_list[fname] = exp
|
err_list[fname] = exp
|
||||||
# Datas consistency check
|
# Datas consistency check
|
||||||
if len(err_list) == 0:
|
if len(err_list) == 0:
|
||||||
for fname in self.fieldnames(include_ro = True):
|
for fname in self.fieldnames(include_ro=True):
|
||||||
field = self._fields[fname]
|
field = self._fields[fname]
|
||||||
ret = field.check_data_consistency(self, fname, self.__datas)
|
ret = field.check_data_consistency(self, fname, self.__datas)
|
||||||
if isinstance(ret, Exception):
|
if isinstance(ret, Exception):
|
||||||
|
|
@ -404,8 +408,8 @@ 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
|
||||||
|
|
@ -415,8 +419,8 @@ class LeObject(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
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
|
||||||
|
|
@ -424,10 +428,10 @@ class LeObject(object):
|
||||||
# @return Checked datas
|
# @return Checked datas
|
||||||
# @throw LeApiDataCheckError if errors reported during check
|
# @throw LeApiDataCheckError if errors reported during check
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_datas_value(cls, datas, complete = False, allow_internal = True):
|
def check_datas_value(cls, datas, complete=False, allow_internal=True):
|
||||||
err_l = dict() #Error storing
|
err_l = dict() # Error storing
|
||||||
correct = set() #valid fields name
|
correct = set() # valid fields name
|
||||||
mandatory = set() #mandatory fields name
|
mandatory = set() # mandatory fields name
|
||||||
for fname, datahandler in cls._fields.items():
|
for fname, datahandler in cls._fields.items():
|
||||||
if allow_internal or not datahandler.is_internal():
|
if allow_internal or not datahandler.is_internal():
|
||||||
correct.add(fname)
|
correct.add(fname)
|
||||||
|
|
@ -436,15 +440,15 @@ class LeObject(object):
|
||||||
provided = set(datas.keys())
|
provided = set(datas.keys())
|
||||||
# searching for unknow fields
|
# searching for unknow fields
|
||||||
for u_f in provided - correct:
|
for u_f in provided - correct:
|
||||||
#Here we can check if the field is invalid or rejected because
|
# Here we can check if the field is invalid or rejected because
|
||||||
# it is internel
|
# it is internel
|
||||||
err_l[u_f] = AttributeError("Unknown or unauthorized field '%s'" % u_f)
|
err_l[u_f] = AttributeError("Unknown or unauthorized field '%s'" % u_f)
|
||||||
# searching for missing mandatory fieldsa
|
# searching for missing mandatory fieldsa
|
||||||
for missing in mandatory - provided:
|
for missing in mandatory - provided:
|
||||||
err_l[missing] = AttributeError("The data for field '%s' is missing" % missing)
|
err_l[missing] = AttributeError("The data for field '%s' is missing" % missing)
|
||||||
#Checks datas
|
# Checks datas
|
||||||
checked_datas = dict()
|
checked_datas = dict()
|
||||||
for name, value in [ (name, value) for name, value in datas.items() if name in correct ]:
|
for name, value in [(name, value) for name, value in datas.items() if name in correct]:
|
||||||
dh = cls._fields[name]
|
dh = cls._fields[name]
|
||||||
res = dh.check_data_value(value)
|
res = dh.check_data_value(value)
|
||||||
checked_datas[name], err = res
|
checked_datas[name], err = res
|
||||||
|
|
@ -455,10 +459,10 @@ 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()
|
||||||
#
|
#
|
||||||
# @param datas dict : {fieldname : fieldvalue, ...}
|
# @param datas dict : {fieldname : fieldvalue, ...}
|
||||||
# @param complete bool : If True you MUST give all the datas
|
# @param complete bool : If True you MUST give all the datas
|
||||||
# @param allow_internal : Wether or not interal fields are expected in datas
|
# @param allow_internal : Wether or not interal fields are expected in datas
|
||||||
|
|
@ -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
|
||||||
|
|
@ -488,13 +492,13 @@ construction and consitency when datas are not complete\n")
|
||||||
def _construct_datas(cls, datas):
|
def _construct_datas(cls, datas):
|
||||||
constructor = DatasConstructor(cls, datas, cls._fields)
|
constructor = DatasConstructor(cls, datas, cls._fields)
|
||||||
ret = {
|
ret = {
|
||||||
fname:constructor[fname]
|
fname: constructor[fname]
|
||||||
for fname, ftype in cls._fields.items()
|
for fname, ftype in cls._fields.items()
|
||||||
if not ftype.is_internal() or ftype.internal != 'autosql'
|
if not ftype.is_internal() or ftype.internal != 'autosql'
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
@ -511,29 +515,29 @@ 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
|
||||||
# @param datas dict : Datas that have been returned by LeCrud.prepare_datas() method
|
# @param datas dict : Datas that have been returned by LeCrud.prepare_datas() method
|
||||||
# @param type_query str : Type of query to be performed , default value : insert
|
# @param type_query str : Type of query to be performed , default value : insert
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_consistency(cls, datas, type_query = 'insert'):
|
def make_consistency(cls, datas, type_query='insert'):
|
||||||
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):
|
||||||
datas = self.datas(internal=False) if datas is None else datas
|
datas = self.datas(internal=False) if datas is None else datas
|
||||||
uids = self._uid
|
uids = self._uid
|
||||||
query_filter = list()
|
query_filter = list()
|
||||||
|
|
@ -543,15 +547,15 @@ construction and consitency when datas are not complete\n")
|
||||||
query = LeUpdateQuery(self.__class__, query_filter)
|
query = LeUpdateQuery(self.__class__, query_filter)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = query.execute(datas)
|
result = query.execute(datas)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
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):
|
||||||
|
|
@ -565,8 +569,8 @@ construction and consitency when datas are not complete\n")
|
||||||
result = query.execute()
|
result = query.execute()
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -576,7 +580,7 @@ construction and consitency when datas are not complete\n")
|
||||||
query = LeDeleteQuery(cls, query_filters)
|
query = LeDeleteQuery(cls, query_filters)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = query.execute()
|
result = query.execute()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
|
@ -584,11 +588,11 @@ construction and consitency when datas are not complete\n")
|
||||||
if not result is None:
|
if not result is None:
|
||||||
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
|
||||||
#@ref leobject_filters
|
#@ref leobject_filters
|
||||||
#@param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
#@param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
||||||
#@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
#@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
||||||
|
|
@ -598,49 +602,49 @@ construction and consitency when datas are not complete\n")
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
|
def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
|
||||||
if field_list is not None:
|
if field_list is not None:
|
||||||
for uid in [ uidname
|
for uid in [uidname
|
||||||
for uidname in cls.uid_fieldname()
|
for uidname in cls.uid_fieldname()
|
||||||
if uidname not in field_list ]:
|
if uidname not in field_list]:
|
||||||
field_list.append(uid)
|
field_list.append(uid)
|
||||||
if CLASS_ID_FIELDNAME not in field_list:
|
if CLASS_ID_FIELDNAME not in field_list:
|
||||||
field_list.append(CLASS_ID_FIELDNAME)
|
field_list.append(CLASS_ID_FIELDNAME)
|
||||||
try:
|
try:
|
||||||
query = LeGetQuery(
|
query = LeGetQuery(
|
||||||
cls, query_filters = query_filters, field_list = field_list,
|
cls, query_filters=query_filters, field_list=field_list,
|
||||||
order = order, group = group, limit = limit, offset = offset)
|
order=order, group=group, limit=limit, offset=offset)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = query.execute()
|
result = query.execute()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
objects = list()
|
objects = list()
|
||||||
for res in result:
|
for res in result:
|
||||||
res_cls = cls.name2class(res[CLASS_ID_FIELDNAME])
|
res_cls = cls.name2class(res[CLASS_ID_FIELDNAME])
|
||||||
inst = res_cls.__new__(res_cls,**res)
|
inst = res_cls.__new__(res_cls, **res)
|
||||||
objects.append(inst)
|
objects.append(inst)
|
||||||
|
|
||||||
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):
|
||||||
if cls.uid_fieldname() is None:
|
if cls.uid_fieldname() is None:
|
||||||
raise LodelFatalError(
|
raise LodelFatalError(
|
||||||
"No uid defined for class %s" % cls.__name__)
|
"No uid defined for class %s" % cls.__name__)
|
||||||
uidname = cls.uid_fieldname()[0] #Brokes composed UID
|
uidname = cls.uid_fieldname()[0] # Brokes composed UID
|
||||||
res = cls.get([(uidname,'=', uid)])
|
res = cls.get([(uidname, '=', uid)])
|
||||||
|
|
||||||
#dedoublonnage vu que query ou la datasource est bugué
|
# dedoublonnage vu que query ou la datasource est bugué
|
||||||
if len(res) > 1:
|
if len(res) > 1:
|
||||||
res_cp = res
|
res_cp = res
|
||||||
res = []
|
res = []
|
||||||
while len(res_cp) > 0:
|
while len(res_cp) > 0:
|
||||||
cur_res = res_cp.pop()
|
cur_res = res_cp.pop()
|
||||||
if cur_res.uid() in [ r.uid() for r in res_cp]:
|
if cur_res.uid() in [r.uid() for r in res_cp]:
|
||||||
logger.error("DOUBLON detected in query results !!!")
|
logger.error("DOUBLON detected in query results !!!")
|
||||||
else:
|
else:
|
||||||
res.append(cur_res)
|
res.append(cur_res)
|
||||||
|
|
@ -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')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
{% set objects = my_classes.Issue.get(('%s = %s') % (uidfield, lodel_id)) %}
|
{% set objects = my_classes.Issue.get(('%s = %s') % (uidfield, lodel_id)) %}
|
||||||
{% set person_class = leapi.name2class('Person') %}
|
{% set person_class = leapi.name2class('Person') %}
|
||||||
{% set obj = objects.pop() %}
|
{% set obj = objects.pop() %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/{{ root_url }}/">Home</a></li>
|
<li><a href="/{{ root_url }}/">Home</a></li>
|
||||||
<li><a href="/{{ root_url }}/collection">Collections</a></li>
|
<li><a href="/{{ root_url }}/collection">Collections</a></li>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -84,4 +84,4 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -5,40 +5,42 @@ import os
|
||||||
import configparser
|
import configparser
|
||||||
import copy
|
import copy
|
||||||
import warnings
|
import warnings
|
||||||
import types # for dynamic bindings
|
import types # for dynamic bindings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from lodel.context import LodelContext
|
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
|
||||||
#
|
#
|
||||||
# @par Basic instance usage
|
# @par Basic instance usage
|
||||||
# For example if a file defines confs like :
|
# For example if a file defines confs like :
|
||||||
# <pre>
|
# <pre>
|
||||||
|
|
@ -50,15 +52,15 @@ class MetaSettings(type):
|
||||||
#
|
#
|
||||||
# @par Init sequence
|
# @par Init sequence
|
||||||
# The initialization sequence is a bit tricky. In fact, plugins adds allowed
|
# 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.
|
# settings.
|
||||||
# Here is the conceptual presentation of Settings class initialization stages :
|
# Here is the conceptual presentation of Settings class initialization stages :
|
||||||
# -# Preloading (sets values like lodel2 library path or the plugins path)
|
# -# Preloading (sets values like lodel2 library path or the plugins path)
|
||||||
# -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all
|
# -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all
|
||||||
#configurations files
|
# configurations files
|
||||||
# -# Fetch the list of plugins in the loaded settings
|
# -# Fetch the list of plugins in the loaded settings
|
||||||
# -# Merge plugins settings specification with the global lodel settings
|
# -# Merge plugins settings specification with the global lodel settings
|
||||||
#specs ( see @ref lodel.plugin )
|
# specs ( see @ref lodel.plugin )
|
||||||
# -# Fetch all settings from the merged settings specs
|
# -# Fetch all settings from the merged settings specs
|
||||||
#
|
#
|
||||||
# @par Init sequence in practical
|
# @par Init sequence in practical
|
||||||
|
|
@ -68,39 +70,41 @@ class MetaSettings(type):
|
||||||
# -# @ref Settings.__populate_from_specs() (step 5)
|
# -# @ref Settings.__populate_from_specs() (step 5)
|
||||||
# -# And finally @ref Settings.__confs_to_namedtuple()
|
# -# 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
|
# @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,17 +128,17 @@ 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
|
||||||
@classmethod
|
@classmethod
|
||||||
def set(cls, confname, confvalue,validator):
|
def set(cls, confname, confvalue, validator):
|
||||||
loader = SettingsLoader(cls.instance.__conf_dir)
|
loader = SettingsLoader(cls.instance.__conf_dir)
|
||||||
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']})
|
||||||
|
|
@ -144,9 +148,9 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
else:
|
else:
|
||||||
lodel2_specs = self.__conf_specs
|
lodel2_specs = self.__conf_specs
|
||||||
self.__conf_specs = None
|
self.__conf_specs = None
|
||||||
loader = SettingsLoader(self.__conf_dir)
|
loader = SettingsLoader(self.__conf_dir)
|
||||||
plugin_list = []
|
plugin_list = []
|
||||||
for ptype_name,ptype in Plugin.plugin_types().items():
|
for ptype_name, ptype in Plugin.plugin_types().items():
|
||||||
pls = ptype.plist_confspecs()
|
pls = ptype.plist_confspecs()
|
||||||
lodel2_specs = confspec_append(lodel2_specs, **pls)
|
lodel2_specs = confspec_append(lodel2_specs, **pls)
|
||||||
cur_list = loader.getoption(
|
cur_list = loader.getoption(
|
||||||
|
|
@ -162,13 +166,15 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
plugin_list += cur_list
|
plugin_list += cur_list
|
||||||
except TypeError:
|
except TypeError:
|
||||||
plugin_list += [cur_list]
|
plugin_list += [cur_list]
|
||||||
#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")
|
||||||
|
|
@ -181,13 +187,13 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
specs.append(Plugin.get(plugin_name).confspecs)
|
specs.append(Plugin.get(plugin_name).confspecs)
|
||||||
except PluginError as e:
|
except PluginError as e:
|
||||||
errors.append(SettingsError(msg=str(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)
|
raise SettingsErrors(errors)
|
||||||
self.__conf_specs = self.__merge_specs(specs)
|
self.__conf_specs = self.__merge_specs(specs)
|
||||||
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,31 +204,35 @@ 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
|
||||||
# @param loader SettingsLoader : A SettingsLoader instance
|
# @param loader SettingsLoader : A SettingsLoader instance
|
||||||
def __populate_from_specs(self, specs, loader):
|
def __populate_from_specs(self, specs, loader):
|
||||||
self.__confs = dict()
|
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
|
# Construct final specs dict replacing variable sections
|
||||||
# by the actual existing 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:
|
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
|
||||||
|
|
@ -238,8 +248,8 @@ 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
|
||||||
|
|
@ -257,7 +267,7 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
section_name = ""
|
section_name = ""
|
||||||
cur = section_tree
|
cur = section_tree
|
||||||
for sec_part in spl:
|
for sec_part in spl:
|
||||||
section_name += sec_part+'.'
|
section_name += sec_part + '.'
|
||||||
if sec_part not in cur:
|
if sec_part not in cur:
|
||||||
cur[sec_part] = dict()
|
cur[sec_part] = dict()
|
||||||
cur = cur[sec_part]
|
cur = cur[sec_part]
|
||||||
|
|
@ -267,35 +277,35 @@ class Settings(object, metaclass=MetaSettings):
|
||||||
raise SettingsError("Duplicated key for '%s.%s'" % (section_name, kname))
|
raise SettingsError("Duplicated key for '%s.%s'" % (section_name, kname))
|
||||||
cur[kname] = kval
|
cur[kname] = kval
|
||||||
|
|
||||||
path = [ ('root', section_tree) ]
|
path = [('root', section_tree)]
|
||||||
visited = set()
|
visited = set()
|
||||||
|
|
||||||
curname = 'root'
|
curname = 'root'
|
||||||
nodename = 'Lodel2Settings'
|
nodename = 'Lodel2Settings'
|
||||||
cur = section_tree
|
cur = section_tree
|
||||||
while True:
|
while True:
|
||||||
visited.add(nodename)
|
visited.add(nodename)
|
||||||
left = [ (kname, cur[kname])
|
left = [(kname, cur[kname])
|
||||||
for kname in cur
|
for kname in cur
|
||||||
if nodename+'.'+kname.title() not in visited and isinstance(cur[kname], dict)
|
if nodename + '.' + kname.title() not in visited and isinstance(cur[kname], dict)
|
||||||
]
|
]
|
||||||
if len(left) == 0:
|
if len(left) == 0:
|
||||||
name, leaf = path.pop()
|
name, leaf = path.pop()
|
||||||
typename = nodename.replace('.', '')
|
typename = nodename.replace('.', '')
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
# END
|
# END
|
||||||
self.__confs = self.__tree2namedtuple(leaf,typename)
|
self.__confs = self.__tree2namedtuple(leaf, typename)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
path[-1][1][name] = self.__tree2namedtuple(leaf,typename)
|
path[-1][1][name] = self.__tree2namedtuple(leaf, typename)
|
||||||
nodename = '.'.join(nodename.split('.')[:-1])
|
nodename = '.'.join(nodename.split('.')[:-1])
|
||||||
cur = path[-1][1]
|
cur = path[-1][1]
|
||||||
else:
|
else:
|
||||||
curname, cur = left[0]
|
curname, cur = left[0]
|
||||||
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,28 +8,27 @@ 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):
|
||||||
|
|
||||||
## To avoid the DEFAULT section whose values are found in all sections, we
|
## To avoid the DEFAULT section whose values are found in all sections, we
|
||||||
# have to give it an unsual name
|
# have to give it an unsual name
|
||||||
DEFAULT_SECTION = 'lodel2_default_passaway_tip'
|
DEFAULT_SECTION = 'lodel2_default_passaway_tip'
|
||||||
|
|
||||||
## @brief Virtual filename when default value is used
|
## @brief Virtual filename when default value is used
|
||||||
DEFAULT_FILENAME = 'default_value'
|
DEFAULT_FILENAME = 'default_value'
|
||||||
|
|
||||||
##@brief Constructor
|
##@brief Constructor
|
||||||
# @param conf_path str : conf.d path
|
# @param conf_path str : conf.d path
|
||||||
def __init__(self,conf_path):
|
def __init__(self, conf_path):
|
||||||
self.__conf_path=conf_path
|
self.__conf_path = conf_path
|
||||||
self.__conf_sv=dict()
|
self.__conf_sv = dict()
|
||||||
self.__conf=self.__merge()
|
self.__conf = self.__merge()
|
||||||
# Stores errors
|
# Stores errors
|
||||||
self.__errors_list = []
|
self.__errors_list = []
|
||||||
|
|
||||||
##@brief Lists and merges files in settings_loader.conf_path
|
##@brief Lists and merges files in settings_loader.conf_path
|
||||||
# @return dict()
|
# @return dict()
|
||||||
def __merge(self):
|
def __merge(self):
|
||||||
|
|
@ -38,26 +37,24 @@ class SettingsLoader(object):
|
||||||
logger.debug("SettingsLoader found those settings files : %s" % (
|
logger.debug("SettingsLoader found those settings files : %s" % (
|
||||||
', '.join(l_dir)))
|
', '.join(l_dir)))
|
||||||
|
|
||||||
for f_ini in l_dir:
|
for f_ini in l_dir:
|
||||||
config = configparser.ConfigParser(default_section = self.DEFAULT_SECTION ,interpolation=None)
|
config = configparser.ConfigParser(default_section=self.DEFAULT_SECTION, interpolation=None)
|
||||||
config.read(f_ini)
|
config.read(f_ini)
|
||||||
for section in [ s for s in config if s != self.DEFAULT_SECTION ]:
|
for section in [s for s in config if s != self.DEFAULT_SECTION]:
|
||||||
if section not in conf:
|
if section not in conf:
|
||||||
conf[section] = dict()
|
conf[section] = dict()
|
||||||
for param in config[section]:
|
for param in config[section]:
|
||||||
if param not in conf[section]:
|
if param not in conf[section]:
|
||||||
conf[section][param]=dict()
|
conf[section][param] = dict()
|
||||||
conf[section][param]['value'] = config[section][param]
|
conf[section][param]['value'] = config[section][param]
|
||||||
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
|
||||||
# @param section str : name of the section
|
# @param section str : name of the section
|
||||||
# @param keyname str
|
# @param keyname str
|
||||||
|
|
@ -65,8 +62,8 @@ class SettingsLoader(object):
|
||||||
# @param default_value *
|
# @param default_value *
|
||||||
# @param mandatory bool
|
# @param mandatory bool
|
||||||
# @return the option
|
# @return the option
|
||||||
def getoption(self,section,keyname,validator,default_value=None,mandatory=False):
|
def getoption(self, section, keyname, validator, default_value=None, mandatory=False):
|
||||||
conf=self.__conf
|
conf = self.__conf
|
||||||
if section not in conf:
|
if section not in conf:
|
||||||
conf[section] = dict()
|
conf[section] = dict()
|
||||||
|
|
||||||
|
|
@ -85,35 +82,31 @@ 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
|
||||||
else:
|
else:
|
||||||
sec[keyname]=dict()
|
sec[keyname] = dict()
|
||||||
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, \
|
||||||
)
|
filename=sec[keyname]['file'])
|
||||||
expt2 = SettingsError( msg = str(expt),
|
|
||||||
key_id = section+'.'+keyname,
|
|
||||||
filename = sec[keyname]['file'])
|
|
||||||
self.__errors_list.append(expt2)
|
self.__errors_list.append(expt2)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -123,38 +116,38 @@ class SettingsLoader(object):
|
||||||
# @param value str
|
# @param value str
|
||||||
# @param validator callable : takes one argument value and raises validation fail
|
# @param validator callable : takes one argument value and raises validation fail
|
||||||
# @return the option
|
# @return the option
|
||||||
def setoption(self,section,keyname,value,validator):
|
def setoption(self, section, keyname, value, validator):
|
||||||
f_conf=copy.copy(self.__conf[section][keyname]['file'])
|
f_conf = copy.copy(self.__conf[section][keyname]['file'])
|
||||||
if f_conf == SettingsLoader.DEFAULT_FILENAME:
|
if f_conf == SettingsLoader.DEFAULT_FILENAME:
|
||||||
f_conf = self.__conf_path + '/generated.ini'
|
f_conf = self.__conf_path + '/generated.ini'
|
||||||
|
|
||||||
conf=self.__conf
|
conf = self.__conf
|
||||||
conf[section][keyname] = value
|
conf[section][keyname] = value
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read(f_conf)
|
config.read(f_conf)
|
||||||
if section not in config:
|
if section not in config:
|
||||||
config[section]={}
|
config[section] = {}
|
||||||
config[section][keyname] = validator(value)
|
config[section][keyname] = validator(value)
|
||||||
|
|
||||||
with open(f_conf, 'w') as configfile:
|
with open(f_conf, 'w') as configfile:
|
||||||
config.write(configfile)
|
config.write(configfile)
|
||||||
|
|
||||||
##@brief Saves new partial configuration. Writes in the conf files corresponding
|
##@brief Saves new partial configuration. Writes in the conf files corresponding
|
||||||
# @param sections dict
|
# @param sections dict
|
||||||
# @param validators dict of callable : takes one argument value and raises validation fail
|
# @param validators dict of callable : takes one argument value and raises validation fail
|
||||||
def saveconf(self, sections, validators):
|
def saveconf(self, sections, validators):
|
||||||
for sec in sections:
|
for sec in sections:
|
||||||
for kname in sections[sec]:
|
for kname in sections[sec]:
|
||||||
self.setoption(sec,kname,sections[sec][kname],validators[sec][kname])
|
self.setoption(sec, kname, sections[sec][kname], validators[sec][kname])
|
||||||
|
|
||||||
##@brief Returns the section to be configured
|
##@brief Returns the section to be configured
|
||||||
# @param section_prefix str
|
# @param section_prefix str
|
||||||
# @param default_section str
|
# @param default_section str
|
||||||
# @return the section as dict()
|
# @return the section as dict()
|
||||||
def getsection(self,section_prefix,default_section=None):
|
def getsection(self, section_prefix, default_section=None):
|
||||||
conf=copy.copy(self.__conf)
|
conf = copy.copy(self.__conf)
|
||||||
|
|
||||||
sections=[]
|
sections = []
|
||||||
if section_prefix in conf:
|
if section_prefix in conf:
|
||||||
sections.append(section_prefix)
|
sections.append(section_prefix)
|
||||||
for sect_names in conf:
|
for sect_names in conf:
|
||||||
|
|
@ -162,34 +155,33 @@ class SettingsLoader(object):
|
||||||
pass
|
pass
|
||||||
elif sect_names.startswith(section_prefix + '.'):
|
elif sect_names.startswith(section_prefix + '.'):
|
||||||
sections.append(sect_names)
|
sections.append(sect_names)
|
||||||
if sections == [] and default_section:
|
if sections == [] and default_section:
|
||||||
sections.append(section_prefix + '.' + default_section)
|
sections.append(section_prefix + '.' + default_section)
|
||||||
elif sections == []:
|
elif sections == []:
|
||||||
raise NameError("Not existing settings section : %s" % section_prefix)
|
raise NameError("Not existing settings section : %s" % section_prefix)
|
||||||
|
|
||||||
return sections
|
return sections
|
||||||
|
|
||||||
##@brief Returns invalid settings
|
##@brief Returns invalid settings
|
||||||
#
|
#
|
||||||
# This method returns all the settings that was not fecthed by
|
# This method returns all the settings that was not fecthed by
|
||||||
# getsection() method. For the Settings object it allows to know
|
# getsection() method. For the Settings object it allows to know
|
||||||
# the list of invalids settings keys
|
# the list of invalids settings keys
|
||||||
# @return a dict with SECTION_NAME+":"+KEY_NAME as key and the filename
|
# @return a dict with SECTION_NAME+":"+KEY_NAME as key and the filename
|
||||||
# where the settings was found as value
|
# where the settings was found as value
|
||||||
def getremains(self):
|
def getremains(self):
|
||||||
return self.__conf_sv
|
return self.__conf_sv
|
||||||
|
|
||||||
##@brief Raise a SettingsErrors exception if some confs remains
|
##@brief Raise a SettingsErrors exception if some confs remains
|
||||||
#@note typically used at the end of Settings bootstrap
|
#@note typically used at the end of Settings bootstrap
|
||||||
def raise_errors(self):
|
def raise_errors(self):
|
||||||
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ def get_utc_timestamp():
|
||||||
d = datetime.datetime.utcnow()
|
d = datetime.datetime.utcnow()
|
||||||
epoch = datetime.datetime(1970, 1, 1)
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
t = (d - epoch).total_seconds()
|
t = (d - epoch).total_seconds()
|
||||||
return t
|
return t
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ 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'
|
||||||
|
|
||||||
langs = [
|
langs = [
|
||||||
|
|
@ -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,11 +31,12 @@ 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
|
||||||
if not self.lang_is_valid(lang):
|
if not self.lang_is_valid(lang):
|
||||||
raise ValueError("Invalid lang : '%s'" % lang)
|
raise ValueError("Invalid lang : '%s'" % 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,16 +66,16 @@ 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:
|
||||||
return cls.__default_lang
|
return cls.__default_lang
|
||||||
if not cls.lang_is_valid(lang):
|
if not cls.lang_is_valid(lang):
|
||||||
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):
|
||||||
|
|
@ -89,13 +90,13 @@ class MlString(object):
|
||||||
def d_hash(self):
|
def d_hash(self):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
for lang in sorted(list(self.values.keys())):
|
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')
|
return int.from_bytes(m.digest(), byteorder='big')
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -70,76 +78,68 @@ class SettingValidator(object):
|
||||||
raise TypeError("Callable expected but got %s" % type(callback))
|
raise TypeError("Callable expected but got %s" % type(callback))
|
||||||
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
|
||||||
|
|
@ -225,37 +245,103 @@ def host_val(value):
|
||||||
try:
|
try:
|
||||||
socket.inet_aton(value)
|
socket.inet_aton(value)
|
||||||
return value
|
return value
|
||||||
except (TypeError,OSError):
|
except (TypeError, OSError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
socket.inet_pton(socket.AF_INET6, value)
|
socket.inet_pton(socket.AF_INET6, value)
|
||||||
return value
|
return value
|
||||||
except (TypeError,OSError):
|
except (TypeError, OSError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
socket.getaddrinfo(value, 80)
|
socket.getaddrinfo(value, 80)
|
||||||
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
|
||||||
classnames = { cls.__name__.lower():cls for cls in dyncode.dynclasses}
|
classnames = {cls.__name__.lower(): cls for cls in dyncode.dynclasses}
|
||||||
if value[0].lower() not in classnames:
|
if value[0].lower() not in classnames:
|
||||||
msg = "Following dynamic class do not exists in current EM : %s"
|
msg = "Following dynamic class do not exists in current EM : %s"
|
||||||
raise SettingsValidationError(msg % value[0])
|
raise SettingsValidationError(msg % value[0])
|
||||||
|
|
@ -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,12 +1,12 @@
|
||||||
#-*- 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
|
||||||
################
|
################
|
||||||
#
|
#
|
||||||
# @note We can pass the path to a directory to write results file, nocontext_tests.log
|
# @note We can pass the path to a directory to write results file, nocontext_tests.log
|
||||||
# It has to be at first, otherwise it will not be taken
|
# It has to be at first, otherwise it will not be taken
|
||||||
# and the default one, current directory, will be used.
|
# and the default one, current directory, will be used.
|
||||||
# The results are not displayed, only stored in nocontext_tests.log
|
# The results are not displayed, only stored in nocontext_tests.log
|
||||||
#
|
#
|
||||||
|
|
@ -19,20 +19,22 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, os, os.path
|
import sys
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
loader = unittest.TestLoader()
|
loader = unittest.TestLoader()
|
||||||
|
|
||||||
if ((len(sys.argv) > 1) and (sys.argv[1].startswith('-')) is False):
|
if ((len(sys.argv) > 1) and (sys.argv[1].startswith('-')) is False):
|
||||||
dpath = sys.argv[1]
|
dpath = sys.argv[1]
|
||||||
else:
|
else:
|
||||||
dpath = '.'
|
dpath = '.'
|
||||||
|
|
||||||
suite = loader.discover('tests', pattern='nc_test*.py')
|
suite = loader.discover('tests', pattern='nc_test*.py')
|
||||||
with open(dpath+'/nocontext_tests.log', 'w') as logfile:
|
with open(dpath + '/nocontext_tests.log', 'w') as logfile:
|
||||||
unittest.TextTestRunner(
|
unittest.TextTestRunner(
|
||||||
logfile,
|
logfile,
|
||||||
failfast = '-f' in sys.argv,
|
failfast='-f' in sys.argv,
|
||||||
verbosity = 2 if '-v' in sys.argv else 1).run(suite)
|
verbosity=2 if '-v' in sys.argv else 1).run(suite)
|
||||||
|
|
|
||||||
|
|
@ -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