diff --git a/configure.ac b/configure.ac index 2de1d57..a0de450 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,8 @@ AC_CONFIG_FILES([Makefile \ lodel/leapi/datahandlers/Makefile \ lodel/plugin/Makefile \ lodel/settings/Makefile \ + lodel/validator/Makefile \ + lodel/mlnamedobject/Makefile \ lodel/utils/Makefile \ progs/Makefile \ progs/slim/Makefile \ diff --git a/editorial_models/em_simple.py b/editorial_models/em_simple.py index 82b4d17..2f05fa9 100644 --- a/editorial_models/em_simple.py +++ b/editorial_models/em_simple.py @@ -272,7 +272,7 @@ article.new_field( 'author_note', group = editorial_group, data_handler = 'text' ) -# Classe Review +# Classe Review review = em.new_class( 'review', display_name = 'Review', group = editorial_group, @@ -439,7 +439,7 @@ issue.new_field( 'print_pub_date', }, data_handler = 'datetime', group = editorial_group, -) +) issue.new_field( 'e_pub_date', display_name = { 'eng': 'Electronic publication date', @@ -447,7 +447,7 @@ issue.new_field( 'e_pub_date', }, data_handler = 'datetime', group = editorial_group, -) +) issue.new_field( 'abstract', display_name = { 'eng': 'Abstract', @@ -455,7 +455,7 @@ issue.new_field( 'abstract', }, data_handler = 'text', group = editorial_group, -) +) issue.new_field( 'collection', display_name = { 'eng': 'Collection', @@ -691,7 +691,7 @@ user.new_field( 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' em.save('picklefile', filename = pickle_file) print("Output written in %s" % pickle_file) diff --git a/lodel/Makefile.am b/lodel/Makefile.am index e94c9b7..bef02b3 100644 --- a/lodel/Makefile.am +++ b/lodel/Makefile.am @@ -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 lodel_PYTHON = *.py CLEANFILES = buildconf.py diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index ff27133..2486d56 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -##@package lodel.editorial_model.components +# @package lodel.editorial_model.components #@brief Defines all @ref lodel2_em "EM" components #@ingroup lodel2_em @@ -12,28 +12,30 @@ import hashlib from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.utils.mlstring': ['MlString'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.settings': ['Settings'], 'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'], 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME']}) -##@brief Abstract class to represent editorial model components +# @brief Abstract class to represent editorial model components # @see EmClass EmField # @todo forbid '.' in uid #@ingroup lodel2_em -class EmComponent(object): - - ##@brief Instanciate an EmComponent + + +class EmComponent(MlNamedObject): + + # @brief Instanciate an EmComponent # @param uid str : uniq identifier # @param display_name MlString|str|dict : component display_name # @param help_text MlString|str|dict : help_text - def __init__(self, uid, display_name = None, help_text = None, group = None): + def __init__(self, uid, display_name=None, help_text=None, group=None): if self.__class__ == EmComponent: raise NotImplementedError('EmComponent is an abstract class') self.uid = uid - self.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 - + super().__init__(display_name, help_text) + def __str__(self): if self.display_name is None: return str(self.uid) @@ -42,34 +44,34 @@ class EmComponent(object): def d_hash(self): m = hashlib.md5() for data in ( - self.uid, - 'NODISPNAME' if self.display_name is None else str(self.display_name.d_hash()), - 'NOHELP' if self.help_text is None else str(self.help_text.d_hash()), - 'NOGROUP' if self.group is None else str(self.group.d_hash()), + self.uid, + 'NODISPNAME' if self.display_name is None else str(self.display_name.d_hash()), + 'NOHELP' if self.help_text is None else str(self.help_text.d_hash()), + 'NOGROUP' if self.group is None else str(self.group.d_hash()), ): m.update(bytes(data, 'utf-8')) return int.from_bytes(m.digest(), byteorder='big') -##@brief Handles editorial model objects classes +# @brief Handles editorial model objects classes #@ingroup lodel2_em class EmClass(EmComponent): - - ##@brief Instanciate a new EmClass + + # @brief Instanciate a new EmClass #@param uid str : uniq identifier #@param display_name MlString|str|dict : component display_name #@param abstract bool : set the class as asbtract if True #@param pure_abstract bool : if True the EmClass will not be represented in - #leapi dyncode + # leapi dyncode #@param parents list: parent EmClass list or uid list #@param help_text MlString|str|dict : help_text - #@param datasources str|tuple|list : The datasource name ( see + #@param datasources str|tuple|list : The datasource name ( see #@ref lodel2_datasources ) or two names (first is read_only datasource the - #second is read write) + # second is read write) def __init__( - self, uid, display_name = None, help_text = None, abstract = False, - parents = None, group = None, pure_abstract = False, - datasources = 'default'): + self, uid, display_name=None, help_text=None, abstract=False, + parents=None, group=None, pure_abstract=False, + datasources='default'): super().__init__(uid, display_name, help_text, group) self.abstract = bool(abstract) @@ -85,49 +87,50 @@ class EmClass(EmComponent): parents = [parents] for parent in parents: if not isinstance(parent, EmClass): - raise ValueError(" expected in parents list, but %s found" % type(parent)) + raise ValueError( + " expected in parents list, but %s found" % type(parent)) else: parents = list() self.parents = parents - ##@brief Stores EmFields instances indexed by field uid - self.__fields = dict() - + # @brief Stores EmFields instances indexed by field uid + self.__fields = dict() + self.group = group if group is None: warnings.warn("NO GROUP FOR EMCLASS %s" % uid) else: group.add_components([self]) - - #Adding common field + + # Adding common field if not self.abstract: self.new_field( CLASS_ID_FIELDNAME, - display_name = { + display_name={ 'eng': "LeObject subclass identifier", 'fre': "Identifiant de la class fille de LeObject"}, - help_text = { + help_text={ 'eng': "Allow to create instance of the good class when\ fetching arbitrary datas from DB"}, - data_handler = 'LeobjectSubclassIdentifier', - internal = True, - group = group) + data_handler='LeobjectSubclassIdentifier', + internal=True, + group=group) - ##@brief Property that represent a dict of all fields (the EmField defined in this class and all its parents) + # @brief Property that represent a dict of all fields (the EmField defined in this class and all its parents) # @todo use Settings.editorialmodel.groups to determine wich fields should be returned @property def __all_fields(self): res = dict() - for pfields in [ p.__all_fields for p in self.parents]: + for pfields in [p.__all_fields for p in self.parents]: res.update(pfields) res.update(self.__fields) return res - - ##@brief RO access to datasource attribute + + # @brief RO access to datasource attribute @property def datasource(self): return self.__datasource - ##@brief Return the list of all dependencies + # @brief Return the list of all dependencies # # Reccursive parents listing @property @@ -140,29 +143,29 @@ class EmClass(EmComponent): res |= parent.parents_recc return res - ##@brief EmField getter + # @brief EmField getter # @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance # @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes # @return A list on EmFields instances (if uid is None) else return an EmField instance # @todo use Settings.editorialmodel.groups to determine wich fields should be returned - def fields(self, uid = None, no_parents = False): + def fields(self, uid=None, no_parents=False): fields = self.__fields if no_parents else self.__all_fields try: return list(fields.values()) if uid is None else fields[uid] except KeyError: raise EditorialModelError("No such EmField '%s'" % uid) - - ##@brief Keep in __fields only fields contained in active groups + + # @brief Keep in __fields only fields contained in active groups def _set_active_fields(self, active_groups): if not Settings.editorialmodel.editormode: active_fields = [] for grp_name, agrp in active_groups.items(): - active_fields += [ emc for emc in agrp.components() - if isinstance(emc, EmField)] - self.__fields = { fname:fdh for fname, fdh in self.__fields.items() - if fdh in active_fields } + active_fields += [emc for emc in agrp.components() + if isinstance(emc, EmField)] + self.__fields = {fname: fdh for fname, fdh in self.__fields.items() + if fdh in active_fields} - ##@brief Add a field to the EmClass + # @brief Add a field to the EmClass # @param emfield EmField : an EmField instance # @warning do not add an EmField allready in another class ! # @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents) @@ -170,38 +173,40 @@ class EmClass(EmComponent): def add_field(self, emfield): assert_edit() if emfield.uid in self.__fields: - raise EditorialModelError("Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self)) + raise EditorialModelError( + "Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self)) # Incomplete field override check if emfield.uid in self.__all_fields: parent_field = self.__all_fields[emfield.uid] if not emfield.data_handler_instance.can_override(parent_field.data_handler_instance): - raise AttributeError("'%s' field override a parent field, but data_handles are not compatible" % emfield.uid) + raise AttributeError( + "'%s' field override a parent field, but data_handles are not compatible" % emfield.uid) self.__fields[emfield.uid] = emfield return emfield - - ##@brief Create a new EmField and add it to the EmClass + + # @brief Create a new EmField and add it to the EmClass # @param data_handler str : A DataHandler name # @param uid str : the EmField uniq id - # @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() ) + # @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() ) def new_field(self, uid, data_handler, **field_kwargs): assert_edit() return self.add_field(EmField(uid, data_handler, self, **field_kwargs)) def d_hash(self): m = hashlib.md5() - payload = str(super().d_hash()) + ("1" if self.abstract else "0") + payload = str(super().d_hash()) + ("1" if self.abstract else "0") for p in sorted(self.parents): payload += str(p.d_hash()) for fuid in sorted(self.__fields.keys()): payload += str(self.__fields[fuid].d_hash()) - + m.update(bytes(payload, 'utf-8')) return int.from_bytes(m.digest(), byteorder='big') def __str__(self): return "" % self.uid - + def __repr__(self): if not self.abstract: abstract = '' @@ -209,94 +214,95 @@ class EmClass(EmComponent): abstract = 'PureAbstract' else: abstract = 'Abstract' - return "" % (abstract, repr(self.uid) ) + return "" % (abstract, repr(self.uid)) -##@brief Handles editorial model classes fields +# @brief Handles editorial model classes fields #@ingroup lodel2_em class EmField(EmComponent): - ##@brief Instanciate a new EmField + # @brief Instanciate a new EmField # @param uid str : uniq identifier # @param display_name MlString|str|dict : field display_name # @param data_handler str : A DataHandler name # @param help_text MlString|str|dict : help text # @param group EmGroup : # @param **handler_kwargs : data handler arguments - def __init__(self, uid, data_handler, em_class = None, display_name = None, help_text = None, group = None, **handler_kwargs): + def __init__(self, uid, data_handler, em_class=None, display_name=None, help_text=None, group=None, **handler_kwargs): from lodel.leapi.datahandlers.base_classes import DataHandler super().__init__(uid, display_name, help_text, group) - ##@brief The data handler name + # @brief The data handler name self.data_handler_name = data_handler - ##@brief The data handler class + # @brief The data handler class self.data_handler_cls = DataHandler.from_name(data_handler) - ##@brief The data handler instance associated with this EmField + # @brief The data handler instance associated with this EmField self.data_handler_instance = self.data_handler_cls(**handler_kwargs) - ##@brief Stores data handler instanciation options + # @brief Stores data handler instanciation options self.data_handler_options = handler_kwargs - ##@brief Stores the emclass that contains this field (set by EmClass.add_field() method) + # @brief Stores the emclass that contains this field (set by EmClass.add_field() method) self._emclass = em_class if self._emclass is None: - warnings.warn("No EmClass for field %s" %uid) + warnings.warn("No EmClass for field %s" % uid) if group is None: warnings.warn("No EmGroup for field %s" % uid) else: group.add_components([self]) - ##@brief Returns data_handler_name attribute + # @brief Returns data_handler_name attribute def get_data_handler_name(self): return copy.copy(self.data_handler_name) - - ##@brief Returns data_handler_cls attribute + + # @brief Returns data_handler_cls attribute def get_data_handler_cls(self): - return copy.copy(selfdata_handler_cls) - + return copy.copy(self.data_handler_cls) + ##@brief Returne the uid of the emclass which contains this field def get_emclass_uid(self): return self._emclass.uid - + # @warning Not complete ! # @todo Complete the hash when data handlers becomes available def d_hash(self): return int.from_bytes(hashlib.md5( - bytes( - "%s%s%s" % ( super().d_hash(), - self.data_handler_name, - self.data_handler_options), - 'utf-8') + bytes( + "%s%s%s" % (super().d_hash(), + self.data_handler_name, + self.data_handler_options), + 'utf-8') ).digest(), byteorder='big') -##@brief Handles functionnal group of EmComponents +# @brief Handles functionnal group of EmComponents #@ingroup lodel2_em -class EmGroup(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 # @param uid str : Uniq identifier # @param depends list : A list of EmGroup dependencies - # @param display_name MlString|str : - # @param help_text MlString|str : - def __init__(self, uid, depends = None, display_name = None, help_text = None): + # @param display_name MlString|str : + # @param help_text MlString|str : + def __init__(self, uid, depends=None, display_name=None, help_text=None): self.uid = uid - ##@brief Stores the list of groups that depends on this EmGroup indexed by uid + # @brief Stores the list of groups that depends on this EmGroup indexed by uid self.required_by = dict() - ##@brief Stores the list of dependencies (EmGroup) indexed by uid + # @brief Stores the list of dependencies (EmGroup) indexed by uid self.require = dict() - ##@brief Stores the list of EmComponent instances contained in this group + # @brief Stores the list of EmComponent instances contained in this group self.__components = set() + super().__init__(display_name, help_text) - 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: for grp in depends: if not isinstance(grp, EmGroup): raise ValueError("EmGroup expected in depends argument but %s found" % grp) self.add_dependencie(grp) - - ##@brief Returns EmGroup dependencie + + # @brief Returns EmGroup dependencie # @param recursive bool : if True return all dependencies and their dependencies # @return a dict of EmGroup identified by uid - def dependencies(self, recursive = False): + def dependencies(self, recursive=False): res = copy.copy(self.require) if not recursive: return res @@ -308,11 +314,11 @@ class EmGroup(object): to_scan.append(new_dep) res[new_dep.uid] = new_dep return res - - ##@brief Returns EmGroup applicants + + # @brief Returns EmGroup applicants # @param recursive bool : if True return all dependencies and their dependencies # @returns a dict of EmGroup identified by uid - def applicants(self, recursive = False): + def applicants(self, recursive=False): res = copy.copy(self.required_by) if not recursive: return res @@ -324,29 +330,31 @@ class EmGroup(object): to_scan.append(new_app) res[new_app.uid] = new_app return res - - ##@brief Returns EmGroup components + + # @brief Returns EmGroup components # @returns a copy of the set of components def components(self): return (self.__components).copy() - ##@brief Returns EmGroup display_name + # @brief Returns EmGroup display_name # @param lang str | None : If None return default lang translation # @returns None if display_name is None, a str for display_name else def get_display_name(self, lang=None): - name=self.display_name - if name is None : return None - return name.get(lang); + name = self.display_name + if name is None: + return None + return name.get(lang) - ##@brief Returns EmGroup help_text + # @brief Returns EmGroup help_text # @param lang str | None : If None return default lang translation # @returns None if display_name is None, a str for display_name else def get_help_text(self, lang=None): - help=self.help_text - if help is None : return None - return help.get(lang); - - ##@brief Add components in a group + help = self.help_text + if help is None: + return None + return help.get(lang) + + # @brief Add components in a group # @param components list : EmComponent instances list def add_components(self, components): assert_edit() @@ -357,10 +365,11 @@ class EmGroup(object): msg %= (component, self) warnings.warn(msg) elif not isinstance(component, EmClass): - raise EditorialModelError("Expecting components to be a list of EmComponent, but %s found in the list" % type(component)) + raise EditorialModelError( + "Expecting components to be a list of EmComponent, but %s found in the list" % type(component)) self.__components |= set(components) - ##@brief Add a dependencie + # @brief Add a dependencie # @param em_group EmGroup|iterable : an EmGroup instance or list of instance def add_dependencie(self, grp): assert_edit() @@ -368,16 +377,17 @@ class EmGroup(object): for group in grp: self.add_dependencie(group) return - except TypeError: pass - + except TypeError: + pass + if grp.uid in self.require: return if self.__circular_dependencie(grp): raise EditorialModelError("Circular dependencie detected, cannot add dependencie") self.require[grp.uid] = grp grp.required_by[self.uid] = self - - ##@brief Add a applicant + + # @brief Add a applicant # @param em_group EmGroup|iterable : an EmGroup instance or list of instance # Useless ??? def add_applicant(self, grp): @@ -386,26 +396,27 @@ class EmGroup(object): for group in grp: self.add_applicant(group) return - except TypeError: pass - + except TypeError: + pass + if grp.uid in self.required_by: return if self.__circular_applicant(grp): raise EditorialModelError("Circular applicant detected, cannot add applicant") self.required_by[grp.uid] = grp grp.require[self.uid] = self - - ##@brief Search for circular dependencie + + # @brief Search for circular dependencie # @return True if circular dep found else False def __circular_dependencie(self, new_dep): return self.uid in new_dep.dependencies(True) - - ##@brief Search for circular applicant + + # @brief Search for circular applicant # @return True if circular app found else False def __circular_applicant(self, new_app): return self.uid in new_app.applicants(True) - ##@brief Fancy string representation of an EmGroup + # @brief Fancy string representation of an EmGroup # @return a string def __str__(self): if self.display_name is None: @@ -414,11 +425,11 @@ class EmGroup(object): return self.display_name.get() def d_hash(self): - + payload = "%s%s%s" % ( - self.uid, - 'NODNAME' if self.display_name is None else self.display_name.d_hash(), - 'NOHELP' if self.help_text is None else self.help_text.d_hash() + self.uid, + 'NODNAME' if self.display_name is None else self.display_name.d_hash(), + 'NOHELP' if self.help_text is None else self.help_text.d_hash() ) for recurs in (False, True): deps = self.dependencies(recurs) @@ -427,11 +438,11 @@ class EmGroup(object): for req_by_uid in self.required_by: payload += req_by_uid return int.from_bytes( - bytes(payload, 'utf-8'), - byteorder = 'big' + bytes(payload, 'utf-8'), + byteorder='big' ) - - ##@brief Complete string representation of an EmGroup + + # @brief Complete string representation of an EmGroup # @return a string def __repr__(self): - return "" % (self.uid, ', '.join([duid for duid in self.dependencies(False)]) ) + return "" % (self.uid, ', '.join([duid for duid in self.dependencies(False)])) diff --git a/lodel/editorial_model/model.py b/lodel/editorial_model/model.py index 5bdf0b0..bc05790 100644 --- a/lodel/editorial_model/model.py +++ b/lodel/editorial_model/model.py @@ -7,6 +7,7 @@ import copy from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.utils.mlstring': ['MlString'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.logger': 'logger', 'lodel.settings': ['Settings'], 'lodel.settings.utils': ['SettingsError'], @@ -14,29 +15,34 @@ LodelContext.expose_modules(globals(), { 'lodel.editorial_model.components': ['EmClass', 'EmField', 'EmGroup']}) -##@brief Describe an editorial model +# @brief Describe an editorial model #@ingroup lodel2_em -class EditorialModel(object): - - ##@brief Create a new editorial model +class EditorialModel(MlNamedObject): + + # @brief Create a new editorial model # @param name MlString|str|dict : the editorial model name # @param description MlString|str|dict : the editorial model description - def __init__(self, name, description = None): + def __init__(self, name, description=None, display_name=None, help_text=None): self.name = MlString(name) self.description = MlString(description) - ##@brief Stores all groups indexed by id + # @brief Stores all groups indexed by id self.__groups = dict() - ##@brief Stores all classes indexed by id + # @brief Stores all classes indexed by id self.__classes = dict() - ## @brief Stores all activated groups indexed by id + #  @brief Stores all activated groups indexed by id self.__active_groups = dict() - ## @brief Stores all activated classes indexed by id + #  @brief Stores all activated classes indexed by id self.__active_classes = dict() self.__set_actives() - - ##@brief EmClass uids accessor + if display_name is None: + display_name = name + if help_text is None: + help_text = description + super().__init__(display_name, help_text) + + # @brief EmClass uids accessor #@return a dict of emclasses - def all_classes(self, uid = None): + def all_classes(self, uid=None): if uid is None: return copy.copy(self.__classes) else: @@ -44,8 +50,8 @@ class EditorialModel(object): return copy.copy(self.__classes[uid]) except KeyError: raise EditorialModelException("EmClass not found : '%s'" % uid) - - def all_classes_ref(self, uid = None): + + def all_classes_ref(self, uid=None): if uid is None: return self.__classes else: @@ -53,16 +59,15 @@ class EditorialModel(object): return self.__classes[uid] except KeyError: raise EditorialModelException("EmGroup not found : '%s'" % uid) - - ##@brief active EmClass uids accessor + + # @brief active EmClass uids accessor #@return a list of class uids def active_classes_uids(self): - return list(self.__active_classes.keys()) - - - ##@brief EmGroups accessor + return list(self.__active_classes.keys()) + + # @brief EmGroups accessor #@return a dict of groups - def all_groups(self, uid = None): + def all_groups(self, uid=None): if uid is None: return copy.copy(self.__groups) else: @@ -70,10 +75,10 @@ class EditorialModel(object): return copy.copy(self.__groups[uid]) except KeyError: raise EditorialModelException("EmGroup not found : '%s'" % uid) - - ##@brief EmGroups accessor + + # @brief EmGroups accessor #@return a dict of groups - def all_groups_ref(self, uid = None): + def all_groups_ref(self, uid=None): if uid is None: return self.__groups else: @@ -81,26 +86,26 @@ class EditorialModel(object): return self.__groups[uid] except KeyError: raise EditorialModelException("EmGroup not found : '%s'" % uid) - - ##@brief active EmClass uids accessor + + # @brief active EmClass uids accessor #@return a list of class uids def active_groups_uids(self): - return list(self.__active_groups.keys()) + return list(self.__active_groups.keys()) - ##@brief EmClass accessor + # @brief EmClass accessor #@param uid None | str : give this argument to get a specific EmClass #@return if uid is given returns an EmClass else returns an EmClass # iterator #@todo use Settings.editorialmodel.groups to determine wich classes should # be returned - def classes(self, uid = None): + def classes(self, uid=None): try: - return self.__elt_getter( self.__active_classes, - uid) + return self.__elt_getter(self.__active_classes, + uid) except KeyError: raise EditorialModelException("EmClass not found : '%s'" % uid) - - ##@brief EmClass child list accessor + + # @brief EmClass child list accessor #@param uid str : the EmClass uid #@return a set of EmClass def get_class_childs(self, uid): @@ -111,24 +116,23 @@ class EditorialModel(object): res.append(cls) return set(res) - - ##@brief EmGroup getter + # @brief EmGroup getter # @param uid None | str : give this argument to get a specific EmGroup # @return if uid is given returns an EmGroup else returns an EmGroup iterator - def groups(self, uid = None): + def groups(self, uid=None): try: - return self.__elt_getter( self.__active_groups, - uid) + return self.__elt_getter(self.__active_groups, + uid) except KeyError: raise EditorialModelException("EmGroup not found : '%s'" % uid) - - ##@brief Private getter for __groups or __classes + + # @brief Private getter for __groups or __classes # @see classes() groups() def __elt_getter(self, elts, uid): return list(elts.values()) if uid is None else elts[uid] - - ##@brief Update the EditorialModel.__active_groups and - #EditorialModel.__active_classes attibutes + + # @brief Update the EditorialModel.__active_groups and + # EditorialModel.__active_classes attibutes def __set_actives(self): if Settings.editorialmodel.editormode: logger.warning("All EM groups active because editormode in ON") @@ -136,7 +140,7 @@ class EditorialModel(object): self.__active_groups = self.__groups self.__active_classes = self.__classes else: - #determine groups first + # determine groups first self.__active_groups = dict() self.__active_classes = dict() for agrp in Settings.editorialmodel.groups: @@ -153,13 +157,13 @@ class EditorialModel(object): raise RuntimeError("No active class found. Abording") for clsname, acls in self.__active_classes.items(): acls._set_active_fields(self.__active_groups) - - ##@brief EmField getter + + # @brief EmField getter # @param uid str : An EmField uid represented by "CLASSUID.FIELDUID" # @return Fals or an EmField instance # # @todo delete it, useless... - def field(self, uid = None): + def field(self, uid=None): spl = uid.split('.') if len(spl) != 2: raise ValueError("Malformed EmField identifier : '%s'" % uid) @@ -175,7 +179,7 @@ class EditorialModel(object): pass return False - ##@brief Add a class to the editorial model + # @brief Add a class to the editorial model # @param emclass EmClass : the EmClass instance to add # @return emclass def add_class(self, emclass): @@ -187,7 +191,7 @@ class EditorialModel(object): self.__classes[emclass.uid] = emclass return emclass - ##@brief Add a group to the editorial model + # @brief Add a group to the editorial model # @param emgroup EmGroup : the EmGroup instance to add # @return emgroup def add_group(self, emgroup): @@ -199,15 +203,15 @@ class EditorialModel(object): self.__groups[emgroup.uid] = emgroup return emgroup - ##@brief Add a new EmClass to the editorial model + # @brief Add a new EmClass to the editorial model #@param uid str : EmClass uid - #@param **kwargs : EmClass constructor options ( + #@param **kwargs : EmClass constructor options ( # see @ref lodel.editorial_model.component.EmClass.__init__() ) def new_class(self, uid, **kwargs): assert_edit() return self.add_class(EmClass(uid, **kwargs)) - - ##@brief Add a new EmGroup to the editorial model + + # @brief Add a new EmGroup to the editorial model #@param uid str : EmGroup uid #@param *kwargs : EmGroup constructor keywords arguments ( # see @ref lodel.editorial_model.component.EmGroup.__init__() ) @@ -215,7 +219,7 @@ class EditorialModel(object): assert_edit() return self.add_group(EmGroup(uid, **kwargs)) - ##@brief Save a model + # @brief Save a model # @param translator module : The translator module to use # @param **translator_args def save(self, translator, **translator_kwargs): @@ -223,14 +227,15 @@ class EditorialModel(object): if isinstance(translator, str): translator = self.translator_from_name(translator) return translator.save(self, **translator_kwargs) - - ##@brief Raise an error if lodel is not in EM edition mode + + # @brief Raise an error if lodel is not in EM edition mode @staticmethod def raise_if_ro(): if not Settings.editorialmodel.editormode: - raise EditorialModelError("Lodel in not in EM editor mode. The EM is in read only state") + raise EditorialModelError( + "Lodel in not in EM editor mode. The EM is in read only state") - ##@brief Load a model + # @brief Load a model # @param translator module : The translator module to use # @param **translator_args @classmethod @@ -241,7 +246,7 @@ class EditorialModel(object): res.__set_actives() return res - ##@brief Return a translator module given a translator name + # @brief Return a translator module given a translator name # @param translator_name str : The translator name # @return the translator python module # @throw NameError if the translator does not exists @@ -253,12 +258,12 @@ class EditorialModel(object): except ImportError: raise NameError("No translator named %s") return mod - - ##@brief Lodel hash + + # @brief Lodel hash def d_hash(self): payload = "%s%s" % ( - self.name, - 'NODESC' if self.description is None else self.description.d_hash() + self.name, + 'NODESC' if self.description is None else self.description.d_hash() ) for guid in sorted(self.__groups): payload += str(self.__groups[guid].d_hash()) @@ -267,7 +272,6 @@ class EditorialModel(object): payload += str(self.__classes[cuid].d_hash()) return int.from_bytes( - hashlib.md5(bytes(payload, 'utf-8')).digest(), - byteorder='big' + hashlib.md5(bytes(payload, 'utf-8')).digest(), + byteorder='big' ) - diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index cd7e9eb..1bb6afa 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -12,34 +12,51 @@ import warnings from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'DataNoneValid', 'FieldValidationError'], - 'lodel.leapi.datahandlers.exceptions': ['LodelDataHandlerConsistencyException', 'LodelDataHandlerException'], - 'lodel.logger': 'logger'}) + 'lodel.exceptions': [ + 'LodelException', + 'LodelExceptions', + '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 -#@ingroup lodel2_datahandlers -class DataHandler(object): +## @brief Base class for all data handlers +# @ingroup lodel2_datahandlers +class DataHandler(MlNamedObject): base_type = "type" _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 - ##@brief Stores custom datahandlers classes indexed by name + ## @brief Stores custom datahandlers classes indexed by name # @todo do it ! (like plugins, register handlers... blablabla) __custom_handlers = dict() 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 = [] directly_editable = True - ##@brief constructor + + ## @brief constructor + # # @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 - # designed globally and immutable - # @param **args + # @param immutable bool : indicates if the fieldtype has to be defined in child classes of + # LeObject or if it is designed globally and immutable # @throw NotImplementedError if it is instanciated directly def __init__(self, **kwargs): if self.__class__ == DataHandler: @@ -54,9 +71,30 @@ class DataHandler(object): self.default, error = self.check_data_value(kwargs['default']) if error: raise error - del(kwargs['default']) + del kwargs['default'] for argname, argval in kwargs.items(): 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 @classmethod @@ -74,15 +112,15 @@ class DataHandler(object): def is_primary_key(self): return self.primary_key - ##@brief checks if a fieldtype is internal + ## @brief checks if a fieldtype is internal # @return bool def is_internal(self): return self.internal is not False - ##brief check if a value can be nullable - #@param value * - #@throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable - #@return value (if not None) + ## @brief check if a value can be nullable + # @param value * + # @throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable + # @return value (if not None) # @return value def _check_data_value(self, value): if value is None: @@ -91,9 +129,9 @@ class DataHandler(object): raise DataNoneValid("None with a nullable. This exeption is allowed") return value - ##@brief calls the data_field (defined in derived class) _check_data_value() method - #@param value * - #@return tuple (value|None, None|error) value can be cast if NoneError + ## @brief calls the data_field (defined in derived class) _check_data_value() method + # @param value * + # @return tuple (value|None, None|error) value can be cast if NoneError def check_data_value(self, value): try: value = self._check_data_value(value) @@ -103,7 +141,7 @@ class DataHandler(object): return None, expt 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 # @return bool def can_override(self, data_handler): @@ -111,17 +149,17 @@ class DataHandler(object): return False return True - ##@brief Build field value - #@ingroup lodel2_dh_checks - #@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see - #@ref _construct_data() and @ref lodel2_dh_check_impl ) - #@param emcomponent EmComponent : An EmComponent child class instance - #@param fname str : The field name - #@param datas dict : dict storing fields values (from the component) - #@param cur_value : the value from the current field (identified by fieldname) - #@return the value - #@throw RunTimeError if data construction fails - #@todo raise something else + ## @brief Build field value + # @ingroup lodel2_dh_checks + # @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see + # @ref _construct_data() and @ref lodel2_dh_check_impl ) + # @param emcomponent EmComponent : An EmComponent child class instance + # @param fname str : The field name + # @param datas dict : dict storing fields values (from the component) + # @param cur_value : the value from the current field (identified by fieldname) + # @return the value + # @throw RunTimeError if data construction fails + # @todo raise something else def construct_data(self, emcomponent, fname, datas, cur_value): emcomponent_fields = emcomponent.fields() data_handler = None @@ -136,41 +174,41 @@ class DataHandler(object): new_val = None return self._construct_data(emcomponent, fname, datas, new_val) - ##@brief Designed to be reimplemented by child classes - #@param emcomponent EmComponent : An EmComponent child class instance - #@param fname str : The field name - #@param datas dict : dict storing fields values (from the component) - #@param cur_value : the value from the current field (identified by fieldname) - #@return the value - #@see construct_data() lodel2_dh_check_impl + ## @brief Designed to be reimplemented by child classes + # @param emcomponent EmComponent : An EmComponent child class instance + # @param fname str : The field name + # @param datas dict : dict storing fields values (from the component) + # @param cur_value : the value from the current field (identified by fieldname) + # @return the value + # @see construct_data() lodel2_dh_check_impl def _construct_data(self, empcomponent, fname, datas, cur_value): return cur_value - ##@brief Check datas consistency - #@ingroup lodel2_dh_checks - #@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see - #@ref _construct_data() and @ref lodel2_dh_check_impl ) - #@warning the datas argument looks like a dict but is not a dict - #see @ref base_classes.DatasConstructor "DatasConstructor" and - #@ref lodel2_dh_datas_construction "Datas construction section" - #@param emcomponent EmComponent : An EmComponent child class instance - #@param fname : the field name - #@param datas dict : dict storing fields values - #@return an Exception instance if fails else True - #@todo A implémenter + ## @brief Check datas consistency + # @ingroup lodel2_dh_checks + # @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see + # @ref _construct_data() and @ref lodel2_dh_check_impl ) + # @warning the datas argument looks like a dict but is not a dict + # see @ref base_classes.DatasConstructor "DatasConstructor" and + # @ref lodel2_dh_datas_construction "Datas construction section" + # @param emcomponent EmComponent : An EmComponent child class instance + # @param fname : the field name + # @param datas dict : dict storing fields values + # @return an Exception instance if fails else True + # @todo A implémenter def check_data_consistency(self, emcomponent, fname, datas): return self._check_data_consistency(emcomponent, fname, datas) - ##@brief Designed to be reimplemented by child classes - #@param emcomponent EmComponent : An EmComponent child class instance - #@param fname : the field name - #@param datas dict : dict storing fields values - #@return an Exception instance if fails else True - #@see check_data_consistency() lodel2_dh_check_impl + ## @brief Designed to be reimplemented by child classes + # @param emcomponent EmComponent : An EmComponent child class instance + # @param fname : the field name + # @param datas dict : dict storing fields values + # @return an Exception instance if fails else True + # @see check_data_consistency() lodel2_dh_check_impl def _check_data_consistency(self, emcomponent, fname, datas): return True - ##@brief make consistency after a query + ## @brief make consistency after a query # @param emcomponent EmComponent : An EmComponent child class instance # @param fname : the field name # @param datas dict : dict storing fields values @@ -179,7 +217,7 @@ class DataHandler(object): def make_consistency(self, emcomponent, fname, datas): 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 def register_new_handler(cls, name, 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") cls.__custom_handlers[name] = data_handler - ##@brief Load all datahandlers + ## @brief Load all datahandlers @classmethod def load_base_handlers(cls): if cls._base_handlers is None: @@ -201,10 +239,11 @@ class DataHandler(object): cls._base_handlers[name.lower()] = obj 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) # @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 def from_name(cls, name): cls.load_base_handlers() @@ -214,7 +253,24 @@ class DataHandler(object): raise NameError("No data handlers named '%s'" % (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 # @return a str @classmethod @@ -222,62 +278,64 @@ class DataHandler(object): name = name.lower() handler_class = cls.from_name(name) return '{module_name}.{class_name}'.format( - module_name=handler_class.__module__, - class_name=handler_class.__name__ + module_name=handler_class.__module__, + class_name=handler_class.__name__ ) - ##@brief __hash__ implementation for fieldtypes + ## @brief __hash__ implementation for fieldtypes def __hash__(self): hash_dats = [self.__class__.__module__] for kdic in sorted([k for k in self.__dict__.keys() if not k.startswith('_')]): hash_dats.append((kdic, getattr(self, kdic))) return hash(tuple(hash_dats)) -##@brief Base class for datas data handler (by opposition with references) -#@ingroup lodel2_datahandlers +## @brief Base class for datas data handler (by opposition with references) +# @ingroup lodel2_datahandlers class DataField(DataHandler): 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 # editorial object -#@todo Construct data implementation : transform the data into a LeObject -#instance - +# @todo Construct data implementation : transform the data into a LeObject instance class Reference(DataHandler): base_type = "ref" - ##@brief Instanciation + ## @brief Instanciation # @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 internal bool : if False, the field is not internal # @param **kwargs : other arguments 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 = 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 len(back_reference) != 2: 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): - # raise TypeError("Back reference was expected to be a tuple(, str) but got : (%s, %s)" % (back_reference[0], back_reference[1])) + # if not issubclass(lodel.leapi.leobject.LeObject, back_reference[0]) + # or not isinstance(back_reference[1], str): + # raise TypeError("Back reference was expected to be a tuple(, str) + # but got : (%s, %s)" % (back_reference[0], back_reference[1])) self.__back_reference = back_reference super().__init__(internal=internal, **kwargs) - ##@brief Method designed to return an empty value for this kind of - #multipleref + ## @brief Method designed to return an empty value for this kind of + # multipleref @classmethod def empty(cls): 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 def back_reference(self): return copy.copy(self.__back_reference) - ##@brief Property that takes value of datahandler of the backreference or - #None + ## @brief Property that takes value of datahandler of the backreference or + # None @property def back_ref_datahandler(self): if self.__back_reference is None: @@ -288,15 +346,15 @@ class Reference(DataHandler): def linked_classes(self): 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): self.__back_reference = back_reference - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is an appropriate type - #@return value - #@todo implement the check when we have LeObject uid check value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is an appropriate type + # @return value + # @todo implement the check when we have LeObject uid check value def _check_data_value(self, value): from lodel.leapi.leobject import LeObject 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) return value - ##@brief Check datas consistency - #@param emcomponent EmComponent : An EmComponent child class instance - #@param fname : the field name - #@param datas dict : dict storing fields values - #@return an Exception instance if fails else True - #@todo check for performance issue and check logics - #@warning composed uid capabilities broken here + ## @brief Check datas consistency + # @param emcomponent EmComponent : An EmComponent child class instance + # @param fname : the field name + # @param datas dict : dict storing fields values + # @return an Exception instance if fails else True + # @todo check for performance issue and check logics + # @warning composed uid capabilities broken here def check_data_consistency(self, emcomponent, fname, datas): rep = super().check_data_consistency(emcomponent, fname, datas) if isinstance(rep, Exception): @@ -333,21 +391,21 @@ class Reference(DataHandler): if not target_class.is_exist(value): logger.warning('Object referenced does not exist') return False - #target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here - #obj = target_class.get([(target_uidfield, '=', value)]) - #if len(obj) == 0: + # target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here + # obj = target_class.get([(target_uidfield, '=', value)]) + # if len(obj) == 0: # logger.warning('Object referenced does not exist') # return False return True - - ##@brief Utility method designed to fetch referenced objects - #@param value mixed : the field value - #@throw NotImplementedError + + ## @brief Utility method designed to fetch referenced objects + # @param value mixed : the field value + # @throw NotImplementedError def get_referenced(self, value): 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 class SingleRef(Reference): @@ -356,18 +414,18 @@ class SingleRef(Reference): super().__init__(allowed_classes=allowed_classes, **kwargs) - ##@brief Check and cast value in appropriate type - #@param value: * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value: * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) return value - ##@brief Utility method designed to fetch referenced objects - #@param value mixed : the field value - #@return A LeObject child class instance - #@throw LodelDataHandlerConsistencyException if no referenced object found + ## @brief Utility method designed to fetch referenced objects + # @param value mixed : the field value + # @return A LeObject child class instance + # @throw LodelDataHandlerConsistencyException if no referenced object found def get_referenced(self, value): for leo_cls in self.linked_classes: res = leo_cls.get_from_uid(value) @@ -377,30 +435,30 @@ class SingleRef(Reference): referenced object with uid %s" % value) -##@brief This class represent a data_handler for multiple references to another object -#@ingroup lodel2_datahandlers +## @brief This class represent a data_handler for multiple references to another object +# @ingroup lodel2_datahandlers # # The fields using this data handlers are like SingleRef but can store multiple references in one field # @note for the moment split on ',' chars class MultipleRef(Reference): - ## + ## @brief Constructor # @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): self.max_item = max_item super().__init__(**kwargs) - ##@brief Method designed to return an empty value for this kind of - #multipleref + ## @brief Method designed to return an empty value for this kind of + # multipleref @classmethod def empty(cls): return [] - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value - #@TODO Writing test error for errors when stored multiple references in one field + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value + # @TODO Writing test error for errors when stored multiple references in one field def _check_data_value(self, value): value = DataHandler._check_data_value(self, value) if not hasattr(value, '__iter__'): @@ -408,7 +466,7 @@ class MultipleRef(Reference): if self.max_item is not None: if self.max_item < len(value): raise FieldValidationError("Too many items") - new_val = list() + new_val = list() error_list = list() for i, v in enumerate(value): try: @@ -420,11 +478,11 @@ class MultipleRef(Reference): raise FieldValidationError("MultipleRef have for invalid values [%s] :" % (",".join(error_list))) return new_val - ##@brief Utility method designed to fetch referenced objects - #@param values mixed : the field values - #@return A list of LeObject child class instance - #@throw LodelDataHandlerConsistencyException if some referenced objects - #were not found + ## @brief Utility method designed to fetch referenced objects + # @param values mixed : the field values + # @return A list of LeObject child class instance + # @throw LodelDataHandlerConsistencyException if some referenced objects + # were not found def get_referenced(self, values): if values is None or len(values) == 0: return list() @@ -432,18 +490,19 @@ class MultipleRef(Reference): values = set(values) res = list() 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( [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 if len(left) == 0: return res raise LodelDataHandlerConsistencyException("Unable to find \ some referenced objects. Following uids were not found : %s" % ','.join(left)) + ## @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. # @@ -457,15 +516,15 @@ class DatasConstructor(object): # @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 def __init__(self, leobject, datas, fields_handler): - ## Stores concerned class + # Stores concerned class self._leobject = leobject - ## Stores datas and constructed datas + # Stores datas and constructed datas self._datas = copy.copy(datas) - ## Stores fieldtypes + # Stores fieldtypes self._fields_handler = fields_handler - ## Stores list of fieldname for constructed datas + # Stores list of fieldname for constructed datas self._constructed = [] - ## Stores construct calls list + # Stores construct calls list self._construct_calls = [] ## @brief Implements the dict.keys() method on instance @@ -488,3 +547,30 @@ class DatasConstructor(object): self._datas[fname] = value 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() diff --git a/lodel/leapi/datahandlers/datas.py b/lodel/leapi/datahandlers/datas.py index 9f4eb0e..a9078a4 100644 --- a/lodel/leapi/datahandlers/datas.py +++ b/lodel/leapi/datahandlers/datas.py @@ -7,9 +7,9 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar', - 'DateTime', 'Text', 'File'], + 'DateTime', 'Text', 'File'], 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']}) + 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']}) ##@brief Data field designed to handle formated strings @@ -26,7 +26,7 @@ build its content' def __init__(self, format_string, field_list, **kwargs): self._field_list = field_list self._format_string = format_string - super().__init__(internal='automatic',**kwargs) + super().__init__(internal='automatic', **kwargs) def _construct_data(self, emcomponent, fname, datas, cur_value): ret = self._format_string % tuple( @@ -49,7 +49,7 @@ max_length and regex' # @param **kwargs def __init__(self, regex='', max_length=10, **kwargs): 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) ##@brief Check and cast value in appropriate type @@ -106,7 +106,8 @@ be internal") if not inspect.isclass(emcomponent): cls = emcomponent.__class__ return cls.__name__ - + + ##@brief Data field designed to handle concatenated fields class Concat(FormatString): help = 'Automatic strings concatenation' @@ -116,11 +117,11 @@ class Concat(FormatString): # @param field_list list : List of field to use # @param separator str # @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]) - super().__init__( - format_string = format_string, field_list = field_list, **kwargs) - + super().__init__(format_string=format_string, + field_list=field_list, + **kwargs) class Password(Varchar): @@ -129,7 +130,6 @@ class Password(Varchar): pass - class VarcharList(Varchar): help = 'DataHandler designed to make a list out of a string.' base_type = 'varchar' @@ -140,7 +140,6 @@ class VarcharList(Varchar): self.delimiter = str(delimiter) super().__init__(**kwargs) - def construct_data(self, emcomponent, fname, datas, cur_value): result = cur_value.split(self.delimiter) return result diff --git a/lodel/leapi/datahandlers/datas_base.py b/lodel/leapi/datahandlers/datas_base.py index c78b1fe..03a209d 100644 --- a/lodel/leapi/datahandlers/datas_base.py +++ b/lodel/leapi/datahandlers/datas_base.py @@ -8,32 +8,33 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.leapi.datahandlers.base_classes': ['DataField'], '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): help = 'A basic boolean field' base_type = 'bool' - ##@brief A boolean field + ## @brief A boolean field def __init__(self, **kwargs): #if 'check_data_value' not in kwargs: # kwargs['check_data_value'] = self._check_data_value super().__init__(ftype='bool', **kwargs) - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) if not isinstance(value, bool): raise FieldValidationError("The value '%s' is not, and will never, be a boolean" % value) return value -##@brief Data field designed to handle integer values + +## @brief Data field designed to handle integer values class Integer(DataField): help = 'Basic integer field' @@ -41,14 +42,14 @@ class Integer(DataField): cast_type = int 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 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 # @return value - def _check_data_value(self, value, strict = False): + def _check_data_value(self, value, strict=False): value = super()._check_data_value(value) if (strict and not isinstance(value, int)): 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) return value -##@brief Data field designed to handle string + +## @brief Data field designed to handle string class Varchar(DataField): help = 'Basic string (varchar) field. Default size is 64 characters' base_type = 'char' - ##@brief A string field + ## @brief A string field # @brief max_length int: The maximum length of this field def __init__(self, max_length=64, **kwargs): self.max_length = int(max_length) 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 # @return bool def can_override(self, data_handler): @@ -83,25 +85,26 @@ class Varchar(DataField): return False return True - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) if not isinstance(value, str): raise FieldValidationError("The value '%s' can't be a str" % value) 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 -##@brief Data field designed to handle date & time + +## @brief Data field designed to handle date & time class DateTime(DataField): help = 'A datetime field. Take two boolean options now_on_update and now_on_create' 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_create bool : If true, the date is set to NEW on creation # @param **kwargs @@ -111,13 +114,13 @@ class DateTime(DataField): self.datetime_format = '%Y-%m-%d' if 'format' not in kwargs else kwargs['format'] super().__init__(**kwargs) - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) - if isinstance(value,str): + if isinstance(value, str): try: value = datetime.datetime.fromtimestamp(time.mktime(time.strptime(value, self.datetime_format))) except ValueError: @@ -131,7 +134,8 @@ class DateTime(DataField): return datetime.datetime.now() return cur_value -##@brief Data field designed to handle long string + +## @brief Data field designed to handle long string class Text(DataField): help = 'A text field (big string)' base_type = 'text' @@ -139,22 +143,23 @@ class Text(DataField): def __init__(self, **kwargs): super(self.__class__, self).__init__(ftype='text', **kwargs) - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) if not isinstance(value, str): raise FieldValidationError("The content passed to this Text field is not a convertible to a string") return value -##@brief Data field designed to handle Files + +## @brief Data field designed to handle Files class File(DataField): base_type = 'file' - ##@brief a file field + ## @brief a file field # @param upload_path str : None by default # @param **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) def _check_data_value(self, value): return super()._check_data_value(value) - diff --git a/lodel/leapi/datahandlers/exceptions.py b/lodel/leapi/datahandlers/exceptions.py index 35cd59e..0cdd266 100644 --- a/lodel/leapi/datahandlers/exceptions.py +++ b/lodel/leapi/datahandlers/exceptions.py @@ -1,5 +1,6 @@ -def LodelDataHandlerException(Exception): +class LodelDataHandlerException(Exception): pass -def LodelDataHandlerConsistencyException(LodelDataHandlerException): + +class LodelDataHandlerConsistencyException(LodelDataHandlerException): pass diff --git a/lodel/leapi/datahandlers/references.py b/lodel/leapi/datahandlers/references.py index d7a3055..85fd332 100644 --- a/lodel/leapi/datahandlers/references.py +++ b/lodel/leapi/datahandlers/references.py @@ -3,35 +3,37 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.leapi.datahandlers.base_classes': ['Reference', 'MultipleRef', - 'SingleRef'], + 'SingleRef'], 'lodel.logger': 'logger', 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']}) + 'LodelFatalError', 'DataNoneValid', + 'FieldValidationError']}) + class Link(SingleRef): 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): - ##@brief instanciates a list reference + ## @brief instanciates a list reference # @param max_length int # @param kwargs # - allowed_classes list | None : list of allowed em classes if None no restriction # - internal bool - def __init__(self, max_length = None, **kwargs): + def __init__(self, max_length=None, **kwargs): super().__init__(**kwargs) @classmethod def empty(cls): return list() - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) try: @@ -39,12 +41,12 @@ class List(MultipleRef): except Exception as e: raise FieldValidationError("Given iterable is not castable in \ 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): - ##@brief instanciates a set reference + ## @brief instanciates a set reference # @param kwargs : named arguments # - allowed_classes list | None : list of allowed em classes if None no restriction # - internal bool : if False, the field is not internal @@ -55,10 +57,10 @@ class Set(MultipleRef): def empty(cls): return set() - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) try: @@ -66,11 +68,12 @@ class Set(MultipleRef): except Exception as e: raise FieldValidationError("Given iterable is not castable in \ 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): - ##@brief instanciates a dict reference + ## @brief instanciates a dict reference # @param kwargs : named arguments # - allowed_classes list | None : list of allowed em classes if None no restriction # - internal bool : if False, the field is not internal @@ -81,38 +84,40 @@ class Map(MultipleRef): def empty(cls): return dict() - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) if not isinstance(value, dict): raise FieldValidationError("Values for dict fields should be dict") 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): 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 max_depth int | None : limit of depth # @param max_childs int | Nine : maximum number of childs by nodes - def __init__(self, back_reference, max_depth = None, max_childs = None, **kwargs): - super().__init__( back_reference = back_reference, - max_depth = max_depth, - max_childs = max_childs, - **kwargs) + def __init__(self, back_reference, max_depth=None, max_childs=None, **kwargs): + super().__init__(back_reference=back_reference, + max_depth=max_depth, + max_childs=max_childs, + **kwargs) @classmethod def empty(cls): return tuple() - ##@brief Check and cast value in appropriate type - #@param value * - #@throw FieldValidationError if value is unappropriate or can not be cast - #@return value + ## @brief Check and cast value in appropriate type + # @param value * + # @throw FieldValidationError if value is unappropriate or can not be cast + # @return value def _check_data_value(self, value): value = super()._check_data_value(value) if not (isinstance(value, list) or isinstance(value, str)): diff --git a/lodel/leapi/leobject.py b/lodel/leapi/leobject.py index e609866..147896c 100644 --- a/lodel/leapi/leobject.py +++ b/lodel/leapi/leobject.py @@ -11,77 +11,79 @@ LodelContext.expose_modules(globals(), { 'lodel.settings': 'Settings', 'lodel.settings.utils': 'SettingsError', 'lodel.leapi.query': ['LeInsertQuery', 'LeUpdateQuery', 'LeDeleteQuery', - 'LeGetQuery'], + 'LeGetQuery'], 'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors', - 'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError', - 'LeApiQueryErrors'], + 'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError', + 'LeApiQueryErrors'], 'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError', - 'LodelScriptError', 'DatasourcePluginError'], + 'LodelScriptError', 'DatasourcePluginError'], 'lodel.exceptions': ['LodelFatalError'], 'lodel.plugin.hooks': ['LodelHook'], 'lodel.plugin': ['Plugin', 'DatasourcePlugin'], 'lodel.leapi.datahandlers.base_classes': ['DatasConstructor', 'Reference']}) -##@brief Stores the name of the field present in each LeObject that indicates -#the name of LeObject subclass represented by this object +# @brief Stores the name of the field present in each LeObject that indicates +# the name of LeObject subclass represented by this object 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 # @note Wrapped methods are : LeObject.data() & LeObject.set_data() + + class LeObjectValues(object): - - ##@brief Construct a new LeObjectValues + + # @brief Construct a new LeObjectValues # @param fieldnames_callback method # @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 def __init__(self, fieldnames_callback, set_callback, get_callback): self._setter = set_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 # @param fname str : Field name def __getattribute__(self, fname): getter = super().__getattribute__('_getter') 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 # @param fname str : Field name # @param fval * : the field value def __setattribute__(self, fname, fval): setter = super().__getattribute__('_setter') return setter(fname, fval) - + 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 - ##@brief A dict that stores DataHandler instances indexed by field name + # @brief A dict that stores DataHandler instances indexed by field name _fields = None - ##@brief A tuple of fieldname (or a uniq fieldname) representing uid - _uid = None - ##@brief Read only datasource ( see @ref lodel2_datasources ) + # @brief A tuple of fieldname (or a uniq fieldname) representing uid + _uid = None + # @brief Read only datasource ( see @ref lodel2_datasources ) _ro_datasource = None - ##@brief Read & write datasource ( see @ref lodel2_datasources ) + # @brief Read & write datasource ( see @ref lodel2_datasources ) _rw_datasource = None - ##@brief Store the list of child classes + # @brief Store the list of child classes _child_classes = None - ##@brief Name of the datasource plugin + # @brief Name of the datasource plugin _datasource_name = None def __new__(cls, **kwargs): - + self = object.__new__(cls) - ##@brief A dict that stores fieldvalues indexed by fieldname - self.__datas = { fname:None for fname in self._fields } - ##@brief Store a list of initianilized fields when instanciation not complete else store True + # @brief A dict that stores fieldvalues indexed by fieldname + self.__datas = {fname: None for fname in self._fields} + # @brief Store a list of initianilized fields when instanciation not complete else store True 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) for fieldname, fieldval in kwargs.items(): self.__datas[fieldname] = fieldval @@ -90,11 +92,12 @@ class LeObject(object): self.__set_initialized() 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 def __init__(self, **kwargs): 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 for uid_name in self._uid: @@ -105,7 +108,7 @@ class LeObject(object): self.__initialized.append(uid_name) # Processing given fields - allowed_fieldnames = self.fieldnames(include_ro = False) + allowed_fieldnames = self.fieldnames(include_ro=False) err_list = dict() for fieldname, fieldval in kwargs.items(): if fieldname not in allowed_fieldnames: @@ -119,39 +122,39 @@ class LeObject(object): self.__datas[fieldname] = fieldval self.__initialized.append(fieldname) if len(err_list) > 0: - raise LeApiErrors(msg = "Unable to __init__ %s" % self.__class__, - exceptions = err_list) + raise LeApiErrors(msg="Unable to __init__ %s" % self.__class__, + exceptions=err_list) self.__set_initialized() - + #-----------------------------------# # 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 def initialized(self): return self.__is_initialized - - ##@return The uid field name + + # @return The uid field name @classmethod def uid_fieldname(cls): 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 # @return a list of str @classmethod - def fieldnames(cls, include_ro = False): + def fieldnames(cls, include_ro=False): 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: return list(cls._fields.keys()) - + @classmethod def name2objname(cls, name): 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 # @return A data handler instance #@todo update class of exception raised @@ -160,18 +163,18 @@ class LeObject(object): if not fieldname in cls._fields: raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__)) 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 #@return {'fieldname': datahandler, ...} @classmethod - def reference_handlers(cls, with_backref = True): - return { fname: fdh - for fname, fdh in cls.fields(True).items() - if fdh.is_reference() and \ - (not with_backref or fdh.back_reference is not None)} - - ##@brief Return a LeObject child class from a name + def reference_handlers(cls, with_backref=True): + return {fname: fdh + for fname, fdh in cls.fields(True).items() + if fdh.is_reference() and + (not with_backref or fdh.back_reference is not None)} + + # @brief Return a LeObject child class from a name # @warning This method has to be called from dynamically generated LeObjects # @param leobject_name str : LeObject name # @return A LeObject child class @@ -183,14 +186,14 @@ class LeObject(object): mod = importlib.import_module(cls.__module__) try: return getattr(mod, leobject_name) - except (AttributeError, TypeError) : + except (AttributeError, TypeError): raise LeApiError("No LeObject named '%s'" % leobject_name) - + @classmethod def is_abstract(cls): return cls._abstract - - ##@brief Field data handler getter + + # @brief Field data handler getter #@param fieldname str : The field name #@return A datahandler instance #@throw NameError if the field doesn't exist @@ -199,20 +202,22 @@ class LeObject(object): try: return cls._fields[fieldname] except KeyError: - raise NameError("No field named '%s' in %s" % ( fieldname, - cls.__name__)) - ##@return A dict with fieldname as key and datahandler as instance + raise NameError("No field named '%s' in %s" % (fieldname, + cls.__name__)) + # @return A dict with fieldname as key and datahandler as instance + @classmethod - def fields(cls, include_ro = False): + def fields(cls, include_ro=False): if include_ro: return copy.copy(cls._fields) else: - return {fname:cls._fields[fname] for fname in cls._fields if not cls._fields[fname].is_internal()} - - ##@brief Return the list of parents classes + return {fname: cls._fields[fname] for fname in cls._fields\ + if not cls._fields[fname].is_internal()} + + # @brief Return the list of parents classes # #@note the first item of the list is the current class, the second is it's - #parent etc... + # parent etc... #@param cls #@warning multiple inheritance broken by this method #@return a list of LeObject child classes @@ -222,23 +227,22 @@ class LeObject(object): res = [cls] cur = cls while True: - cur = cur.__bases__[0] # Multiple inheritance broken HERE + cur = cur.__bases__[0] # Multiple inheritance broken HERE if cur in (LeObject, object): break else: res.append(cur) return res - - ##@brief Return a tuple a child classes + + # @brief Return a tuple a child classes #@return a tuple of child classes @classmethod def child_classes(cls): 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 @classmethod def uid_source(cls): @@ -246,19 +250,19 @@ class LeObject(object): return False hierarch = cls.hierarch() 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:]: puid_handlers = set(cls._fields[name] for name in pcls._uid) if set(pcls._uid) != set(prev._uid) \ - or puid_handlers != uid_handlers: + or puid_handlers != uid_handlers: break prev = pcls 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 - #by a datasource instance to avoid doing this operation for each query + # 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 #@see LeObject::_init_datasource() @classmethod def _init_datasources(cls): @@ -266,7 +270,7 @@ class LeObject(object): rw_ds = ro_ds = cls._datasource_name else: ro_ds, rw_ds = cls._datasource_name - #Read only datasource initialisation + # Read only datasource initialisation cls._ro_datasource = DatasourcePlugin.init_datasource(ro_ds, True) if cls._ro_datasource is None: 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 %= (ro_ds, cls.__name__) logger.debug(log_msg) - #Read write datasource initialisation + # Read write datasource initialisation cls._rw_datasource = DatasourcePlugin.init_datasource(rw_ds, False) if cls._ro_datasource is None: 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 %= (ro_ds, cls.__name__) 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 #@warning Broke multiple uid capabilities def uid(self): 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 # @param field_name str : field name # @return the Value @@ -303,16 +307,16 @@ class LeObject(object): if field_name not in self._fields.keys(): raise NameError("No such field in %s : %s" % (self.__class__.__name__, field_name)) 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] - - ##@brief Read only access to all datas + + # @brief Read only access to all datas #@return a dict representing datas of current instance - def datas(self, internal = False): - return {fname:self.data(fname) for fname in self.fieldnames(internal)} - - - ##@brief Datas setter + def datas(self, internal=False): + return {fname: self.data(fname) for fname in self.fieldnames(internal)} + + # @brief Datas setter # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance # @param fname str : field name # @param fval * : field value @@ -320,7 +324,7 @@ class LeObject(object): # @throw NameError if fname is not valid # @throw AttributeError if the field is not writtable 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(): raise NameError("No such field in %s : %s" % (self.__class__.__name__, fname)) else: @@ -342,23 +346,23 @@ class LeObject(object): # We skip full validation here because the LeObject is not fully initialized yet val, err = self._fields[fname].check_data_value(fval) if isinstance(err, Exception): - #Revert change to be in valid state + # Revert change to be in valid state del(self.__datas[fname]) del(self.__initialized[-1]) - raise LeApiErrors("Data check error", {fname:err}) + raise LeApiErrors("Data check error", {fname: err}) else: 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 def __set_initialized(self): 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): 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) # @return None if checks succeded else return an exception list @@ -366,7 +370,7 @@ class LeObject(object): err_list = dict() if self.__initialized is True: # 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]) if err is not None: err_list[fname] = err @@ -374,19 +378,19 @@ class LeObject(object): self.__datas[fname] = val # Data construction if len(err_list) == 0: - for fname in self.fieldnames(include_ro = True): + for fname in self.fieldnames(include_ro=True): try: field = self._fields[fname] - self.__datas[fname] = field.construct_data( self, - fname, - self.__datas, - self.__datas[fname] - ) + self.__datas[fname] = field.construct_data(self, + fname, + self.__datas, + self.__datas[fname] + ) except Exception as exp: err_list[fname] = exp # Datas consistency check 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] ret = field.check_data_consistency(self, fname, self.__datas) if isinstance(ret, Exception): @@ -404,8 +408,8 @@ class LeObject(object): #--------------------# # 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 # at the end of the dyncode parse @@ -415,8 +419,8 @@ class LeObject(object): @classmethod def _set__fields(cls, 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 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 @@ -424,10 +428,10 @@ class LeObject(object): # @return Checked datas # @throw LeApiDataCheckError if errors reported during check @classmethod - def check_datas_value(cls, datas, complete = False, allow_internal = True): - err_l = dict() #Error storing - correct = set() #valid fields name - mandatory = set() #mandatory fields name + def check_datas_value(cls, datas, complete=False, allow_internal=True): + err_l = dict() # Error storing + correct = set() # valid fields name + mandatory = set() # mandatory fields name for fname, datahandler in cls._fields.items(): if allow_internal or not datahandler.is_internal(): correct.add(fname) @@ -436,15 +440,15 @@ class LeObject(object): provided = set(datas.keys()) # searching for unknow fields 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 err_l[u_f] = AttributeError("Unknown or unauthorized field '%s'" % u_f) # searching for missing mandatory fieldsa for missing in mandatory - provided: err_l[missing] = AttributeError("The data for field '%s' is missing" % missing) - #Checks datas + # Checks datas 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] res = dh.check_data_value(value) checked_datas[name], err = res @@ -455,10 +459,10 @@ class LeObject(object): raise LeApiDataCheckErrors("Error while checking datas", err_l) 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() - # + # # @param datas dict : {fieldname : fieldvalue, ...} # @param complete bool : If True you MUST give all the 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) return ret_datas - ## @brief Construct datas values + # @brief Construct datas values # # @param cls # @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): constructor = DatasConstructor(cls, datas, cls._fields) ret = { - fname:constructor[fname] - for fname, ftype in cls._fields.items() - if not ftype.is_internal() or ftype.internal != 'autosql' + fname: constructor[fname] + for fname, ftype in cls._fields.items() + if not ftype.is_internal() or ftype.internal != 'autosql' } return ret - - ## @brief Check datas consistency + + # @brief Check datas consistency #  # @warning assert that datas is complete # @param cls @@ -511,29 +515,29 @@ construction and consitency when datas are not complete\n") if len(err_l) > 0: raise LeApiDataCheckError("Datas consistency checks fails", err_l) - - ## @brief Check datas consistency + + # @brief Check datas consistency #  # @warning assert that datas is complete # @param cls # @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 @classmethod - def make_consistency(cls, datas, type_query = 'insert'): + def make_consistency(cls, datas, type_query='insert'): for fname, dh in cls._fields.items(): 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 @classmethod def insert(cls, datas): query = LeInsertQuery(cls) return query.execute(datas) - ## @brief Update an instance of LeObject + # @brief Update an instance of LeObject # - #@param datas : list of new datas - def update(self, datas = None): + #@param datas : list of new datas + def update(self, datas=None): datas = self.datas(internal=False) if datas is None else datas uids = self._uid query_filter = list() @@ -543,15 +547,15 @@ construction and consitency when datas are not complete\n") query = LeUpdateQuery(self.__class__, query_filter) except Exception as err: raise err - + try: result = query.execute(datas) except Exception as err: raise err return result - - ## @brief Delete an instance of LeObject + + # @brief Delete an instance of LeObject # #@return 1 if the objet has been deleted def delete(self): @@ -565,8 +569,8 @@ construction and consitency when datas are not complete\n") result = query.execute() return result - - ## @brief Delete instances of LeObject + + # @brief Delete instances of LeObject #@param query_filters list #@returns the number of deleted items @classmethod @@ -576,7 +580,7 @@ construction and consitency when datas are not complete\n") query = LeDeleteQuery(cls, query_filters) except Exception as err: raise err - + try: result = query.execute() except Exception as err: @@ -584,11 +588,11 @@ construction and consitency when datas are not complete\n") if not result is None: deleted += result 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 field_list list|None : list of string representing fields see + #@param field_list list|None : list of string representing fields see #@ref leobject_filters #@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]) @@ -598,49 +602,49 @@ construction and consitency when datas are not complete\n") @classmethod def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0): if field_list is not None: - for uid in [ uidname - for uidname in cls.uid_fieldname() - if uidname not in field_list ]: + for uid in [uidname + for uidname in cls.uid_fieldname() + if uidname not in field_list]: field_list.append(uid) if CLASS_ID_FIELDNAME not in field_list: field_list.append(CLASS_ID_FIELDNAME) try: query = LeGetQuery( - cls, query_filters = query_filters, field_list = field_list, - order = order, group = group, limit = limit, offset = offset) + cls, query_filters=query_filters, field_list=field_list, + order=order, group=group, limit=limit, offset=offset) except ValueError as err: raise err - + try: result = query.execute() except Exception as err: raise err - + objects = list() for res in result: 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) - + return objects - - ##@brief Retrieve an object given an UID + + # @brief Retrieve an object given an UID #@todo broken multiple UID @classmethod def get_from_uid(cls, uid): if cls.uid_fieldname() is None: raise LodelFatalError( "No uid defined for class %s" % cls.__name__) - uidname = cls.uid_fieldname()[0] #Brokes composed UID - res = cls.get([(uidname,'=', uid)]) - - #dedoublonnage vu que query ou la datasource est bugué + uidname = cls.uid_fieldname()[0] # Brokes composed UID + res = cls.get([(uidname, '=', uid)]) + + # dedoublonnage vu que query ou la datasource est bugué if len(res) > 1: res_cp = res res = [] while len(res_cp) > 0: 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 !!!") else: res.append(cur_res) @@ -651,7 +655,7 @@ object ! For class %s with uid value = %s" % (cls, uid)) return None return res[0] - ##@brief Checks if an object exists + # @brief Checks if an object exists @classmethod def is_exist(cls, uid): 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__) from .query import is_exist return is_exist(cls, uid) - diff --git a/lodel/mlnamedobject/Makefile.am b/lodel/mlnamedobject/Makefile.am new file mode 100644 index 0000000..8bc4c80 --- /dev/null +++ b/lodel/mlnamedobject/Makefile.am @@ -0,0 +1,2 @@ +mlnamedobject_PYTHON= *.py +mlnamedobjectdir=$(pkgpythondir)/mlnamedobject diff --git a/lodel/mlnamedobject/__init__.py b/lodel/mlnamedobject/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lodel/mlnamedobject/mlnamedobject.py b/lodel/mlnamedobject/mlnamedobject.py new file mode 100644 index 0000000..8cb2272 --- /dev/null +++ b/lodel/mlnamedobject/mlnamedobject.py @@ -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) diff --git a/lodel/plugin/datasource_plugin.py b/lodel/plugin/datasource_plugin.py index cf520c8..024224a 100644 --- a/lodel/plugin/datasource_plugin.py +++ b/lodel/plugin/datasource_plugin.py @@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'], - 'lodel.settings.validator': ['SettingValidator'], + 'lodel.validator.validator': ['Validator'], 'lodel.exceptions': ['LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']}) @@ -97,7 +97,7 @@ class DatasourcePlugin(Plugin): 'section': 'lodel2', 'key': 'datasource_connectors', 'default': 'dummy_datasource', - 'validator': SettingValidator( + 'validator': Validator( 'custom_list', none_is_valid = False, validator_name = 'plugin', validator_kwargs = { 'ptype': _glob_typename, @@ -280,13 +280,13 @@ but %s is a %s" % (ds_name, pinstance.__class__.__name__)) #CONFSPEC = { # 'lodel2.datasource.mysql.*' : { # 'host': ( 'localhost', -# SettingValidator('host')), +# Validator('host')), # 'db_name': ( 'lodel', -# SettingValidator('string')), +# Validator('string')), # 'username': ( None, -# SettingValidator('string')), +# Validator('string')), # 'password': ( None, -# SettingValidator('string')), +# Validator('string')), # } #} # diff --git a/lodel/plugin/extensions.py b/lodel/plugin/extensions.py index 9672c90..4c9c098 100644 --- a/lodel/plugin/extensions.py +++ b/lodel/plugin/extensions.py @@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'], - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) _glob_typename = 'extension' @@ -14,7 +14,7 @@ class Extension(Plugin): 'section': 'lodel2', 'key': 'extensions', 'default': None, - 'validator': SettingValidator( + 'validator': Validator( 'custom_list', none_is_valid = True, validator_name = 'plugin', validator_kwargs = { 'ptype': _glob_typename, diff --git a/lodel/plugin/interface.py b/lodel/plugin/interface.py index 04d1a1b..feb0aac 100644 --- a/lodel/plugin/interface.py +++ b/lodel/plugin/interface.py @@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'], - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) _glob_typename = 'ui' @@ -19,7 +19,7 @@ class InterfacePlugin(Plugin): 'section': 'lodel2', 'key': 'interface', 'default': None, - 'validator': SettingValidator( + 'validator': Validator( 'plugin', none_is_valid = True, ptype = _glob_typename)} _type_conf_name = _glob_typename diff --git a/lodel/plugin/sessionhandler.py b/lodel/plugin/sessionhandler.py index 5333fd2..4ad19df 100644 --- a/lodel/plugin/sessionhandler.py +++ b/lodel/plugin/sessionhandler.py @@ -3,7 +3,7 @@ LodelContext.expose_modules(globals(), { 'lodel.plugin.plugins': ['Plugin', 'MetaPlugType'], 'lodel.plugin.exceptions': ['PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'], - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) ##@brief SessionHandlerPlugin metaclass designed to implements a wrapper @@ -53,7 +53,7 @@ class SessionHandlerPlugin(Plugin, metaclass=SessionPluginWrapper): 'section': 'lodel2', 'key': 'session_handler', 'default': None, - 'validator': SettingValidator( + 'validator': Validator( 'plugin', none_is_valid=False,ptype = _glob_typename)} _type_conf_name = _glob_typename diff --git a/lodel/plugins/dummy/__init__.py b/lodel/plugins/dummy/__init__.py index c0b6786..fe04298 100644 --- a/lodel/plugins/dummy/__init__.py +++ b/lodel/plugins/dummy/__init__.py @@ -1,6 +1,6 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) __plugin_name__ = "dummy" __version__ = '0.0.1' #or __version__ = [0,0,1] diff --git a/lodel/plugins/dummy/confspec.py b/lodel/plugins/dummy/confspec.py index 9d9dba2..e9e7e35 100644 --- a/lodel/plugins/dummy/confspec.py +++ b/lodel/plugins/dummy/confspec.py @@ -2,11 +2,11 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) CONFSPEC = { 'lodel2.section1': { 'key1': ( None, - SettingValidator('dummy')) + Validator('dummy')) } } diff --git a/lodel/plugins/dummy_datasource/__init__.py b/lodel/plugins/dummy_datasource/__init__.py index 4934325..7b0ca83 100644 --- a/lodel/plugins/dummy_datasource/__init__.py +++ b/lodel/plugins/dummy_datasource/__init__.py @@ -1,6 +1,6 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) from .datasource import DummyDatasource as Datasource __plugin_type__ = 'datasource' @@ -12,7 +12,7 @@ __plugin_deps__ = [] CONFSPEC = { 'lodel2.datasource.dummy_datasource.*' : { 'dummy': ( None, - SettingValidator('dummy'))} + Validator('dummy'))} } diff --git a/lodel/plugins/filesystem_session/__init__.py b/lodel/plugins/filesystem_session/__init__.py index bbb2ffb..ae430e7 100644 --- a/lodel/plugins/filesystem_session/__init__.py +++ b/lodel/plugins/filesystem_session/__init__.py @@ -1,6 +1,6 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) __plugin_name__ = 'filesystem_session' __version__ = [0,0,1] diff --git a/lodel/plugins/filesystem_session/confspec.py b/lodel/plugins/filesystem_session/confspec.py index c8da6a4..6ae7059 100644 --- a/lodel/plugins/filesystem_session/confspec.py +++ b/lodel/plugins/filesystem_session/confspec.py @@ -2,12 +2,12 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) CONFSPEC = { 'lodel2.sessions':{ - 'directory': ('/tmp/', SettingValidator('path')), - 'expiration': (900, SettingValidator('int')), - 'file_template': ('lodel2_%s.sess', SettingValidator('dummy')) + 'directory': ('/tmp/', Validator('path')), + 'expiration': (900, Validator('int')), + 'file_template': ('lodel2_%s.sess', Validator('dummy')) } } diff --git a/lodel/plugins/mongodb_datasource/confspec.py b/lodel/plugins/mongodb_datasource/confspec.py index feabcc4..945c8c9 100644 --- a/lodel/plugins/mongodb_datasource/confspec.py +++ b/lodel/plugins/mongodb_datasource/confspec.py @@ -2,7 +2,7 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) ##@brief Mongodb datasource plugin confspec #@ingroup plugin_mongodb_datasource @@ -10,11 +10,11 @@ LodelContext.expose_modules(globals(), { #Describe mongodb plugin configuration. Keys are : CONFSPEC = { 'lodel2.datasource.mongodb_datasource.*':{ - 'read_only': (False, SettingValidator('bool')), - 'host': ('localhost', SettingValidator('host')), - 'port': (None, SettingValidator('string', none_is_valid = True)), - 'db_name':('lodel', SettingValidator('string')), - 'username': (None, SettingValidator('string')), - 'password': (None, SettingValidator('string')) + 'read_only': (False, Validator('bool')), + 'host': ('localhost', Validator('host')), + 'port': (None, Validator('string', none_is_valid = True)), + 'db_name':('lodel', Validator('string')), + 'username': (None, Validator('string')), + 'password': (None, Validator('string')) } } diff --git a/lodel/plugins/multisite/__init__.py b/lodel/plugins/multisite/__init__.py index 6a5e7fe..6d3d50a 100644 --- a/lodel/plugins/multisite/__init__.py +++ b/lodel/plugins/multisite/__init__.py @@ -1,7 +1,7 @@ from lodel.context import LodelContext, ContextError try: LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) __plugin_name__ = "multisite" __version__ = '0.0.1' #or __version__ = [0,0,1] @@ -13,8 +13,8 @@ try: CONFSPEC = { 'lodel2.server': { - 'port': (80,SettingValidator('int')), - 'listen_addr': ('', SettingValidator('string')), + 'port': (80,Validator('int')), + 'listen_addr': ('', Validator('string')), } } diff --git a/lodel/plugins/multisite/confspecs.py b/lodel/plugins/multisite/confspecs.py index a8c8408..6015b57 100644 --- a/lodel/plugins/multisite/confspecs.py +++ b/lodel/plugins/multisite/confspecs.py @@ -1,34 +1,34 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) #Define a minimal confspec used by multisite loader LODEL2_CONFSPECS = { 'lodel2': { - 'debug': (True, SettingValidator('bool')) + 'debug': (True, Validator('bool')) }, 'lodel2.server': { - 'listen_address': ('127.0.0.1', SettingValidator('dummy')), - #'listen_address': ('', SettingValidator('ip')), #<-- not implemented - 'listen_port': ( 1337, SettingValidator('int')), - 'uwsgi_workers': (8, SettingValidator('int')), - 'uwsgicmd': ('/usr/bin/uwsgi', SettingValidator('dummy')), - 'virtualenv': (None, SettingValidator('path', none_is_valid = True)), + 'listen_address': ('127.0.0.1', Validator('dummy')), + #'listen_address': ('', Validator('ip')), #<-- not implemented + 'listen_port': ( 1337, Validator('int')), + 'uwsgi_workers': (8, Validator('int')), + 'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')), + 'virtualenv': (None, Validator('path', none_is_valid = True)), }, 'lodel2.logging.*' : { 'level': ( 'ERROR', - SettingValidator('loglevel')), + Validator('loglevel')), 'context': ( False, - SettingValidator('bool')), + Validator('bool')), 'filename': ( None, - SettingValidator('errfile', none_is_valid = True)), + Validator('errfile', none_is_valid = True)), 'backupcount': ( 10, - SettingValidator('int', none_is_valid = False)), + Validator('int', none_is_valid = False)), 'maxbytes': ( 1024*10, - SettingValidator('int', none_is_valid = False)), + Validator('int', none_is_valid = False)), }, 'lodel2.datasources.*': { - 'read_only': (False, SettingValidator('bool')), - 'identifier': ( None, SettingValidator('string')), + 'read_only': (False, Validator('bool')), + 'identifier': ( None, Validator('string')), } } diff --git a/lodel/plugins/ram_sessions/__init__.py b/lodel/plugins/ram_sessions/__init__.py index 8d580c4..536bd25 100644 --- a/lodel/plugins/ram_sessions/__init__.py +++ b/lodel/plugins/ram_sessions/__init__.py @@ -1,6 +1,6 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) __plugin_name__ = 'ram_sessions' __version__ = [0,0,1] @@ -11,7 +11,7 @@ __fullname__ = "RAM Session Store Plugin" CONFSPEC = { 'lodel2.sessions':{ - 'expiration': (900, SettingValidator('int')), - 'tokensize': (512, SettingValidator('int')), + 'expiration': (900, Validator('int')), + 'tokensize': (512, Validator('int')), } } diff --git a/lodel/plugins/webui/confspec.py b/lodel/plugins/webui/confspec.py index b1cdded..5c1abfc 100644 --- a/lodel/plugins/webui/confspec.py +++ b/lodel/plugins/webui/confspec.py @@ -1,30 +1,30 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.settings.validator': ['SettingValidator']}) + 'lodel.validator.validator': ['Validator']}) CONFSPEC = { 'lodel2.webui': { 'standalone': ( 'False', - SettingValidator('string')), + Validator('string')), 'listen_address': ( '127.0.0.1', - SettingValidator('dummy')), + Validator('dummy')), 'listen_port': ( '9090', - SettingValidator('int')), + Validator('int')), 'static_url': ( 'http://127.0.0.1/static/', - SettingValidator('regex', pattern = r'^https?://[^/].*$')), + Validator('regex', pattern = r'^https?://[^/].*$')), 'virtualenv': (None, - SettingValidator('path', none_is_valid=True)), - 'uwsgicmd': ('/usr/bin/uwsgi', SettingValidator('dummy')), - 'cookie_secret_key': ('ConfigureYourOwnCookieSecretKey', SettingValidator('dummy')), - 'cookie_session_id': ('lodel', SettingValidator('dummy')), - 'uwsgi_workers': (2, SettingValidator('int')) + Validator('path', none_is_valid=True)), + 'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')), + 'cookie_secret_key': ('ConfigureYourOwnCookieSecretKey', Validator('dummy')), + 'cookie_session_id': ('lodel', Validator('dummy')), + 'uwsgi_workers': (2, Validator('int')) }, 'lodel2.webui.sessions': { 'directory': ( '/tmp', - SettingValidator('path')), + Validator('path')), 'expiration': ( 900, - SettingValidator('int')), + Validator('int')), 'file_template': ( 'lodel2_%s.sess', - SettingValidator('dummy')), + Validator('dummy')), } } diff --git a/lodel/plugins/webui/templates/listing/issue.html b/lodel/plugins/webui/templates/listing/issue.html index 0872734..c6ecda9 100644 --- a/lodel/plugins/webui/templates/listing/issue.html +++ b/lodel/plugins/webui/templates/listing/issue.html @@ -4,7 +4,7 @@ {% set objects = my_classes.Issue.get(('%s = %s') % (uidfield, lodel_id)) %} {% set person_class = leapi.name2class('Person') %} {% set obj = objects.pop() %} -{% block content %} +{% block content %}