From b3437848bb5edc0a3a0d92c4d5238246b82e03f0 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 2 Feb 2017 09:32:48 +0100 Subject: [PATCH 01/35] Added the DatahandlerOption class --- lodel/leapi/datahandlers/base_classes.py | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index cd7e9eb..6157282 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -488,3 +488,36 @@ 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(object): + + ## @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.__display_name = display_name + self.__help_text = help_text + self.__validator = validator + + @property + def id(self): + return self.__id + + @property + def display_name(self): + return self.__display_name + + @property + def help_text(self): + return self.__help_text + + ## @brief checks a value corresponding to this option is valid + # @param value + # @return casted value + def check_value(self, value): + return self.__validator(value) From ac2312dad0c1ab2cff94b9d026955e8488e3c392 Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 11:17:03 +0100 Subject: [PATCH 02/35] Class validator --- lodel/settings/settings_loader.py | 4 +- lodel/settings/validator.py | 303 +------------------------- lodel/validator/__init__.py | 6 + lodel/validator/validator.py | 339 +++++++++++++++++++++++++++++ lodel/validator/validator.py.sav | 341 ++++++++++++++++++++++++++++++ 5 files changed, 695 insertions(+), 298 deletions(-) create mode 100644 lodel/validator/__init__.py create mode 100644 lodel/validator/validator.py create mode 100644 lodel/validator/validator.py.sav diff --git a/lodel/settings/settings_loader.py b/lodel/settings/settings_loader.py index ff245ed..587a6ae 100644 --- a/lodel/settings/settings_loader.py +++ b/lodel/settings/settings_loader.py @@ -9,7 +9,7 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.settings.utils': ['SettingsError', 'SettingsErrors'], - 'lodel.settings.validator': ['SettingsValidationError']}) + 'lodel.settings.validator': ['SettingValidationError']}) ##@brief Merges and loads configuration files class SettingsLoader(object): @@ -107,7 +107,7 @@ class SettingsLoader(object): key_id = section+'.'+keyname) self.__errors_list.append(expt) else: - expt = SettingsValidationError( + expt = ValidationError( "For %s.%s : %s" % (section, keyname,e) ) diff --git a/lodel/settings/validator.py b/lodel/settings/validator.py index fd97aa1..554ab9c 100644 --- a/lodel/settings/validator.py +++ b/lodel/settings/validator.py @@ -10,7 +10,8 @@ import copy from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'FieldValidationError']}) + 'LodelFatalError', 'FieldValidationError'], + 'lodel.validator.validator': ['Validator', 'ValidationError']}) ## @package lodel.settings.validator Lodel2 settings validators/cast module # @@ -19,7 +20,7 @@ LodelContext.expose_modules(globals(), { #
$ python scripts/settings_validator.py
##@brief Exception class that should be raised when a validation fails -class SettingsValidationError(Exception): +class SettingValidationError(ValidationError): pass ##@brief Handles settings validators @@ -28,216 +29,21 @@ class SettingsValidationError(Exception): # a SettingsValidationError if validation fails, else it returns a properly # casted value. #@todo implement an IP validator and use it in multisite confspec -class SettingValidator(object): - - _validators = dict() - _description = dict() - +class SettingValidator(Validator): + ##@brief Instanciate a validator #@param name str : validator name #@param none_is_valid bool : if True None will be validated #@param **kwargs : more arguement for the validator def __init__(self, name, none_is_valid = False, **kwargs): - if name is not None and name not in self._validators: - raise LodelFatalError("No validator named '%s'" % name) - self.__none_is_valid = none_is_valid - self.__name = name - self._opt_args = kwargs + super().__init__(name, none_is_valid = False, **kwargs) ##@brief Call the validator # @param value * # @return properly casted value # @throw SettingsValidationError def __call__(self, value): - if self.__none_is_valid and value is None: - return None - try: - ret = self._validators[self.__name](value, **self._opt_args) - return ret - except Exception as e: - raise SettingsValidationError(e) - - ##@brief Register a new validator - # @param name str : validator name - # @param callback callable : the function that will validate a value - # @param description str - @classmethod - def register_validator(cls, name, callback, description=None): - if name in cls._validators: - raise NameError("A validator named '%s' allready exists" % name) - # Broken test for callable - if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'): - raise TypeError("Callable expected but got %s" % type(callback)) - cls._validators[name] = callback - cls._description[name] = description - - ##@brief Get the validator list associated with description - @classmethod - def validators_list(cls): - return copy.copy(cls._description) - - ##@brief Create and register a list validator - # @param elt_validator callable : The validator that will be used for validate each elt value - # @param validator_name str - # @param description None | str - # @param separator str : The element separator - # @return A SettingValidator instance - @classmethod - def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): - def list_validator(value): - res = list() - errors = list() - for elt in value.split(separator): - elt = elt_validator(elt) - if len(elt) > 0: - res.append(elt) - return res - description = "Convert value to an array" if description is None else description - cls.register_validator( - validator_name, - list_validator, - description) - return cls(validator_name) - - ##@brief Create and register a list validator which reads an array and returns a string - # @param elt_validator callable : The validator that will be used for validate each elt value - # @param validator_name str - # @param description None | str - # @param separator str : The element separator - # @return A SettingValidator instance - @classmethod - def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): - def write_list_validator(value): - res = '' - errors = list() - for elt in value: - res += elt_validator(elt) + ',' - return res[:len(res)-1] - description = "Convert value to a string" if description is None else description - cls.register_validator( - validator_name, - write_list_validator, - description) - return cls(validator_name) - - ##@brief Create and register a regular expression validator - # @param pattern str : regex pattern - # @param validator_name str : The validator name - # @param description str : Validator description - # @return a SettingValidator instance - @classmethod - def create_re_validator(cls, pattern, validator_name, description = None): - def re_validator(value): - if not re.match(pattern, value): - raise SettingsValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern) - return value - #registering the validator - cls.register_validator( - validator_name, - re_validator, - ("Match value to '%s'" % pattern) if description is None else description) - return cls(validator_name) - - - ## @return a list of registered validators - @classmethod - def validators_list_str(cls): - result = '' - for name in sorted(cls._validators.keys()): - result += "\t%016s" % name - if name in cls._description and cls._description[name] is not None: - result += ": %s" % cls._description[name] - result += "\n" - return result - -##@brief Integer value validator callback -def int_val(value): - return int(value) - -##@brief Output file validator callback -# @return A file object (if filename is '-' return sys.stderr) -def file_err_output(value): - if not isinstance(value, str): - raise SettingsValidationError("A string was expected but got '%s' " % value) - if value == '-': - return None - return value - -##@brief Boolean value validator callback -def boolean_val(value): - if isinstance(value, bool): - return value - if value.strip().lower() == 'true' or value.strip() == '1': - value = True - elif value.strip().lower() == 'false' or value.strip() == '0': - value = False - else: - raise SettingsValidationError("A boolean was expected but got '%s' " % value) - return bool(value) - -##@brief Validate a directory path -def directory_val(value): - res = SettingValidator('strip')(value) - if not os.path.isdir(res): - raise SettingsValidationError("Folowing path don't exists or is not a directory : '%s'"%res) - return res - -##@brief Validate a loglevel value -def loglevel_val(value): - valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] - if value.upper() not in valids: - raise SettingsValidationError( - "The value '%s' is not a valid loglevel" % value) - return value.upper() - -##@brief Validate a path -def path_val(value): - if value is None or not os.path.exists(value): - raise SettingsValidationError( - "path '%s' doesn't exists" % value) - return value - -##@brief Validate None -def none_val(value): - if value is None: - return None - raise SettingsValidationError("This settings cannot be set in configuration file") - -##@brief Validate a string -def str_val(value): - try: - return str(value) - except Exception as e: - raise SettingsValidationError("Not able to convert value to string : " + str(e)) - -##@brief Validate using a regex -def regex_val(value, pattern): - if re.match(pattern, value) is None: - raise SettingsValidationError("The value '%s' is not validated by : \ -r\"%s\"" %(value, pattern)) - return value - -##@brief Validate a hostname (ipv4 or ipv6) -def host_val(value): - if value == 'localhost': - return value - ok = False - try: - socket.inet_aton(value) - return value - except (TypeError,OSError): - pass - try: - socket.inet_pton(socket.AF_INET6, value) - return value - except (TypeError,OSError): - pass - try: - socket.getaddrinfo(value, 80) - return value - except (TypeError,socket.gaierror): - msg = "The value '%s' is not a valid host" - raise SettingsValidationError(msg % value) + super().__call__(value) ##@brief Validator for Editorial model component # @@ -292,113 +98,18 @@ named '%s' that is a '%s' plugin" raise SettingsValidationError(msg) return value -def custom_list_validator(value, validator_name, validator_kwargs = None): - validator_kwargs = dict() if validator_kwargs is None else validator_kwargs - validator = SettingValidator(validator_name, **validator_kwargs) - for item in value.split(): - validator(item) - return value.split() -# -# Default validators registration -# SettingValidator.register_validator( 'plugin', plugin_validator, 'plugin name & type validator') -SettingValidator.register_validator( - 'custom_list', - custom_list_validator, - 'A list validator that takes a "validator_name" as argument') - -SettingValidator.register_validator( - 'dummy', - lambda value:value, - 'Validate anything') - -SettingValidator.register_validator( - 'none', - none_val, - 'Validate None') - -SettingValidator.register_validator( - 'string', - str_val, - 'Validate string values') - -SettingValidator.register_validator( - 'strip', - str.strip, - 'String trim') - -SettingValidator.register_validator( - 'int', - int_val, - 'Integer value validator') - -SettingValidator.register_validator( - 'bool', - boolean_val, - 'Boolean value validator') - -SettingValidator.register_validator( - 'errfile', - file_err_output, - 'Error output file validator (return stderr if filename is "-")') - -SettingValidator.register_validator( - 'directory', - directory_val, - 'Directory path validator') - -SettingValidator.register_validator( - 'loglevel', - loglevel_val, - 'Loglevel validator') - -SettingValidator.register_validator( - 'path', - path_val, - 'path validator') - -SettingValidator.register_validator( - 'host', - host_val, - 'host validator') - SettingValidator.register_validator( 'emfield', emfield_val, 'EmField name validator') -SettingValidator.register_validator( - 'regex', - regex_val, - 'RegEx name validator (take re as argument)') - -SettingValidator.create_list_validator( - 'list', - SettingValidator('strip'), - description = "Simple list validator. Validate a list of values separated by ','", - separator = ',') - -SettingValidator.create_list_validator( - 'directory_list', - SettingValidator('directory'), - description = "Validator for a list of directory path separated with ','", - separator = ',') -SettingValidator.create_write_list_validator( - 'write_list', - SettingValidator('directory'), - description = "Validator for an array of values which will be set in a string, separated by ','", - separator = ',') -SettingValidator.create_re_validator( - r'^https?://[^\./]+.[^\./]+/?.*$', - 'http_url', - 'Url validator') - # # Lodel 2 configuration specification # diff --git a/lodel/validator/__init__.py b/lodel/validator/__init__.py new file mode 100644 index 0000000..a6e0e37 --- /dev/null +++ b/lodel/validator/__init__.py @@ -0,0 +1,6 @@ +#-*- coding: utf-8 -*- + +## @package lodel.validator Lodel2 validator package +# + + diff --git a/lodel/validator/validator.py b/lodel/validator/validator.py new file mode 100644 index 0000000..b1a8759 --- /dev/null +++ b/lodel/validator/validator.py @@ -0,0 +1,339 @@ +#-*- coding: utf-8 -*- + +import sys +import os.path +import re +import socket +import inspect +import copy + +from lodel.context import LodelContext +LodelContext.expose_modules(globals(), { + 'lodel.exceptions': ['LodelException', 'LodelExceptions', + 'LodelFatalError', 'FieldValidationError']}) + +## @package lodel.settings.validator Lodel2 settings validators/cast module +# +# Validator are registered in the Validator class. +# @note to get a list of registered default validators just run +#
$ python scripts/settings_validator.py
+ +##@brief Exception class that should be raised when a validation fails +class ValidationError(Exception): + pass + +##@brief Handles settings validators +# +# Class instance are callable objects that takes a value argument (the value to validate). It raises +# a ValidationError if validation fails, else it returns a properly +# casted value. +#@todo implement an IP validator and use it in multisite confspec +class Validator(object): + + _validators = dict() + _description = dict() + + ##@brief Instanciate a validator + #@param name str : validator name + #@param none_is_valid bool : if True None will be validated + #@param **kwargs : more arguement for the validator + def __init__(self, name, none_is_valid = False, **kwargs): + if name is not None and name not in self._validators: + raise LodelFatalError("No validator named '%s'" % name) + self.__none_is_valid = none_is_valid + self.__name = name + self._opt_args = kwargs + + ##@brief Call the validator + # @param value * + # @return properly casted value + # @throw ValidationError + def __call__(self, value): + if self.__none_is_valid and value is None: + return None + try: + ret = self._validators[self.__name](value, **self._opt_args) + return ret + except Exception as e: + raise ValidationError(e) + + ##@brief Register a new validator + # @param name str : validator name + # @param callback callable : the function that will validate a value + # @param description str + @classmethod + def register_validator(cls, name, callback, description=None): + if name in cls._validators: + raise NameError("A validator named '%s' allready exists" % name) + # Broken test for callable + if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'): + raise TypeError("Callable expected but got %s" % type(callback)) + cls._validators[name] = callback + cls._description[name] = description + + ##@brief Get the validator list associated with description + @classmethod + def validators_list(cls): + return copy.copy(cls._description) + + ##@brief Create and register a list validator + # @param elt_validator callable : The validator that will be used for validate each elt value + # @param validator_name str + # @param description None | str + # @param separator str : The element separator + # @return A Validator instance + @classmethod + def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): + def list_validator(value): + res = list() + errors = list() + for elt in value.split(separator): + elt = elt_validator(elt) + if len(elt) > 0: + res.append(elt) + return res + description = "Convert value to an array" if description is None else description + cls.register_validator( + validator_name, + list_validator, + description) + return cls(validator_name) + + ##@brief Create and register a list validator which reads an array and returns a string + # @param elt_validator callable : The validator that will be used for validate each elt value + # @param validator_name str + # @param description None | str + # @param separator str : The element separator + # @return A Validator instance + @classmethod + def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): + def write_list_validator(value): + res = '' + errors = list() + for elt in value: + res += elt_validator(elt) + ',' + return res[:len(res)-1] + description = "Convert value to a string" if description is None else description + cls.register_validator( + validator_name, + write_list_validator, + description) + return cls(validator_name) + + ##@brief Create and register a regular expression validator + # @param pattern str : regex pattern + # @param validator_name str : The validator name + # @param description str : Validator description + # @return a Validator instance + @classmethod + def create_re_validator(cls, pattern, validator_name, description = None): + def re_validator(value): + if not re.match(pattern, value): + raise ValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern) + return value + #registering the validator + cls.register_validator( + validator_name, + re_validator, + ("Match value to '%s'" % pattern) if description is None else description) + return cls(validator_name) + + + ## @return a list of registered validators + @classmethod + def validators_list_str(cls): + result = '' + for name in sorted(cls._validators.keys()): + result += "\t%016s" % name + if name in cls._description and cls._description[name] is not None: + result += ": %s" % cls._description[name] + result += "\n" + return result + +##@brief Integer value validator callback +def int_val(value): + return int(value) + +##@brief Output file validator callback +# @return A file object (if filename is '-' return sys.stderr) +def file_err_output(value): + if not isinstance(value, str): + raise ValidationError("A string was expected but got '%s' " % value) + if value == '-': + return None + return value + +##@brief Boolean value validator callback +def boolean_val(value): + if isinstance(value, bool): + return value + if value.strip().lower() == 'true' or value.strip() == '1': + value = True + elif value.strip().lower() == 'false' or value.strip() == '0': + value = False + else: + raise ValidationError("A boolean was expected but got '%s' " % value) + return bool(value) + +##@brief Validate a directory path +def directory_val(value): + res = Validator('strip')(value) + if not os.path.isdir(res): + raise ValidationError("Folowing path don't exists or is not a directory : '%s'"%res) + return res + +##@brief Validate a loglevel value +def loglevel_val(value): + valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] + if value.upper() not in valids: + raise ValidationError( + "The value '%s' is not a valid loglevel" % value) + return value.upper() + +##@brief Validate a path +def path_val(value): + if value is None or not os.path.exists(value): + raise ValidationError( + "path '%s' doesn't exists" % value) + return value + +##@brief Validate None +def none_val(value): + if value is None: + return None + raise ValidationError("This settings cannot be set in configuration file") + +##@brief Validate a string +def str_val(value): + try: + return str(value) + except Exception as e: + raise ValidationError("Not able to convert value to string : " + str(e)) + +##@brief Validate using a regex +def regex_val(value, pattern): + if re.match(pattern, value) is None: + raise ValidationError("The value '%s' is not validated by : \ +r\"%s\"" %(value, pattern)) + return value + +##@brief Validate a hostname (ipv4 or ipv6) +def host_val(value): + if value == 'localhost': + return value + ok = False + try: + socket.inet_aton(value) + return value + except (TypeError,OSError): + pass + try: + socket.inet_pton(socket.AF_INET6, value) + return value + except (TypeError,OSError): + pass + try: + socket.getaddrinfo(value, 80) + return value + except (TypeError,socket.gaierror): + msg = "The value '%s' is not a valid host" + raise ValidationError(msg % value) + +def custom_list_validator(value, validator_name, validator_kwargs = None): + validator_kwargs = dict() if validator_kwargs is None else validator_kwargs + validator = Validator(validator_name, **validator_kwargs) + for item in value.split(): + validator(item) + return value.split() + +# +# Default validators registration +# + +Validator.register_validator( + 'custom_list', + custom_list_validator, + 'A list validator that takes a "validator_name" as argument') + +Validator.register_validator( + 'dummy', + lambda value:value, + 'Validate anything') + +Validator.register_validator( + 'none', + none_val, + 'Validate None') + +Validator.register_validator( + 'string', + str_val, + 'Validate string values') + +Validator.register_validator( + 'strip', + str.strip, + 'String trim') + +Validator.register_validator( + 'int', + int_val, + 'Integer value validator') + +Validator.register_validator( + 'bool', + boolean_val, + 'Boolean value validator') + +Validator.register_validator( + 'errfile', + file_err_output, + 'Error output file validator (return stderr if filename is "-")') + +Validator.register_validator( + 'directory', + directory_val, + 'Directory path validator') + +Validator.register_validator( + 'loglevel', + loglevel_val, + 'Loglevel validator') + +Validator.register_validator( + 'path', + path_val, + 'path validator') + +Validator.register_validator( + 'host', + host_val, + 'host validator') + +Validator.register_validator( + 'regex', + regex_val, + 'RegEx name validator (take re as argument)') + +Validator.create_list_validator( + 'list', + Validator('strip'), + description = "Simple list validator. Validate a list of values separated by ','", + separator = ',') + +Validator.create_list_validator( + 'directory_list', + Validator('directory'), + description = "Validator for a list of directory path separated with ','", + separator = ',') + +Validator.create_write_list_validator( + 'write_list', + Validator('directory'), + description = "Validator for an array of values which will be set in a string, separated by ','", + separator = ',') + +Validator.create_re_validator( + r'^https?://[^\./]+.[^\./]+/?.*$', + 'http_url', + 'Url validator') diff --git a/lodel/validator/validator.py.sav b/lodel/validator/validator.py.sav new file mode 100644 index 0000000..ab3963f --- /dev/null +++ b/lodel/validator/validator.py.sav @@ -0,0 +1,341 @@ +#-*- coding: utf-8 -*- + +import sys +import os.path +import re +import socket +import inspect +import copy + +from lodel.context import LodelContext +LodelContext.expose_modules(globals(), { + 'lodel.exceptions': ['LodelException', 'LodelExceptions', + 'LodelFatalError', 'FieldValidationError']}) + +## @package lodel.settings.validator Lodel2 settings validators/cast module +# +# Validator are registered in the Validator class. +# @note to get a list of registered default validators just run +#
$ python scripts/settings_validator.py
+ +##@brief Exception class that should be raised when a validation fails +class ValidationError(Exception): + pass + +##@brief Handles settings validators +# +# Class instance are callable objects that takes a value argument (the value to validate). It raises +# a ValidationError if validation fails, else it returns a properly +# casted value. + +class Validator(object): + + _validators = dict() + _description = dict() + + ##@brief Instanciate a validator + #@param name str : validator name + #@param none_is_valid bool : if True None will be validated + #@param **kwargs : more arguement for the validator + def __init__(self, name, none_is_valid = False, **kwargs): + if name is not None and name not in self._validators: + raise LodelFatalError("No validator named '%s'" % name) + self.__none_is_valid = none_is_valid + self.__name = name + self._opt_args = kwargs + + ##@brief Call the validator + # @param value * + # @return properly casted value + # @throw ValidationError + def __call__(self, value): + if self.__none_is_valid and value is None: + return None + try: + ret = self._validators[self.__name](value, **self._opt_args) + return ret + except Exception as e: + raise ValidationError(e) + + ##@brief Register a new validator + # @param name str : validator name + # @param callback callable : the function that will validate a value + # @param description str + @classmethod + def register_validator(cls, name, callback, description=None): + if name in cls._validators: + raise NameError("A validator named '%s' allready exists" % name) + # Broken test for callable + if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'): + raise TypeError("Callable expected but got %s" % type(callback)) + cls._validators[name] = callback + cls._description[name] = description + + ##@brief Get the validator list associated with description + @classmethod + def validators_list(cls): + return copy.copy(cls._description) + + ##@brief Create and register a list validator + # @param elt_validator callable : The validator that will be used for validate each elt value + # @param validator_name str + # @param description None | str + # @param separator str : The element separator + # @return A SettingValidator instance + @classmethod + def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): + def list_validator(value): + res = list() + errors = list() + for elt in value.split(separator): + elt = elt_validator(elt) + if len(elt) > 0: + res.append(elt) + return res + description = "Convert value to an array" if description is None else description + cls.register_validator( + validator_name, + list_validator, + description) + return cls(validator_name) + + ##@brief Create and register a list validator which reads an array and returns a string + # @param elt_validator callable : The validator that will be used for validate each elt value + # @param validator_name str + # @param description None | str + # @param separator str : The element separator + # @return A SettingValidator instance + @classmethod + def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): + def write_list_validator(value): + res = '' + errors = list() + for elt in value: + res += elt_validator(elt) + ',' + return res[:len(res)-1] + description = "Convert value to a string" if description is None else description + cls.register_validator( + validator_name, + write_list_validator, + description) + return cls(validator_name) + + ##@brief Create and register a regular expression validator + # @param pattern str : regex pattern + # @param validator_name str : The validator name + # @param description str : Validator description + # @return a SettingValidator instance + @classmethod + def create_re_validator(cls, pattern, validator_name, description = None): + def re_validator(value): + if not re.match(pattern, value): + raise ValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern) + return value + #registering the validator + cls.register_validator( + validator_name, + re_validator, + ("Match value to '%s'" % pattern) if description is None else description) + return cls(validator_name) + + + ## @return a list of registered validators + @classmethod + def validators_list_str(cls): + result = '' + for name in sorted(cls._validators.keys()): + result += "\t%016s" % name + if name in cls._description and cls._description[name] is not None: + result += ": %s" % cls._description[name] + result += "\n" + return result + +##@brief Integer value validator callback +def int_val(value): + return int(value) + +##@brief Output file validator callback +# @return A file object (if filename is '-' return sys.stderr) +def file_err_output(value): + if not isinstance(value, str): + raise ValidationError("A string was expected but got '%s' " % value) + if value == '-': + return None + return value + +##@brief Boolean value validator callback +def boolean_val(value): + if isinstance(value, bool): + return value + if value.strip().lower() == 'true' or value.strip() == '1': + value = True + elif value.strip().lower() == 'false' or value.strip() == '0': + value = False + else: + raise ValidationError("A boolean was expected but got '%s' " % value) + return bool(value) + +##@brief Validate a directory path +def directory_val(value): + res = SettingValidator('strip')(value) + if not os.path.isdir(res): + raise SettingsValidationError("Following path doesn't exist or is not a directory : '%s'"%res) + return res + +##@brief Validate a loglevel value +def loglevel_val(value): + valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] + if value.upper() not in valids: + raise SettingsValidationError( + "The value '%s' is not a valid loglevel" % value) + return value.upper() + +##@brief Validate a path +def path_val(value): + if value is None or not os.path.exists(value): + raise ValidationError( + "path '%s' doesn't exists" % value) + return value + +##@brief Validate None +def none_val(value): + if value is None: + return None + raise ValidationError("None value is required to be validated") + +##@brief Validate a string +def str_val(value): + try: + return str(value) + except Exception as e: + raise ValidationError("Not able to convert value to string : " + str(e)) + +##@brief Validate using a regex +def regex_val(value, pattern): + if re.match(pattern, value) is None: + raise ValidationError("The value '%s' is not validated by : \ +r\"%s\"" %(value, pattern)) + return value + +##@brief Validate a hostname (ipv4 or ipv6) +def host_val(value): + if value == 'localhost': + return value + ok = False + try: + socket.inet_aton(value) + return value + except (TypeError,OSError): + pass + try: + socket.inet_pton(socket.AF_INET6, value) + return value + except (TypeError,OSError): + pass + try: + socket.getaddrinfo(value, 80) + return value + except (TypeError,socket.gaierror): + msg = "The value '%s' is not a valid host" + raise ValidationError(msg % value) + + +def custom_list_validator(value, validator_name, validator_kwargs = None): + validator_kwargs = dict() if validator_kwargs is None else validator_kwargs + validator = Validator(validator_name, **validator_kwargs) + for item in value.split(): + validator(item) + return value.split() + +# +# Default validators registration +# + + +Validator.register_validator( + 'custom_list', + custom_list_validator, + 'A list validator that takes a "validator_name" as argument') + +Validator.register_validator( + 'dummy', + lambda value:value, + 'Validate anything') + +Validator.register_validator( + 'none', + none_val, + 'Validate None') + +Validator.register_validator( + 'string', + str_val, + 'Validate string values') + +Validator.register_validator( + 'strip', + str.strip, + 'String trim') + +Validator.register_validator( + 'int', + int_val, + 'Integer value validator') + +Validator.register_validator( + 'bool', + boolean_val, + 'Boolean value validator') + +Validator.register_validator( + 'errfile', + file_err_output, + 'Error output file validator (return stderr if filename is "-")') + +Validator.register_validator( + 'directory', + directory_val, + 'Directory path validator') + +Validator.register_validator( + 'loglevel', + loglevel_val, + 'Loglevel validator') + +Validator.register_validator( + 'path', + path_val, + 'path validator') + +Validator.register_validator( + 'host', + host_val, + 'host validator') + +Validator.register_validator( + 'regex', + regex_val, + 'RegEx name validator (take re as argument)') + +Validator.create_list_validator( + 'list', + Validator('strip'), + description = "Simple list validator. Validate a list of values separated by ','", + separator = ',') + +Validator.create_list_validator( + 'directory_list', + Validator('directory'), + description = "Validator for a list of directory path separated with ','", + separator = ',') + +Validator.create_write_list_validator( + 'write_list', + Validator('directory'), + description = "Validator for an array of values which will be set in a string, separated by ','", + separator = ',') + +Validator.create_re_validator( + r'^https?://[^\./]+.[^\./]+/?.*$', + 'http_url', + 'Url validator') From 7a1416ee21e00ba2b8c24e25149b6799b54124fc Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 11:30:43 +0100 Subject: [PATCH 03/35] Class DhOptionvaidator --- lodel/leapi/datahandlers/validator.py | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lodel/leapi/datahandlers/validator.py diff --git a/lodel/leapi/datahandlers/validator.py b/lodel/leapi/datahandlers/validator.py new file mode 100644 index 0000000..a30b0a0 --- /dev/null +++ b/lodel/leapi/datahandlers/validator.py @@ -0,0 +1,42 @@ +#-*- coding: utf-8 -*- + +import sys + +from lodel.context import LodelContext +LodelContext.expose_modules(globals(), { + 'lodel.exceptions': ['LodelException', 'LodelExceptions', + 'LodelFatalError', 'FieldValidationError'], + 'lodel.validator.validator': ['Validator', 'ValidationError']}) + +## @package lodel.DhOptions.validator Lodel2 DhOptions validators/cast module +# +# Validator are registered in the DhOptionValidator class. +# @note to get a list of registered default validators just run +#
$ python scripts/DhOptions_validator.py
+ +##@brief Exception class that should be raised when a validation fails +class DhOptionValidationError(ValidationError): + pass + +##@brief Handles DhOptions validators +# +# Class instance are callable objects that takes a value argument (the value to validate). It raises +# a DhOptionsValidationError if validation fails, else it returns a properly +# casted value. +#@todo implement an IP validator and use it in multisite confspec +class DhOptionValidator(Validator): + + ##@brief Instanciate a validator + #@param name str : validator name + #@param none_is_valid bool : if True None will be validated + #@param **kwargs : more arguement for the validator + def __init__(self, name, none_is_valid = False, **kwargs): + super().__init__(name, none_is_valid = False, **kwargs) + + ##@brief Call the validator + # @param value * + # @return properly casted value + # @throw DhOptionsValidationError + def __call__(self, value): + super().__call__(value) + From c2f62bce50640a22c17ad52083626f9d2b9a0627 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 2 Feb 2017 10:51:15 +0100 Subject: [PATCH 04/35] Added new properties and the check_options method to the DataHandler class --- lodel/leapi/datahandlers/base_classes.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 6157282..73b987f 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -30,6 +30,9 @@ class DataHandler(object): __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 _construct_datas_deps = [] @@ -57,6 +60,15 @@ class DataHandler(object): del(kwargs['default']) for argname, argval in kwargs.items(): setattr(self, argname, argval) + self.check_options() + + def check_options(self): + for option_name, option_value in self.options_values.items(): + self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) + + @property + def options(self): + return self.options_values ## Fieldtype name @classmethod From 6e6463cb8f420484f982edb84612093d4395e1f7 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 2 Feb 2017 14:16:27 +0100 Subject: [PATCH 05/35] Added some changes to the DataHandler class to deal with the options and their values --- lodel/leapi/datahandlers/base_classes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 73b987f..c50b23d 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -64,6 +64,7 @@ class DataHandler(object): def check_options(self): for option_name, option_value in self.options_values.items(): + # TODO Change the call to self.options_spec if the specifications are passed as tuple self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) @property From 763e4a880da174f01652d6e7e0e36154c8172e1c Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 2 Feb 2017 14:33:00 +0100 Subject: [PATCH 06/35] Added a check in the options' check method, to see if the option name indicated corresponds to an existing option --- lodel/leapi/datahandlers/base_classes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index c50b23d..b3b37cf 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -65,7 +65,10 @@ class DataHandler(object): def check_options(self): for option_name, option_value in self.options_values.items(): # TODO Change the call to self.options_spec if the specifications are passed as tuple - self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) + if option_name in self.options_spec: + self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) + else: + pass # TODO decide what kind of exception should be raised here @property def options(self): From 3394543af87c315bca1a5674c19bcb87dc76b373 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 2 Feb 2017 14:37:33 +0100 Subject: [PATCH 07/35] Added a new exception raised when a not allowed option is passed to a datahandler --- lodel/leapi/datahandlers/base_classes.py | 5 ++++- lodel/leapi/datahandlers/exceptions.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index b3b37cf..74ae038 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -10,6 +10,7 @@ import inspect import warnings from lodel.context import LodelContext +from .exceptions import LodelDataHandlerNotAllowedOptionException LodelContext.expose_modules(globals(), { 'lodel.exceptions': ['LodelException', 'LodelExceptions', @@ -62,13 +63,15 @@ class DataHandler(object): setattr(self, argname, argval) self.check_options() + ## @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_value in self.options_values.items(): # TODO Change the call to self.options_spec if the specifications are passed as tuple if option_name in self.options_spec: self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) else: - pass # TODO decide what kind of exception should be raised here + raise LodelDataHandlerNotAllowedOptionException() @property def options(self): diff --git a/lodel/leapi/datahandlers/exceptions.py b/lodel/leapi/datahandlers/exceptions.py index 35cd59e..724006b 100644 --- a/lodel/leapi/datahandlers/exceptions.py +++ b/lodel/leapi/datahandlers/exceptions.py @@ -3,3 +3,6 @@ def LodelDataHandlerException(Exception): def LodelDataHandlerConsistencyException(LodelDataHandlerException): pass + +def LodelDataHandlerNotAllowedOptionException(LodelDataHandlerException): + pass \ No newline at end of file From 8844197c57be3132be640a0d04a269aada0dddee Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 14:51:49 +0100 Subject: [PATCH 08/35] New class MlNamedObject --- lodel/mlnamedobject/__init__.py | 0 lodel/mlnamedobject/mlnamedobject.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 lodel/mlnamedobject/__init__.py create mode 100644 lodel/mlnamedobject/mlnamedobject.py 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..f43c2f7 --- /dev/null +++ b/lodel/mlnamedobject/mlnamedobject.py @@ -0,0 +1,15 @@ +#-*- coding:utf-8 -*- + +from lodel.context import LodelContext +LodelContext.expose_modules(globals(), { + 'lodel.utils.mlstring': ['MlString'], + 'lodel.logger': 'logger', + 'lodel.settings': ['Settings'], + 'lodel.settings.utils': ['SettingsError']}) + +class MlNamedObject(object): + + def __init__(self, display_name, help_text): + self.display_name = MlString(display_name) + self.help_text = MlString(help_text) + From 3abf402cfb10332d1855c2b96984323fd150251a Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 14:58:24 +0100 Subject: [PATCH 09/35] Inheritance of MlNamedObject --- lodel/editorial_model/components.py | 5 +++-- lodel/editorial_model/model.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index ff27133..6735fc8 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -12,6 +12,7 @@ import hashlib from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.utils.mlstring': ['MlString'], + 'lodel.mlnamedobject': ['MlNamedObject'], 'lodel.settings': ['Settings'], 'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'], 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME']}) @@ -20,7 +21,7 @@ LodelContext.expose_modules(globals(), { # @see EmClass EmField # @todo forbid '.' in uid #@ingroup lodel2_em -class EmComponent(object): +class EmComponent(MlNamedObject): ##@brief Instanciate an EmComponent # @param uid str : uniq identifier @@ -268,7 +269,7 @@ class EmField(EmComponent): ##@brief Handles functionnal group of EmComponents #@ingroup lodel2_em -class EmGroup(object): +class EmGroup(MlNamedObject): ##@brief Create a new EmGroup # @note you should NEVER call the constructor yourself. Use Model.add_group instead diff --git a/lodel/editorial_model/model.py b/lodel/editorial_model/model.py index 5bdf0b0..4fd9707 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'], 'lodel.logger': 'logger', 'lodel.settings': ['Settings'], 'lodel.settings.utils': ['SettingsError'], @@ -16,7 +17,7 @@ LodelContext.expose_modules(globals(), { ##@brief Describe an editorial model #@ingroup lodel2_em -class EditorialModel(object): +class EditorialModel(MlNamedObject): ##@brief Create a new editorial model # @param name MlString|str|dict : the editorial model name From 6d5da933e0ecf7343364aeed0541bc3ae898e97b Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 15:10:01 +0100 Subject: [PATCH 10/35] Inheritance of MlNamedObject 2 --- lodel/editorial_model/components.py | 6 +- lodel/editorial_model/model.py | 7 +- lodel/mlnamedobject/mlnamedobject.py | 6 +- lodel/validator/validator.py | 8 +- lodel/validator/validator.py.sav | 341 --------------------------- 5 files changed, 17 insertions(+), 351 deletions(-) delete mode 100644 lodel/validator/validator.py.sav diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index 6735fc8..d8b3240 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -31,9 +31,8 @@ class EmComponent(MlNamedObject): 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: @@ -285,9 +284,8 @@ class EmGroup(MlNamedObject): self.require = dict() ##@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): diff --git a/lodel/editorial_model/model.py b/lodel/editorial_model/model.py index 4fd9707..388c973 100644 --- a/lodel/editorial_model/model.py +++ b/lodel/editorial_model/model.py @@ -22,7 +22,7 @@ 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 @@ -34,6 +34,11 @@ class EditorialModel(MlNamedObject): ## @brief Stores all activated classes indexed by id self.__active_classes = dict() self.__set_actives() + if display_name is None: + display_name = name + if help_text is None: + help_text = description + super().__init__(display_name, help_text) ##@brief EmClass uids accessor #@return a dict of emclasses diff --git a/lodel/mlnamedobject/mlnamedobject.py b/lodel/mlnamedobject/mlnamedobject.py index f43c2f7..d546c0e 100644 --- a/lodel/mlnamedobject/mlnamedobject.py +++ b/lodel/mlnamedobject/mlnamedobject.py @@ -9,7 +9,7 @@ LodelContext.expose_modules(globals(), { class MlNamedObject(object): - def __init__(self, display_name, help_text): - self.display_name = MlString(display_name) - self.help_text = MlString(help_text) + 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/validator/validator.py b/lodel/validator/validator.py index b1a8759..7cdcc90 100644 --- a/lodel/validator/validator.py +++ b/lodel/validator/validator.py @@ -9,6 +9,7 @@ import copy from lodel.context import LodelContext LodelContext.expose_modules(globals(), { + 'lodel.mlnamedobject': ['MlNamedObject'], 'lodel.exceptions': ['LodelException', 'LodelExceptions', 'LodelFatalError', 'FieldValidationError']}) @@ -28,7 +29,7 @@ class ValidationError(Exception): # a ValidationError if validation fails, else it returns a properly # casted value. #@todo implement an IP validator and use it in multisite confspec -class Validator(object): +class Validator(MlNamedObject): _validators = dict() _description = dict() @@ -37,12 +38,15 @@ class Validator(object): #@param name str : validator name #@param none_is_valid bool : if True None will be validated #@param **kwargs : more arguement for the validator - def __init__(self, name, none_is_valid = False, **kwargs): + def __init__(self, name, none_is_valid = False, display_name = None, help_text = None, **kwargs): if name is not None and name not in self._validators: raise LodelFatalError("No validator named '%s'" % name) self.__none_is_valid = none_is_valid self.__name = name self._opt_args = kwargs + if display_name is None: + display_name = name + super().__init__(display_name, help_text) ##@brief Call the validator # @param value * diff --git a/lodel/validator/validator.py.sav b/lodel/validator/validator.py.sav deleted file mode 100644 index ab3963f..0000000 --- a/lodel/validator/validator.py.sav +++ /dev/null @@ -1,341 +0,0 @@ -#-*- coding: utf-8 -*- - -import sys -import os.path -import re -import socket -import inspect -import copy - -from lodel.context import LodelContext -LodelContext.expose_modules(globals(), { - 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'FieldValidationError']}) - -## @package lodel.settings.validator Lodel2 settings validators/cast module -# -# Validator are registered in the Validator class. -# @note to get a list of registered default validators just run -#
$ python scripts/settings_validator.py
- -##@brief Exception class that should be raised when a validation fails -class ValidationError(Exception): - pass - -##@brief Handles settings validators -# -# Class instance are callable objects that takes a value argument (the value to validate). It raises -# a ValidationError if validation fails, else it returns a properly -# casted value. - -class Validator(object): - - _validators = dict() - _description = dict() - - ##@brief Instanciate a validator - #@param name str : validator name - #@param none_is_valid bool : if True None will be validated - #@param **kwargs : more arguement for the validator - def __init__(self, name, none_is_valid = False, **kwargs): - if name is not None and name not in self._validators: - raise LodelFatalError("No validator named '%s'" % name) - self.__none_is_valid = none_is_valid - self.__name = name - self._opt_args = kwargs - - ##@brief Call the validator - # @param value * - # @return properly casted value - # @throw ValidationError - def __call__(self, value): - if self.__none_is_valid and value is None: - return None - try: - ret = self._validators[self.__name](value, **self._opt_args) - return ret - except Exception as e: - raise ValidationError(e) - - ##@brief Register a new validator - # @param name str : validator name - # @param callback callable : the function that will validate a value - # @param description str - @classmethod - def register_validator(cls, name, callback, description=None): - if name in cls._validators: - raise NameError("A validator named '%s' allready exists" % name) - # Broken test for callable - if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'): - raise TypeError("Callable expected but got %s" % type(callback)) - cls._validators[name] = callback - cls._description[name] = description - - ##@brief Get the validator list associated with description - @classmethod - def validators_list(cls): - return copy.copy(cls._description) - - ##@brief Create and register a list validator - # @param elt_validator callable : The validator that will be used for validate each elt value - # @param validator_name str - # @param description None | str - # @param separator str : The element separator - # @return A SettingValidator instance - @classmethod - def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): - def list_validator(value): - res = list() - errors = list() - for elt in value.split(separator): - elt = elt_validator(elt) - if len(elt) > 0: - res.append(elt) - return res - description = "Convert value to an array" if description is None else description - cls.register_validator( - validator_name, - list_validator, - description) - return cls(validator_name) - - ##@brief Create and register a list validator which reads an array and returns a string - # @param elt_validator callable : The validator that will be used for validate each elt value - # @param validator_name str - # @param description None | str - # @param separator str : The element separator - # @return A SettingValidator instance - @classmethod - def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','): - def write_list_validator(value): - res = '' - errors = list() - for elt in value: - res += elt_validator(elt) + ',' - return res[:len(res)-1] - description = "Convert value to a string" if description is None else description - cls.register_validator( - validator_name, - write_list_validator, - description) - return cls(validator_name) - - ##@brief Create and register a regular expression validator - # @param pattern str : regex pattern - # @param validator_name str : The validator name - # @param description str : Validator description - # @return a SettingValidator instance - @classmethod - def create_re_validator(cls, pattern, validator_name, description = None): - def re_validator(value): - if not re.match(pattern, value): - raise ValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern) - return value - #registering the validator - cls.register_validator( - validator_name, - re_validator, - ("Match value to '%s'" % pattern) if description is None else description) - return cls(validator_name) - - - ## @return a list of registered validators - @classmethod - def validators_list_str(cls): - result = '' - for name in sorted(cls._validators.keys()): - result += "\t%016s" % name - if name in cls._description and cls._description[name] is not None: - result += ": %s" % cls._description[name] - result += "\n" - return result - -##@brief Integer value validator callback -def int_val(value): - return int(value) - -##@brief Output file validator callback -# @return A file object (if filename is '-' return sys.stderr) -def file_err_output(value): - if not isinstance(value, str): - raise ValidationError("A string was expected but got '%s' " % value) - if value == '-': - return None - return value - -##@brief Boolean value validator callback -def boolean_val(value): - if isinstance(value, bool): - return value - if value.strip().lower() == 'true' or value.strip() == '1': - value = True - elif value.strip().lower() == 'false' or value.strip() == '0': - value = False - else: - raise ValidationError("A boolean was expected but got '%s' " % value) - return bool(value) - -##@brief Validate a directory path -def directory_val(value): - res = SettingValidator('strip')(value) - if not os.path.isdir(res): - raise SettingsValidationError("Following path doesn't exist or is not a directory : '%s'"%res) - return res - -##@brief Validate a loglevel value -def loglevel_val(value): - valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] - if value.upper() not in valids: - raise SettingsValidationError( - "The value '%s' is not a valid loglevel" % value) - return value.upper() - -##@brief Validate a path -def path_val(value): - if value is None or not os.path.exists(value): - raise ValidationError( - "path '%s' doesn't exists" % value) - return value - -##@brief Validate None -def none_val(value): - if value is None: - return None - raise ValidationError("None value is required to be validated") - -##@brief Validate a string -def str_val(value): - try: - return str(value) - except Exception as e: - raise ValidationError("Not able to convert value to string : " + str(e)) - -##@brief Validate using a regex -def regex_val(value, pattern): - if re.match(pattern, value) is None: - raise ValidationError("The value '%s' is not validated by : \ -r\"%s\"" %(value, pattern)) - return value - -##@brief Validate a hostname (ipv4 or ipv6) -def host_val(value): - if value == 'localhost': - return value - ok = False - try: - socket.inet_aton(value) - return value - except (TypeError,OSError): - pass - try: - socket.inet_pton(socket.AF_INET6, value) - return value - except (TypeError,OSError): - pass - try: - socket.getaddrinfo(value, 80) - return value - except (TypeError,socket.gaierror): - msg = "The value '%s' is not a valid host" - raise ValidationError(msg % value) - - -def custom_list_validator(value, validator_name, validator_kwargs = None): - validator_kwargs = dict() if validator_kwargs is None else validator_kwargs - validator = Validator(validator_name, **validator_kwargs) - for item in value.split(): - validator(item) - return value.split() - -# -# Default validators registration -# - - -Validator.register_validator( - 'custom_list', - custom_list_validator, - 'A list validator that takes a "validator_name" as argument') - -Validator.register_validator( - 'dummy', - lambda value:value, - 'Validate anything') - -Validator.register_validator( - 'none', - none_val, - 'Validate None') - -Validator.register_validator( - 'string', - str_val, - 'Validate string values') - -Validator.register_validator( - 'strip', - str.strip, - 'String trim') - -Validator.register_validator( - 'int', - int_val, - 'Integer value validator') - -Validator.register_validator( - 'bool', - boolean_val, - 'Boolean value validator') - -Validator.register_validator( - 'errfile', - file_err_output, - 'Error output file validator (return stderr if filename is "-")') - -Validator.register_validator( - 'directory', - directory_val, - 'Directory path validator') - -Validator.register_validator( - 'loglevel', - loglevel_val, - 'Loglevel validator') - -Validator.register_validator( - 'path', - path_val, - 'path validator') - -Validator.register_validator( - 'host', - host_val, - 'host validator') - -Validator.register_validator( - 'regex', - regex_val, - 'RegEx name validator (take re as argument)') - -Validator.create_list_validator( - 'list', - Validator('strip'), - description = "Simple list validator. Validate a list of values separated by ','", - separator = ',') - -Validator.create_list_validator( - 'directory_list', - Validator('directory'), - description = "Validator for a list of directory path separated with ','", - separator = ',') - -Validator.create_write_list_validator( - 'write_list', - Validator('directory'), - description = "Validator for an array of values which will be set in a string, separated by ','", - separator = ',') - -Validator.create_re_validator( - r'^https?://[^\./]+.[^\./]+/?.*$', - 'http_url', - 'Url validator') From 5d2be8737a30e54ffbacaaf23ab09431a070805e Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 2 Feb 2017 15:55:49 +0100 Subject: [PATCH 11/35] Inheritance of MlNamedObject 3 --- lodel/editorial_model/components.py | 2 +- lodel/editorial_model/model.py | 2 +- lodel/mlnamedobject/mlnamedobject.py | 9 +++++---- lodel/validator/validator.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index d8b3240..27bf911 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -12,7 +12,7 @@ import hashlib from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.utils.mlstring': ['MlString'], - 'lodel.mlnamedobject': ['MlNamedObject'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.settings': ['Settings'], 'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'], 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME']}) diff --git a/lodel/editorial_model/model.py b/lodel/editorial_model/model.py index 388c973..f88e368 100644 --- a/lodel/editorial_model/model.py +++ b/lodel/editorial_model/model.py @@ -7,7 +7,7 @@ import copy from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.utils.mlstring': ['MlString'], - 'lodel.mlnamedobject': ['MlNamedObject'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.logger': 'logger', 'lodel.settings': ['Settings'], 'lodel.settings.utils': ['SettingsError'], diff --git a/lodel/mlnamedobject/mlnamedobject.py b/lodel/mlnamedobject/mlnamedobject.py index d546c0e..bd99a14 100644 --- a/lodel/mlnamedobject/mlnamedobject.py +++ b/lodel/mlnamedobject/mlnamedobject.py @@ -2,10 +2,11 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.utils.mlstring': ['MlString'], - 'lodel.logger': 'logger', - 'lodel.settings': ['Settings'], - 'lodel.settings.utils': ['SettingsError']}) + 'lodel.utils.mlstring': ['MlString']}) + +## @package lodel.mlnamedobject Lodel2 description of objects module +# +# Display name and Description of a lodel2 object class MlNamedObject(object): diff --git a/lodel/validator/validator.py b/lodel/validator/validator.py index 7cdcc90..6272ed8 100644 --- a/lodel/validator/validator.py +++ b/lodel/validator/validator.py @@ -9,7 +9,7 @@ import copy from lodel.context import LodelContext LodelContext.expose_modules(globals(), { - 'lodel.mlnamedobject': ['MlNamedObject'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.exceptions': ['LodelException', 'LodelExceptions', 'LodelFatalError', 'FieldValidationError']}) From 08ad11624d15b2a35eac6c88a4a53700d250f472 Mon Sep 17 00:00:00 2001 From: prieto Date: Fri, 3 Feb 2017 11:09:52 +0100 Subject: [PATCH 12/35] Class Validator replaces SettingValidator --- lodel/leapi/datahandlers/validator.py | 42 ----- lodel/plugin/datasource_plugin.py | 12 +- lodel/plugin/extensions.py | 4 +- lodel/plugin/interface.py | 4 +- lodel/plugin/sessionhandler.py | 4 +- lodel/plugins/dummy/__init__.py | 2 +- lodel/plugins/dummy/confspec.py | 4 +- lodel/plugins/dummy_datasource/__init__.py | 4 +- lodel/plugins/filesystem_session/__init__.py | 2 +- lodel/plugins/filesystem_session/confspec.py | 8 +- lodel/plugins/mongodb_datasource/confspec.py | 14 +- lodel/plugins/multisite/__init__.py | 6 +- lodel/plugins/multisite/confspecs.py | 30 ++-- lodel/plugins/ram_sessions/__init__.py | 6 +- lodel/plugins/webui/confspec.py | 26 +-- lodel/settings/settings.py | 2 +- lodel/settings/settings_loader.py | 2 +- lodel/settings/validator.py | 166 ------------------- lodel/validator/validator.py | 121 ++++++++++++++ scripts/settings_validator.py | 4 +- tests/settings/test_validator.py | 28 ++-- 21 files changed, 202 insertions(+), 289 deletions(-) delete mode 100644 lodel/leapi/datahandlers/validator.py diff --git a/lodel/leapi/datahandlers/validator.py b/lodel/leapi/datahandlers/validator.py deleted file mode 100644 index a30b0a0..0000000 --- a/lodel/leapi/datahandlers/validator.py +++ /dev/null @@ -1,42 +0,0 @@ -#-*- coding: utf-8 -*- - -import sys - -from lodel.context import LodelContext -LodelContext.expose_modules(globals(), { - 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'FieldValidationError'], - 'lodel.validator.validator': ['Validator', 'ValidationError']}) - -## @package lodel.DhOptions.validator Lodel2 DhOptions validators/cast module -# -# Validator are registered in the DhOptionValidator class. -# @note to get a list of registered default validators just run -#
$ python scripts/DhOptions_validator.py
- -##@brief Exception class that should be raised when a validation fails -class DhOptionValidationError(ValidationError): - pass - -##@brief Handles DhOptions validators -# -# Class instance are callable objects that takes a value argument (the value to validate). It raises -# a DhOptionsValidationError if validation fails, else it returns a properly -# casted value. -#@todo implement an IP validator and use it in multisite confspec -class DhOptionValidator(Validator): - - ##@brief Instanciate a validator - #@param name str : validator name - #@param none_is_valid bool : if True None will be validated - #@param **kwargs : more arguement for the validator - def __init__(self, name, none_is_valid = False, **kwargs): - super().__init__(name, none_is_valid = False, **kwargs) - - ##@brief Call the validator - # @param value * - # @return properly casted value - # @throw DhOptionsValidationError - def __call__(self, value): - super().__call__(value) - 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/settings/settings.py b/lodel/settings/settings.py index bec2f8b..662b602 100644 --- a/lodel/settings/settings.py +++ b/lodel/settings/settings.py @@ -13,7 +13,7 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(),{ 'lodel.logger': 'logger', 'lodel.settings.utils': ['SettingsError', 'SettingsErrors'], - 'lodel.settings.validator': ['SettingValidator', 'LODEL2_CONF_SPECS', + 'lodel.validator.validator': ['Validator', 'LODEL2_CONF_SPECS', 'confspec_append'], 'lodel.settings.settings_loader':['SettingsLoader']}) diff --git a/lodel/settings/settings_loader.py b/lodel/settings/settings_loader.py index 587a6ae..77c3656 100644 --- a/lodel/settings/settings_loader.py +++ b/lodel/settings/settings_loader.py @@ -9,7 +9,7 @@ from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.settings.utils': ['SettingsError', 'SettingsErrors'], - 'lodel.settings.validator': ['SettingValidationError']}) + 'lodel.validator.validator': ['ValidationError']}) ##@brief Merges and loads configuration files class SettingsLoader(object): diff --git a/lodel/settings/validator.py b/lodel/settings/validator.py index 554ab9c..652f2c0 100644 --- a/lodel/settings/validator.py +++ b/lodel/settings/validator.py @@ -1,169 +1,3 @@ #-*- coding: utf-8 -*- -import sys -import os.path -import re -import socket -import inspect -import copy -from lodel.context import LodelContext -LodelContext.expose_modules(globals(), { - 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'FieldValidationError'], - 'lodel.validator.validator': ['Validator', 'ValidationError']}) - -## @package lodel.settings.validator Lodel2 settings validators/cast module -# -# Validator are registered in the SettingValidator class. -# @note to get a list of registered default validators just run -#
$ python scripts/settings_validator.py
- -##@brief Exception class that should be raised when a validation fails -class SettingValidationError(ValidationError): - pass - -##@brief Handles settings validators -# -# Class instance are callable objects that takes a value argument (the value to validate). It raises -# a SettingsValidationError if validation fails, else it returns a properly -# casted value. -#@todo implement an IP validator and use it in multisite confspec -class SettingValidator(Validator): - - ##@brief Instanciate a validator - #@param name str : validator name - #@param none_is_valid bool : if True None will be validated - #@param **kwargs : more arguement for the validator - def __init__(self, name, none_is_valid = False, **kwargs): - super().__init__(name, none_is_valid = False, **kwargs) - - ##@brief Call the validator - # @param value * - # @return properly casted value - # @throw SettingsValidationError - def __call__(self, value): - super().__call__(value) - -##@brief Validator for Editorial model component -# -# Designed to validate a conf that indicate a class.field in an EM -#@todo modified the hardcoded dyncode import (it's a warning) -def emfield_val(value): - LodelContext.expose_modules(globals(), { - 'lodel.plugin.hooks': ['LodelHook']}) - spl = value.split('.') - if len(spl) != 2: - msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s" - raise SettingsValidationError(msg % value) - value = tuple(spl) - #Late validation hook - @LodelHook('lodel2_dyncode_bootstraped') - def emfield_conf_check(hookname, caller, payload): - import leapi_dyncode as dyncode # <-- dirty & quick - classnames = { cls.__name__.lower():cls for cls in dyncode.dynclasses} - if value[0].lower() not in classnames: - msg = "Following dynamic class do not exists in current EM : %s" - raise SettingsValidationError(msg % value[0]) - ccls = classnames[value[0].lower()] - if value[1].lower() not in ccls.fieldnames(True): - msg = "Following field not found in class %s : %s" - raise SettingsValidationError(msg % value) - return value - -##@brief Validator for plugin name & optionnaly type -# -#Able to check that the value is a plugin and if it is of a specific type -def plugin_validator(value, ptype = None): - LodelContext.expose_modules(globals(), { - 'lodel.plugin.hooks': ['LodelHook']}) - value = copy.copy(value) - @LodelHook('lodel2_dyncode_bootstraped') - def plugin_type_checker(hookname, caller, payload): - LodelContext.expose_modules(globals(), { - 'lodel.plugin.plugins': ['Plugin'], - 'lodel.plugin.exceptions': ['PluginError']}) - if value is None: - return - try: - plugin = Plugin.get(value) - except PluginError: - msg = "No plugin named %s found" - msg %= value - raise SettingsValidationError(msg) - if plugin._type_conf_name.lower() != ptype.lower(): - msg = "A plugin of type '%s' was expected but found a plugin \ -named '%s' that is a '%s' plugin" - msg %= (ptype, value, plugin._type_conf_name) - raise SettingsValidationError(msg) - return value - - - -SettingValidator.register_validator( - 'plugin', - plugin_validator, - 'plugin name & type validator') - -SettingValidator.register_validator( - 'emfield', - emfield_val, - 'EmField name validator') - -# -# Lodel 2 configuration specification -# - -##@brief Append a piece of confspec -#@note orig is modified during the process -#@param orig dict : the confspec to update -#@param section str : section name -#@param key str -#@param validator SettingValidator : the validator to use to check this configuration key's value -#@param default -#@return new confspec -def confspec_append(orig, section, key, validator, default): - if section not in orig: - orig[section] = dict() - if key not in orig[section]: - orig[section][key] = (default, validator) - return orig - -##@brief Global specifications for lodel2 settings -LODEL2_CONF_SPECS = { - 'lodel2': { - 'debug': ( True, - SettingValidator('bool')), - 'sitename': ( 'noname', - SettingValidator('strip')), - 'runtest': ( False, - SettingValidator('bool')), - }, - 'lodel2.logging.*' : { - 'level': ( 'ERROR', - SettingValidator('loglevel')), - 'context': ( False, - SettingValidator('bool')), - 'filename': ( "-", - SettingValidator('errfile', none_is_valid = False)), - 'backupcount': ( 5, - SettingValidator('int', none_is_valid = False)), - 'maxbytes': ( 1024*10, - SettingValidator('int', none_is_valid = False)), - }, - 'lodel2.editorialmodel': { - 'emfile': ( 'em.pickle', SettingValidator('strip')), - 'emtranslator': ( 'picklefile', SettingValidator('strip')), - 'dyncode': ( 'leapi_dyncode.py', SettingValidator('strip')), - 'groups': ( '', SettingValidator('list')), - 'editormode': ( False, SettingValidator('bool')), - }, - 'lodel2.datasources.*': { - 'read_only': (False, SettingValidator('bool')), - 'identifier': ( None, SettingValidator('string')), - }, - 'lodel2.auth': { - 'login_classfield': ('user.login', SettingValidator('emfield')), - 'pass_classfield': ('user.password', SettingValidator('emfield')), - }, -} diff --git a/lodel/validator/validator.py b/lodel/validator/validator.py index 6272ed8..301eab3 100644 --- a/lodel/validator/validator.py +++ b/lodel/validator/validator.py @@ -341,3 +341,124 @@ Validator.create_re_validator( r'^https?://[^\./]+.[^\./]+/?.*$', 'http_url', 'Url validator') +##@brief Validator for Editorial model component +# +# Designed to validate a conf that indicate a class.field in an EM +#@todo modified the hardcoded dyncode import (it's a warning) +def emfield_val(value): + LodelContext.expose_modules(globals(), { + 'lodel.plugin.hooks': ['LodelHook']}) + spl = value.split('.') + if len(spl) != 2: + msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s" + raise SettingsValidationError(msg % value) + value = tuple(spl) + #Late validation hook + @LodelHook('lodel2_dyncode_bootstraped') + def emfield_conf_check(hookname, caller, payload): + import leapi_dyncode as dyncode # <-- dirty & quick + classnames = { cls.__name__.lower():cls for cls in dyncode.dynclasses} + if value[0].lower() not in classnames: + msg = "Following dynamic class do not exists in current EM : %s" + raise SettingsValidationError(msg % value[0]) + ccls = classnames[value[0].lower()] + if value[1].lower() not in ccls.fieldnames(True): + msg = "Following field not found in class %s : %s" + raise SettingsValidationError(msg % value) + return value + +##@brief Validator for plugin name & optionnaly type +# +#Able to check that the value is a plugin and if it is of a specific type +def plugin_validator(value, ptype = None): + LodelContext.expose_modules(globals(), { + 'lodel.plugin.hooks': ['LodelHook']}) + value = copy.copy(value) + @LodelHook('lodel2_dyncode_bootstraped') + def plugin_type_checker(hookname, caller, payload): + LodelContext.expose_modules(globals(), { + 'lodel.plugin.plugins': ['Plugin'], + 'lodel.plugin.exceptions': ['PluginError']}) + if value is None: + return + try: + plugin = Plugin.get(value) + except PluginError: + msg = "No plugin named %s found" + msg %= value + raise ValidationError(msg) + if plugin._type_conf_name.lower() != ptype.lower(): + msg = "A plugin of type '%s' was expected but found a plugin \ +named '%s' that is a '%s' plugin" + msg %= (ptype, value, plugin._type_conf_name) + raise ValidationError(msg) + return value + + +Validator.register_validator( + 'plugin', + plugin_validator, + 'plugin name & type validator') + +Validator.register_validator( + 'emfield', + emfield_val, + 'EmField name validator') + +# +# Lodel 2 configuration specification +# + +##@brief Append a piece of confspec +#@note orig is modified during the process +#@param orig dict : the confspec to update +#@param section str : section name +#@param key str +#@param validator Validator : the validator to use to check this configuration key's value +#@param default +#@return new confspec +def confspec_append(orig, section, key, validator, default): + if section not in orig: + orig[section] = dict() + if key not in orig[section]: + orig[section][key] = (default, validator) + return orig + +##@brief Global specifications for lodel2 settings +LODEL2_CONF_SPECS = { + 'lodel2': { + 'debug': ( True, + Validator('bool')), + 'sitename': ( 'noname', + Validator('strip')), + 'runtest': ( False, + Validator('bool')), + }, + 'lodel2.logging.*' : { + 'level': ( 'ERROR', + Validator('loglevel')), + 'context': ( False, + Validator('bool')), + 'filename': ( "-", + Validator('errfile', none_is_valid = False)), + 'backupcount': ( 5, + Validator('int', none_is_valid = False)), + 'maxbytes': ( 1024*10, + Validator('int', none_is_valid = False)), + }, + 'lodel2.editorialmodel': { + 'emfile': ( 'em.pickle', Validator('strip')), + 'emtranslator': ( 'picklefile', Validator('strip')), + 'dyncode': ( 'leapi_dyncode.py', Validator('strip')), + 'groups': ( '', Validator('list')), + 'editormode': ( False, Validator('bool')), + }, + 'lodel2.datasources.*': { + 'read_only': (False, Validator('bool')), + 'identifier': ( None, Validator('string')), + }, + 'lodel2.auth': { + 'login_classfield': ('user.login', Validator('emfield')), + 'pass_classfield': ('user.password', Validator('emfield')), + }, +} \ No newline at end of file diff --git a/scripts/settings_validator.py b/scripts/settings_validator.py index a810cb2..dde5685 100644 --- a/scripts/settings_validator.py +++ b/scripts/settings_validator.py @@ -2,5 +2,5 @@ import sys import os, os.path sys.path.append(os.path.dirname(os.getcwd()+'/..')) -from lodel.settings.validator import SettingValidator -print(SettingValidator.validators_list_str()) +from lodel.validator.validator import Validator +print(Validator.validators_list_str()) diff --git a/tests/settings/test_validator.py b/tests/settings/test_validator.py index d24a8b1..fc283a2 100644 --- a/tests/settings/test_validator.py +++ b/tests/settings/test_validator.py @@ -5,52 +5,52 @@ from unittest import mock from unittest.mock import patch from lodel.exceptions import * -from lodel.settings.validator import * +from lodel.validator.validator import * -class SettingValidatorTestCase(unittest.TestCase): +class ValidatorTestCase(unittest.TestCase): def test_init_basic(self): """ Testing the SettingsValidator class instanciation""" - valid = SettingValidator('string') + valid = Validator('string') #trying to call it valid('test') def test_init_badname(self): - """ Testing SettingValidator instanciation with non existing validator + """ Testing Validator instanciation with non existing validator name""" with self.assertRaises(LodelFatalError): - SettingValidator('qklfhsdufgsdyfugigsdfsdlcknsdp') + Validator('qklfhsdufgsdyfugigsdfsdlcknsdp') def test_noneswitch(self): """ Testing the none_is_valid switch given at validator instanciation """ - none_invalid = SettingValidator('int') - none_valid = SettingValidator('int', none_is_valid = True) + none_invalid = Validator('int') + none_valid = Validator('int', none_is_valid = True) none_valid(None) - with self.assertRaises(SettingsValidationError): + with self.assertRaises(ValidationError): none_invalid(None) def test_validator_registration(self): - """ Testing the register_validator method of SettingValidator """ + """ Testing the register_validator method of Validator """ mockfun = mock.MagicMock() vname = 'lfkjdshfkuhsdygsuuyfsduyf' testval = 'foo' - SettingValidator.register_validator(vname, mockfun, 'test validator') + Validator.register_validator(vname, mockfun, 'test validator') #Using registered validator - valid = SettingValidator(vname) + valid = Validator(vname) valid(testval) mockfun.assert_called_once_with(testval) def test_validator_optargs_forwarding(self): - """ Testing the ability for SettingValidator to forward optional + """ Testing the ability for Validator to forward optional arguments """ mockfun = mock.MagicMock() vname = 'lkjdsfhsdiufhisduguig' testval = 'azertyuiop' - SettingValidator.register_validator(vname, mockfun, 'test validator') + Validator.register_validator(vname, mockfun, 'test validator') #Using registered validator with more arguments - valid = SettingValidator(vname, + valid = Validator(vname, arga = 'a', argb = 42, argc = '1337') valid(testval) mockfun.assert_called_once_with( From 530ad1088fbc6b013cb0855d4609be1215e42e0a Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 11:13:34 +0100 Subject: [PATCH 13/35] Added inheritance of the datahandler base class and the datahandler option class to the MlNamedObject class --- lodel/leapi/datahandlers/base_classes.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 74ae038..2745c48 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -15,13 +15,14 @@ from .exceptions import LodelDataHandlerNotAllowedOptionException LodelContext.expose_modules(globals(), { 'lodel.exceptions': ['LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError'], + 'lodel.mlnamedobject':['MlNamedObject'], 'lodel.leapi.datahandlers.exceptions': ['LodelDataHandlerConsistencyException', 'LodelDataHandlerException'], 'lodel.logger': 'logger'}) ##@brief Base class for all data handlers #@ingroup lodel2_datahandlers -class DataHandler(object): +class DataHandler(MlNamedObject): base_type = "type" _HANDLERS_MODULES = ('datas_base', 'datas', 'references') ##@brief Stores the DataHandler childs classes indexed by name @@ -30,8 +31,8 @@ class DataHandler(object): # @todo do it ! (like plugins, register handlers... blablabla) __custom_handlers = dict() - help_text = 'Generic Field Data Handler' - display_name = "Generic Field" + #help_text = 'Generic Field Data Handler' + #display_name = "Generic Field" options_spec = dict() options_values = dict() @@ -509,7 +510,7 @@ class DatasConstructor(object): ## @brief Class designed to handle an option of a DataHandler -class DatahandlerOption(object): +class DatahandlerOption(MlNamedObject): ## @brief instanciates a new Datahandler option object # From dd1b1d0b1374f145cfbd12763a0fcf4cb0341f3a Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 11:17:14 +0100 Subject: [PATCH 14/35] Fix on the call to the parent's init in DataHandler and DataHandlerOption classes --- lodel/leapi/datahandlers/base_classes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 2745c48..6490d7c 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -31,8 +31,8 @@ class DataHandler(MlNamedObject): # @todo do it ! (like plugins, register handlers... blablabla) __custom_handlers = dict() - #help_text = 'Generic Field Data Handler' - #display_name = "Generic Field" + help_text = 'Generic Field Data Handler' + display_name = "Generic Field" options_spec = dict() options_values = dict() @@ -63,6 +63,7 @@ class DataHandler(MlNamedObject): for argname, argval in kwargs.items(): setattr(self, argname, argval) self.check_options() + super().__init__(self.display_name, self.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 @@ -523,7 +524,7 @@ class DatahandlerOption(MlNamedObject): self.__display_name = display_name self.__help_text = help_text self.__validator = validator - + super().__init__(self.__display_name, self.__help_text) @property def id(self): return self.__id From 0716e77a1ae5819c0b8d17d491517f5d5f14d02d Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 11:18:08 +0100 Subject: [PATCH 15/35] Code cleaning --- lodel/leapi/datahandlers/base_classes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 6490d7c..86cba94 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -525,6 +525,7 @@ class DatahandlerOption(MlNamedObject): self.__help_text = help_text self.__validator = validator super().__init__(self.__display_name, self.__help_text) + @property def id(self): return self.__id From 18770976cf17777ff138550c8fdbdb5a9f6de2c6 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 13:59:46 +0100 Subject: [PATCH 16/35] Changed the importing of the UnallowedOption Exception --- lodel/leapi/datahandlers/base_classes.py | 16 ++++++++++++---- lodel/leapi/datahandlers/exceptions.py | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 86cba94..7cc1da9 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -10,13 +10,21 @@ import inspect import warnings from lodel.context import LodelContext -from .exceptions import LodelDataHandlerNotAllowedOptionException LodelContext.expose_modules(globals(), { - 'lodel.exceptions': ['LodelException', 'LodelExceptions', - 'LodelFatalError', 'DataNoneValid', 'FieldValidationError'], + 'lodel.exceptions': [ + 'LodelException', + 'LodelExceptions', + 'LodelFatalError', + 'DataNoneValid', + 'FieldValidationError' + ], 'lodel.mlnamedobject':['MlNamedObject'], - 'lodel.leapi.datahandlers.exceptions': ['LodelDataHandlerConsistencyException', 'LodelDataHandlerException'], + 'lodel.leapi.datahandlers.exceptions': [ + 'LodelDataHandlerConsistencyException', + 'LodelDataHandlerException', + 'LodelDataHandlerNotAllowedOptionException' + ], 'lodel.logger': 'logger'}) diff --git a/lodel/leapi/datahandlers/exceptions.py b/lodel/leapi/datahandlers/exceptions.py index 724006b..ceb32f2 100644 --- a/lodel/leapi/datahandlers/exceptions.py +++ b/lodel/leapi/datahandlers/exceptions.py @@ -1,8 +1,10 @@ def LodelDataHandlerException(Exception): pass + def LodelDataHandlerConsistencyException(LodelDataHandlerException): pass + def LodelDataHandlerNotAllowedOptionException(LodelDataHandlerException): pass \ No newline at end of file From 656932fe6869794ba65bbc5e172c32ef238ebd34 Mon Sep 17 00:00:00 2001 From: prieto Date: Fri, 3 Feb 2017 14:34:53 +0100 Subject: [PATCH 17/35] Removes settings/validator.py --- lodel/settings/validator.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 lodel/settings/validator.py diff --git a/lodel/settings/validator.py b/lodel/settings/validator.py deleted file mode 100644 index 652f2c0..0000000 --- a/lodel/settings/validator.py +++ /dev/null @@ -1,3 +0,0 @@ -#-*- coding: utf-8 -*- - - From ebe8338938001c2d64490e4dbb825b6bda216f9b Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 14:41:54 +0100 Subject: [PATCH 18/35] Changed the management of the option values in a datahandler --- lodel/leapi/datahandlers/base_classes.py | 14 +++++++------- lodel/leapi/datahandlers/exceptions.py | 4 ---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 7cc1da9..4202f19 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -22,8 +22,7 @@ LodelContext.expose_modules(globals(), { 'lodel.mlnamedobject':['MlNamedObject'], 'lodel.leapi.datahandlers.exceptions': [ 'LodelDataHandlerConsistencyException', - 'LodelDataHandlerException', - 'LodelDataHandlerNotAllowedOptionException' + 'LodelDataHandlerException' ], 'lodel.logger': 'logger'}) @@ -76,12 +75,13 @@ class DataHandler(MlNamedObject): ## @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_value in self.options_values.items(): - # TODO Change the call to self.options_spec if the specifications are passed as tuple - if option_name in self.options_spec: - self.options_values[option_name] = self.options_spec[option_name].check_value(option_value) + 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 + self.options_values[option_name] = option_datas[1].check_value(self.options_values[option_name]) else: - raise LodelDataHandlerNotAllowedOptionException() + # This option was not configured, we get the default value from the specs + self.options_values[option_name] = option_datas[0] @property def options(self): diff --git a/lodel/leapi/datahandlers/exceptions.py b/lodel/leapi/datahandlers/exceptions.py index ceb32f2..bef899a 100644 --- a/lodel/leapi/datahandlers/exceptions.py +++ b/lodel/leapi/datahandlers/exceptions.py @@ -4,7 +4,3 @@ def LodelDataHandlerException(Exception): def LodelDataHandlerConsistencyException(LodelDataHandlerException): pass - - -def LodelDataHandlerNotAllowedOptionException(LodelDataHandlerException): - pass \ No newline at end of file From 57b650c6866b334e9321d25f7e14b8da06aea177 Mon Sep 17 00:00:00 2001 From: prieto Date: Fri, 3 Feb 2017 15:37:41 +0100 Subject: [PATCH 19/35] display_name and helpt_text are removed in datahandleroption --- lodel/leapi/datahandlers/base_classes.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 4202f19..05b6116 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -19,7 +19,7 @@ LodelContext.expose_modules(globals(), { 'DataNoneValid', 'FieldValidationError' ], - 'lodel.mlnamedobject':['MlNamedObject'], + 'lodel.mlnamedobject.mlnamedobject':['MlNamedObject'], 'lodel.leapi.datahandlers.exceptions': [ 'LodelDataHandlerConsistencyException', 'LodelDataHandlerException' @@ -529,15 +529,9 @@ class DatahandlerOption(MlNamedObject): # @param validator function def __init__(self, id, display_name, help_text, validator): self.__id = id - self.__display_name = display_name - self.__help_text = help_text self.__validator = validator super().__init__(self.__display_name, self.__help_text) - @property - def id(self): - return self.__id - @property def display_name(self): return self.__display_name From be429fc018343600e0f7e55c61249808fa4ad71c Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 3 Feb 2017 15:37:33 +0100 Subject: [PATCH 20/35] Changed help_text and display_name from private to public in the datahandlers options --- lodel/leapi/datahandlers/base_classes.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 05b6116..1a2c2c8 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -83,10 +83,6 @@ class DataHandler(MlNamedObject): # This option was not configured, we get the default value from the specs self.options_values[option_name] = option_datas[0] - @property - def options(self): - return self.options_values - ## Fieldtype name @classmethod def name(cls): @@ -530,15 +526,11 @@ class DatahandlerOption(MlNamedObject): def __init__(self, id, display_name, help_text, validator): self.__id = id self.__validator = validator - super().__init__(self.__display_name, self.__help_text) + super().__init__(display_name, help_text) @property - def display_name(self): - return self.__display_name - - @property - def help_text(self): - return self.__help_text + def id(self): + return self.__id ## @brief checks a value corresponding to this option is valid # @param value From 283fa4a3cc5dd933d405f690a5e364c8d8cf0284 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 9 Feb 2017 13:57:05 +0100 Subject: [PATCH 21/35] Added an Exception management for the DatahandlerOption's check_value method --- lodel/leapi/datahandlers/base_classes.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 1a2c2c8..d2a2227 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -24,6 +24,9 @@ LodelContext.expose_modules(globals(), { 'LodelDataHandlerConsistencyException', 'LodelDataHandlerException' ], + 'lodel.validator.validator': [ + 'ValidationError' + ], 'lodel.logger': 'logger'}) @@ -78,7 +81,10 @@ class DataHandler(MlNamedObject): 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 - self.options_values[option_name] = option_datas[1].check_value(self.options_values[option_name]) + 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 has not been validated else: # This option was not configured, we get the default value from the specs self.options_values[option_name] = option_datas[0] @@ -536,4 +542,7 @@ class DatahandlerOption(MlNamedObject): # @param value # @return casted value def check_value(self, value): - return self.__validator(value) + try: + return self.__validator(value) + except ValidationError: + raise ValueError() From 822ad1bea2db2e5994143895c8efcf82d77a3051 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 10 Feb 2017 09:48:18 +0100 Subject: [PATCH 22/35] Code cleaning --- lodel/leapi/datahandlers/base_classes.py | 277 ++++++++++++----------- 1 file changed, 142 insertions(+), 135 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index d2a2227..993308d 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -19,7 +19,7 @@ LodelContext.expose_modules(globals(), { 'DataNoneValid', 'FieldValidationError' ], - 'lodel.mlnamedobject.mlnamedobject':['MlNamedObject'], + 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.leapi.datahandlers.exceptions': [ 'LodelDataHandlerConsistencyException', 'LodelDataHandlerException' @@ -30,14 +30,14 @@ LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger'}) -##@brief Base class for all data handlers -#@ingroup lodel2_datahandlers +## @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() @@ -46,15 +46,16 @@ class DataHandler(MlNamedObject): 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: @@ -69,22 +70,24 @@ class DataHandler(MlNamedObject): 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() super().__init__(self.display_name, self.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 + # @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]) + 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 has not been validated + 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] @@ -105,15 +108,15 @@ class DataHandler(MlNamedObject): 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: @@ -122,9 +125,9 @@ class DataHandler(MlNamedObject): 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) @@ -134,7 +137,7 @@ class DataHandler(MlNamedObject): 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): @@ -142,17 +145,17 @@ class DataHandler(MlNamedObject): 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 @@ -167,41 +170,41 @@ class DataHandler(MlNamedObject): 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 @@ -210,7 +213,7 @@ class DataHandler(MlNamedObject): 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): @@ -219,7 +222,7 @@ class DataHandler(MlNamedObject): 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: @@ -232,10 +235,11 @@ class DataHandler(MlNamedObject): 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() @@ -245,7 +249,7 @@ class DataHandler(MlNamedObject): 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 Return the module name to import in order to use the datahandler # @param data_handler_name str : Data handler name # @return a str @classmethod @@ -253,62 +257,64 @@ class DataHandler(MlNamedObject): 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: @@ -319,15 +325,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) @@ -342,13 +348,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): @@ -364,21 +370,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): @@ -387,18 +393,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) @@ -408,30 +414,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__'): @@ -451,11 +457,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() @@ -463,18 +469,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. # @@ -488,15 +495,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 From 915817b6bdbfa93ca1b2f5274736afc711ff0883 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 10 Feb 2017 09:55:28 +0100 Subject: [PATCH 23/35] Code cleaning on datahandlers/datas.py --- lodel/leapi/datahandlers/datas.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) 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 From c48c0119a2ae97c53bff6cfc311957b0c9b25b2b Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 10 Feb 2017 10:39:31 +0100 Subject: [PATCH 24/35] code cleaning --- lodel/leapi/datahandlers/datas_base.py | 72 ++++++++++++++------------ 1 file changed, 38 insertions(+), 34 deletions(-) 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) - From b6093866cd0d2a00d9bf673e191e8e49054c7a5a Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 10 Feb 2017 11:12:44 +0100 Subject: [PATCH 25/35] Changed datahandler exceptions from functions to classes --- lodel/leapi/datahandlers/exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lodel/leapi/datahandlers/exceptions.py b/lodel/leapi/datahandlers/exceptions.py index bef899a..0cdd266 100644 --- a/lodel/leapi/datahandlers/exceptions.py +++ b/lodel/leapi/datahandlers/exceptions.py @@ -1,6 +1,6 @@ -def LodelDataHandlerException(Exception): +class LodelDataHandlerException(Exception): pass -def LodelDataHandlerConsistencyException(LodelDataHandlerException): +class LodelDataHandlerConsistencyException(LodelDataHandlerException): pass From dc3a8f4e22af61d3a4e13ebcf40a70d60e22f320 Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Fri, 10 Feb 2017 11:23:08 +0100 Subject: [PATCH 26/35] Code cleaning on references module --- lodel/leapi/datahandlers/references.py | 73 ++++++++++++++------------ 1 file changed, 39 insertions(+), 34 deletions(-) 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)): From 2a3b157a3b4a6e7529573729ad7dfe5cadeb755a Mon Sep 17 00:00:00 2001 From: Roland Haroutiounian Date: Thu, 16 Feb 2017 08:33:35 +0100 Subject: [PATCH 27/35] Correcting a syntax error in EmClass --- lodel/editorial_model/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index 27bf911..e3f3a3d 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -249,7 +249,7 @@ class EmField(EmComponent): ##@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): From b6b0a60b4c6283f8cc769b44132d06d2b2be7af2 Mon Sep 17 00:00:00 2001 From: prieto Date: Fri, 3 Feb 2017 15:57:00 +0100 Subject: [PATCH 28/35] display_name and help_text are handled in __init__ of datahandler base class --- lodel/leapi/datahandlers/base_classes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 993308d..4fce7e8 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -27,7 +27,8 @@ LodelContext.expose_modules(globals(), { 'lodel.validator.validator': [ 'ValidationError' ], - 'lodel.logger': 'logger'}) + 'lodel.logger': 'logger', + 'lodel.utils.mlstring': ['MlString']}) ## @brief Base class for all data handlers @@ -74,7 +75,10 @@ class DataHandler(MlNamedObject): for argname, argval in kwargs.items(): setattr(self, argname, argval) self.check_options() - super().__init__(self.display_name, self.help_text) + + 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 @@ -376,7 +380,7 @@ class Reference(DataHandler): # 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 @@ -445,7 +449,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: From 1fb73c1e7f905a98d257ef5ae6633c46135751e4 Mon Sep 17 00:00:00 2001 From: prieto Date: Thu, 9 Feb 2017 14:51:10 +0100 Subject: [PATCH 29/35] Small adaptation --- lodel/plugins/webui/templates/listing/issue.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 %}