mirror of
https://github.com/yweber/lodel2.git
synced 2025-10-30 11:09:03 +01:00
Validator documentation
This commit is contained in:
parent
46479ab170
commit
ab03bbf7c8
1 changed files with 111 additions and 93 deletions
|
|
@ -6,42 +6,50 @@ import re
|
|||
import socket
|
||||
import inspect
|
||||
import copy
|
||||
|
||||
from lodel.context import LodelContext
|
||||
|
||||
|
||||
LodelContext.expose_modules(globals(), {
|
||||
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
||||
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
||||
'LodelFatalError', 'FieldValidationError']})
|
||||
|
||||
# @package lodel.settings.validator Lodel2 settings validators/cast module
|
||||
|
||||
##
|
||||
# @package lodel.settings.validator Lodel2 settings validators/cast module.
|
||||
#
|
||||
# Validator are registered in the Validator class.
|
||||
# @note to get a list of registered default validators just run
|
||||
# <pre>$ python scripts/settings_validator.py</pre>
|
||||
#
|
||||
# @remarks Should we reconsider specifying conf right in this module?
|
||||
|
||||
|
||||
##
|
||||
# @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 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 cast value.
|
||||
#
|
||||
#@todo Implement an IP validator for use in the multisite confspec
|
||||
class Validator(MlNamedObject):
|
||||
|
||||
_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
|
||||
##
|
||||
# @brief Instantiate a validator
|
||||
#
|
||||
#@param name str: validator name
|
||||
#@param none_is_valid bool: if True None will be validated
|
||||
#@param **kwargs: more arguments for the validator
|
||||
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)
|
||||
|
|
@ -52,9 +60,11 @@ class Validator(MlNamedObject):
|
|||
display_name = name
|
||||
super().__init__(display_name, help_text)
|
||||
|
||||
# @brief Call the validator
|
||||
# @param value *
|
||||
# @return properly casted value
|
||||
##
|
||||
# @brief Calls the validator.
|
||||
#
|
||||
# @param value mixed:
|
||||
# @return mixed: The properly casted value
|
||||
# @throw ValidationError
|
||||
def __call__(self, value):
|
||||
if value is None:
|
||||
|
|
@ -70,31 +80,38 @@ class Validator(MlNamedObject):
|
|||
except Exception as exp:
|
||||
raise ValidationError(exp)
|
||||
|
||||
##
|
||||
# @brief Register a new validator
|
||||
# @param name str : validator name
|
||||
# @param callback callable : the function that will validate a value
|
||||
# @param description str
|
||||
#
|
||||
# @param name string: validator name
|
||||
# @param callback callable: the function that will validate a value
|
||||
# @param description string:
|
||||
@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
|
||||
##
|
||||
# @todo 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
|
||||
##
|
||||
# @brief Creates and registers an iterative list validator
|
||||
#
|
||||
# @param elt_validator callable: The validator that will be used to validate
|
||||
# each of the list values.
|
||||
# @param validator_name string:
|
||||
# @param description None | string:
|
||||
# @param separator string: The element separator.
|
||||
# @return A Validator instance.
|
||||
@classmethod
|
||||
def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
||||
def list_validator(value):
|
||||
|
|
@ -111,11 +128,14 @@ class Validator(MlNamedObject):
|
|||
cls.register_validator(validator_name, list_validator, description)
|
||||
return cls(validator_name)
|
||||
|
||||
# @brief Create and register a list validator which reads an array and returns a string
|
||||
# @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
|
||||
##
|
||||
# @brief Creates and registers a list validator that reads an array
|
||||
# and returns a string
|
||||
# @param elt_validator callable: The validator that will be used to validate
|
||||
# each elt value
|
||||
# @param validator_name string:
|
||||
# @param description None | string:
|
||||
# @param separator string: The element separator
|
||||
# @return A Validator instance
|
||||
@classmethod
|
||||
def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
||||
|
|
@ -128,10 +148,12 @@ class Validator(MlNamedObject):
|
|||
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
|
||||
#
|
||||
# @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):
|
||||
|
|
@ -147,7 +169,8 @@ class Validator(MlNamedObject):
|
|||
if description is None else description)
|
||||
return cls(validator_name)
|
||||
|
||||
# @return a list of registered validators
|
||||
##
|
||||
# @return The list of registered validators.
|
||||
@classmethod
|
||||
def validators_list_str(cls):
|
||||
result = ''
|
||||
|
|
@ -158,16 +181,20 @@ class Validator(MlNamedObject):
|
|||
result += "\n"
|
||||
return result
|
||||
|
||||
|
||||
##
|
||||
# @brief Integer value validator callback
|
||||
|
||||
|
||||
#
|
||||
# @remarks Is it intended that this function simply tries casting to an integer?
|
||||
# Is it also intended that it does not use any try/catch block?
|
||||
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)
|
||||
|
|
@ -175,9 +202,9 @@ def file_err_output(value):
|
|||
return None
|
||||
return value
|
||||
|
||||
# @brief Boolean value validator callback
|
||||
|
||||
|
||||
##
|
||||
# @brief Boolean validator callback
|
||||
def boolean_val(value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
|
@ -189,18 +216,18 @@ def boolean_val(value):
|
|||
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("Following path don't exists or is not a directory : '%s'" % res)
|
||||
return res
|
||||
|
||||
# @brief Validate a loglevel value
|
||||
|
||||
|
||||
##
|
||||
# @brief Validates a loglevel value
|
||||
def loglevel_val(value):
|
||||
valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
|
||||
if value.upper() not in valids:
|
||||
|
|
@ -208,44 +235,49 @@ def loglevel_val(value):
|
|||
"The value '%s' is not a valid loglevel" % value)
|
||||
return value.upper()
|
||||
|
||||
# @brief Validate a path
|
||||
|
||||
|
||||
##
|
||||
# @brief Validates a path
|
||||
#
|
||||
# @remarks is it intended to have both @ref directory_val and this function
|
||||
# right here?
|
||||
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
|
||||
|
||||
|
||||
##
|
||||
# @brief Validates None
|
||||
#
|
||||
# @remarks Purpose?
|
||||
def none_val(value):
|
||||
if value is None:
|
||||
return None
|
||||
raise ValidationError("This settings cannot be set in configuration file")
|
||||
|
||||
# @brief Validate a string
|
||||
|
||||
|
||||
##
|
||||
# @brief Validates a string
|
||||
def str_val(value):
|
||||
try:
|
||||
return str(value)
|
||||
except Exception as exp:
|
||||
raise ValidationError("Can't to convert value to string: " + str(exp))
|
||||
|
||||
|
||||
##
|
||||
# @brief Validate using a regex
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -275,67 +307,52 @@ def custom_list_validator(value, validator_name, validator_kwargs=None):
|
|||
validator(item)
|
||||
return value.split()
|
||||
|
||||
|
||||
#
|
||||
# Default validators registration
|
||||
#
|
||||
|
||||
Validator.register_validator('custom_list', custom_list_validator,
|
||||
'A list validator that takes a "validator_name" as argument')
|
||||
|
||||
Validator.register_validator('dummy', lambda value: value, 'Validate anything')
|
||||
|
||||
Validator.register_validator('none', none_val, 'Validate None')
|
||||
|
||||
Validator.register_validator('string', str_val, 'Validate string values')
|
||||
|
||||
Validator.register_validator('strip', str.strip, 'String trim')
|
||||
|
||||
Validator.register_validator('int', int_val, 'Integer value validator')
|
||||
|
||||
Validator.register_validator('bool', boolean_val, 'Boolean value validator')
|
||||
|
||||
Validator.register_validator('errfile', file_err_output,
|
||||
'Error output file validator (return stderr if filename is "-")')
|
||||
|
||||
Validator.register_validator('directory', directory_val,
|
||||
'Directory path validator')
|
||||
|
||||
Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator')
|
||||
|
||||
Validator.register_validator('path', path_val, 'path validator')
|
||||
|
||||
Validator.register_validator('host', host_val, 'host validator')
|
||||
|
||||
Validator.register_validator('regex', regex_val,
|
||||
'RegEx name validator (take re as argument)')
|
||||
|
||||
Validator.create_list_validator('list', Validator('strip'), description="Simple list validator. Validate a list of values separated by ','",
|
||||
separator=',')
|
||||
|
||||
Validator.create_list_validator(
|
||||
'directory_list',
|
||||
Validator('directory'),
|
||||
description="Validator for a list of directory path separated with ','",
|
||||
separator=',')
|
||||
|
||||
Validator.create_write_list_validator(
|
||||
'write_list',
|
||||
Validator('directory'),
|
||||
description="Validator for an array of values \
|
||||
which will be set in a string, separated by ','",
|
||||
separator=',')
|
||||
|
||||
Validator.create_re_validator(
|
||||
r'^https?://[^\./]+.[^\./]+/?.*$',
|
||||
'http_url',
|
||||
'Url validator')
|
||||
|
||||
# @brief Validator for Editorial model component
|
||||
|
||||
##
|
||||
# @brief Validator for Editorial Model components.
|
||||
#
|
||||
# Designed to validate a conf that indicate a class.field in an EM
|
||||
#@todo modified the hardcoded dyncode import (it's a warning)
|
||||
|
||||
|
||||
# Designed to validate a conf that indicates a class.field in an EM
|
||||
#
|
||||
# @todo modify the hardcoded dyncode import (it's a warning)
|
||||
def emfield_val(value):
|
||||
LodelContext.expose_modules(globals(),
|
||||
{'lodel.plugin.hooks': ['LodelHook']})
|
||||
|
|
@ -359,11 +376,11 @@ def emfield_val(value):
|
|||
raise ValidationError(msg % value)
|
||||
return value
|
||||
|
||||
# @brief Validator for plugin name & optionnaly type
|
||||
|
||||
##
|
||||
# @brief Validator for plugin name & its type optionally
|
||||
#
|
||||
# Able to check that the value is a plugin and if it is of a specific type
|
||||
|
||||
|
||||
def plugin_validator(value, ptype=None):
|
||||
if value:
|
||||
LodelContext.expose_modules(globals(), {
|
||||
|
|
@ -396,26 +413,25 @@ 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
|
||||
|
||||
|
||||
#
|
||||
# @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
|
||||
#
|
||||
# @note orig is modified during the process
|
||||
def confspec_append(orig, section, key, validator, default):
|
||||
if section not in orig:
|
||||
orig[section] = dict()
|
||||
|
|
@ -423,6 +439,8 @@ def confspec_append(orig, section, key, validator, default):
|
|||
orig[section][key] = (default, validator)
|
||||
return orig
|
||||
|
||||
|
||||
##
|
||||
# @brief Global specifications for lodel2 settings
|
||||
LODEL2_CONF_SPECS = {
|
||||
'lodel2': {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue