mirror of
				https://github.com/yweber/lodel2.git
				synced 2025-10-31 03:29: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 socket | ||||||
| import inspect | import inspect | ||||||
| import copy | import copy | ||||||
| 
 |  | ||||||
| from lodel.context import LodelContext | from lodel.context import LodelContext | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| LodelContext.expose_modules(globals(), { | LodelContext.expose_modules(globals(), { | ||||||
|     'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], |     'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], | ||||||
|     'lodel.exceptions': ['LodelException', 'LodelExceptions', |     'lodel.exceptions': ['LodelException', 'LodelExceptions', | ||||||
|                          'LodelFatalError', 'FieldValidationError']}) |                          'LodelFatalError', 'FieldValidationError']}) | ||||||
| 
 | 
 | ||||||
| # @package lodel.settings.validator Lodel2 settings validators/cast module | 
 | ||||||
|  | ## | ||||||
|  | # @package lodel.settings.validator Lodel2 settings validators/cast module. | ||||||
| # | # | ||||||
| # Validator are registered in the Validator class. | # Validator are registered in the Validator class. | ||||||
| # @note to get a list of registered default validators just run | # @note to get a list of registered default validators just run | ||||||
| # <pre>$ python scripts/settings_validator.py</pre> | # <pre>$ python scripts/settings_validator.py</pre> | ||||||
|  | # | ||||||
|  | # @remarks Should we reconsider specifying conf right in this module? | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Exception class that should be raised when a validation fails | # @brief Exception class that should be raised when a validation fails | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ValidationError(Exception): | class ValidationError(Exception): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Handles settings validators | # @brief Handles settings validators | ||||||
| # | # | ||||||
| # Class instance are callable objects that takes a value argument (the value to validate). It raises | # Class instance are callable objects that takes a value argument (the value | ||||||
| # a ValidationError if validation fails, else it returns a properly | # to validate). It raises a ValidationError if validation fails, else it returns | ||||||
| # casted value. | # a properly cast value. | ||||||
| #@todo implement an IP validator and use it in multisite confspec | # | ||||||
| 
 | #@todo Implement an IP validator for use in the multisite confspec | ||||||
| 
 |  | ||||||
| class Validator(MlNamedObject): | class Validator(MlNamedObject): | ||||||
| 
 | 
 | ||||||
|     _validators = dict() |     _validators = dict() | ||||||
|     _description = dict() |     _description = dict() | ||||||
| 
 | 
 | ||||||
|     # @brief Instanciate a validator |     ## | ||||||
|     #@param name str : validator name |     # @brief Instantiate a validator | ||||||
|     #@param none_is_valid bool : if True None will be validated |     # | ||||||
|     #@param **kwargs : more arguement for the 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): |     def __init__(self, name, none_is_valid=False, display_name=None, help_text=None, **kwargs): | ||||||
|         if name is not None and name not in self._validators: |         if name is not None and name not in self._validators: | ||||||
|             raise LodelFatalError("No validator named '%s'" % name) |             raise LodelFatalError("No validator named '%s'" % name) | ||||||
|  | @ -52,9 +60,11 @@ class Validator(MlNamedObject): | ||||||
|             display_name = name |             display_name = name | ||||||
|         super().__init__(display_name, help_text) |         super().__init__(display_name, help_text) | ||||||
| 
 | 
 | ||||||
|     # @brief Call the validator |     ## | ||||||
|     # @param value * |     # @brief Calls the validator. | ||||||
|     # @return properly casted value |     # | ||||||
|  |     # @param value mixed: | ||||||
|  |     # @return mixed: The properly casted value | ||||||
|     # @throw ValidationError |     # @throw ValidationError | ||||||
|     def __call__(self, value): |     def __call__(self, value): | ||||||
|         if value is None: |         if value is None: | ||||||
|  | @ -70,31 +80,38 @@ class Validator(MlNamedObject): | ||||||
|         except Exception as exp: |         except Exception as exp: | ||||||
|             raise ValidationError(exp) |             raise ValidationError(exp) | ||||||
| 
 | 
 | ||||||
|  |     ## | ||||||
|     # @brief Register a new validator |     # @brief Register a new validator | ||||||
|     # @param name str : validator name |     # | ||||||
|     # @param callback callable : the function that will validate a value |     # @param name string: validator name | ||||||
|     # @param description str |     # @param callback callable: the function that will validate a value | ||||||
|  |     # @param description string: | ||||||
|     @classmethod |     @classmethod | ||||||
|     def register_validator(cls, name, callback, description=None): |     def register_validator(cls, name, callback, description=None): | ||||||
|         if name in cls._validators: |         if name in cls._validators: | ||||||
|             raise NameError("A validator named '%s' allready exists" % name) |             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__'): |         if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'): | ||||||
|             raise TypeError("Callable expected but got %s" % type(callback)) |             raise TypeError("Callable expected but got %s" % type(callback)) | ||||||
|         cls._validators[name] = callback |         cls._validators[name] = callback | ||||||
|         cls._description[name] = description |         cls._description[name] = description | ||||||
| 
 | 
 | ||||||
|  |     ## | ||||||
|     # @brief Get the validator list associated with description |     # @brief Get the validator list associated with description | ||||||
|     @classmethod |     @classmethod | ||||||
|     def validators_list(cls): |     def validators_list(cls): | ||||||
|         return copy.copy(cls._description) |         return copy.copy(cls._description) | ||||||
| 
 | 
 | ||||||
|     # @brief Create and register a list validator |     ## | ||||||
|     # @param elt_validator callable : The validator that will be used for validate each elt value |     # @brief Creates and registers an iterative list validator | ||||||
|     # @param validator_name str |     # | ||||||
|     # @param description None | str |     # @param elt_validator callable: The validator that will be used to validate | ||||||
|     # @param separator str : The element separator |     #         each of the list values. | ||||||
|     # @return A Validator instance |     # @param validator_name string: | ||||||
|  |     # @param description None | string: | ||||||
|  |     # @param separator string: The element separator. | ||||||
|  |     # @return A Validator instance. | ||||||
|     @classmethod |     @classmethod | ||||||
|     def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','): |     def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','): | ||||||
|         def list_validator(value): |         def list_validator(value): | ||||||
|  | @ -111,11 +128,14 @@ class Validator(MlNamedObject): | ||||||
|         cls.register_validator(validator_name, list_validator, description) |         cls.register_validator(validator_name, list_validator, description) | ||||||
|         return cls(validator_name) |         return cls(validator_name) | ||||||
| 
 | 
 | ||||||
|     # @brief Create and register a list validator which reads an array and returns a string |     ## | ||||||
|     # @param elt_validator callable : The validator that will be used for validate each elt value |     # @brief Creates and registers a list validator that reads an array | ||||||
|     # @param validator_name str |     #        and returns a string | ||||||
|     # @param description None | str |     # @param elt_validator callable: The validator that will be used to validate | ||||||
|     # @param separator str : The element separator |     #        each elt value | ||||||
|  |     # @param validator_name string: | ||||||
|  |     # @param description None | string: | ||||||
|  |     # @param separator string: The element separator | ||||||
|     # @return A Validator instance |     # @return A Validator instance | ||||||
|     @classmethod |     @classmethod | ||||||
|     def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','): |     def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','): | ||||||
|  | @ -128,10 +148,12 @@ class Validator(MlNamedObject): | ||||||
|         cls.register_validator(validator_name, write_list_validator, description) |         cls.register_validator(validator_name, write_list_validator, description) | ||||||
|         return cls(validator_name) |         return cls(validator_name) | ||||||
| 
 | 
 | ||||||
|  |     ## | ||||||
|     # @brief Create and register a regular expression validator |     # @brief Create and register a regular expression validator | ||||||
|     # @param pattern str : regex pattern |     # | ||||||
|     # @param validator_name str : The validator name |     # @param pattern str: regex pattern | ||||||
|     # @param description str : Validator description |     # @param validator_name str: The validator name | ||||||
|  |     # @param description str: Validator description | ||||||
|     # @return a Validator instance |     # @return a Validator instance | ||||||
|     @classmethod |     @classmethod | ||||||
|     def create_re_validator(cls, pattern, validator_name, description=None): |     def create_re_validator(cls, pattern, validator_name, description=None): | ||||||
|  | @ -147,7 +169,8 @@ class Validator(MlNamedObject): | ||||||
|                                if description is None else description) |                                if description is None else description) | ||||||
|         return cls(validator_name) |         return cls(validator_name) | ||||||
| 
 | 
 | ||||||
|     #  @return a list of registered validators |     ## | ||||||
|  |     #  @return The list of registered validators. | ||||||
|     @classmethod |     @classmethod | ||||||
|     def validators_list_str(cls): |     def validators_list_str(cls): | ||||||
|         result = '' |         result = '' | ||||||
|  | @ -158,16 +181,20 @@ class Validator(MlNamedObject): | ||||||
|             result += "\n" |             result += "\n" | ||||||
|         return result |         return result | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Integer value validator callback | # @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): | def int_val(value): | ||||||
|     return int(value) |     return int(value) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Output file validator callback | # @brief Output file validator callback | ||||||
|  | # | ||||||
| # @return A file object (if filename is '-' return sys.stderr) | # @return A file object (if filename is '-' return sys.stderr) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def file_err_output(value): | def file_err_output(value): | ||||||
|     if not isinstance(value, str): |     if not isinstance(value, str): | ||||||
|         raise ValidationError("A string was expected but got '%s' " % value) |         raise ValidationError("A string was expected but got '%s' " % value) | ||||||
|  | @ -175,9 +202,9 @@ def file_err_output(value): | ||||||
|         return None |         return None | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
| # @brief Boolean value validator callback |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | ## | ||||||
|  | # @brief Boolean validator callback | ||||||
| def boolean_val(value): | def boolean_val(value): | ||||||
|     if isinstance(value, bool): |     if isinstance(value, bool): | ||||||
|         return value |         return value | ||||||
|  | @ -189,18 +216,18 @@ def boolean_val(value): | ||||||
|         raise ValidationError("A boolean was expected but got '%s' " % value) |         raise ValidationError("A boolean was expected but got '%s' " % value) | ||||||
|     return bool(value) |     return bool(value) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Validate a directory path | # @brief Validate a directory path | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def directory_val(value): | def directory_val(value): | ||||||
|     res = Validator('strip')(value) |     res = Validator('strip')(value) | ||||||
|     if not os.path.isdir(res): |     if not os.path.isdir(res): | ||||||
|         raise ValidationError("Following path don't exists or is not a directory : '%s'" % res) |         raise ValidationError("Following path don't exists or is not a directory : '%s'" % res) | ||||||
|     return res |     return res | ||||||
| 
 | 
 | ||||||
| # @brief Validate a loglevel value |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | ## | ||||||
|  | # @brief Validates a loglevel value | ||||||
| def loglevel_val(value): | def loglevel_val(value): | ||||||
|     valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] |     valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL'] | ||||||
|     if value.upper() not in valids: |     if value.upper() not in valids: | ||||||
|  | @ -208,44 +235,49 @@ def loglevel_val(value): | ||||||
|             "The value '%s' is not a valid loglevel" % value) |             "The value '%s' is not a valid loglevel" % value) | ||||||
|     return value.upper() |     return value.upper() | ||||||
| 
 | 
 | ||||||
| # @brief Validate a path |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | ## | ||||||
|  | # @brief Validates a path | ||||||
|  | # | ||||||
|  | # @remarks is it intended to have both @ref directory_val and this function | ||||||
|  | #            right here? | ||||||
| def path_val(value): | def path_val(value): | ||||||
|     if value is None or not os.path.exists(value): |     if value is None or not os.path.exists(value): | ||||||
|         raise ValidationError( |         raise ValidationError( | ||||||
|             "path '%s' doesn't exists" % value) |             "path '%s' doesn't exists" % value) | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
| # @brief Validate None |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | ## | ||||||
|  | # @brief Validates None | ||||||
|  | # | ||||||
|  | # @remarks Purpose? | ||||||
| def none_val(value): | def none_val(value): | ||||||
|     if value is None: |     if value is None: | ||||||
|         return None |         return None | ||||||
|     raise ValidationError("This settings cannot be set in configuration file") |     raise ValidationError("This settings cannot be set in configuration file") | ||||||
| 
 | 
 | ||||||
| # @brief Validate a string |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | ## | ||||||
|  | # @brief Validates a string | ||||||
| def str_val(value): | def str_val(value): | ||||||
|     try: |     try: | ||||||
|         return str(value) |         return str(value) | ||||||
|     except Exception as exp: |     except Exception as exp: | ||||||
|         raise ValidationError("Can't to convert value to string: " + str(exp)) |         raise ValidationError("Can't to convert value to string: " + str(exp)) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Validate using a regex | # @brief Validate using a regex | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def regex_val(value, pattern): | def regex_val(value, pattern): | ||||||
|     if re.match(pattern, value) is None: |     if re.match(pattern, value) is None: | ||||||
|         raise ValidationError("The value '%s' is not validated by : \ |         raise ValidationError("The value '%s' is not validated by : \ | ||||||
| r\"%s\"" % (value, pattern)) | r\"%s\"" % (value, pattern)) | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Validate a hostname (ipv4 or ipv6) | # @brief Validate a hostname (ipv4 or ipv6) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def host_val(value): | def host_val(value): | ||||||
|     if value == 'localhost': |     if value == 'localhost': | ||||||
|         return value |         return value | ||||||
|  | @ -275,67 +307,52 @@ def custom_list_validator(value, validator_name, validator_kwargs=None): | ||||||
|         validator(item) |         validator(item) | ||||||
|     return value.split() |     return value.split() | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # | # | ||||||
| #   Default validators registration | #   Default validators registration | ||||||
| # | # | ||||||
| 
 |  | ||||||
| Validator.register_validator('custom_list', custom_list_validator, | Validator.register_validator('custom_list', custom_list_validator, | ||||||
|                              'A list validator that takes a "validator_name" as argument') |                              'A list validator that takes a "validator_name" as argument') | ||||||
| 
 |  | ||||||
| Validator.register_validator('dummy', lambda value: value, 'Validate anything') | Validator.register_validator('dummy', lambda value: value, 'Validate anything') | ||||||
| 
 |  | ||||||
| Validator.register_validator('none', none_val, 'Validate None') | Validator.register_validator('none', none_val, 'Validate None') | ||||||
| 
 |  | ||||||
| Validator.register_validator('string', str_val, 'Validate string values') | Validator.register_validator('string', str_val, 'Validate string values') | ||||||
| 
 |  | ||||||
| Validator.register_validator('strip', str.strip, 'String trim') | Validator.register_validator('strip', str.strip, 'String trim') | ||||||
| 
 |  | ||||||
| Validator.register_validator('int', int_val, 'Integer value validator') | Validator.register_validator('int', int_val, 'Integer value validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('bool', boolean_val, 'Boolean value validator') | Validator.register_validator('bool', boolean_val, 'Boolean value validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('errfile', file_err_output, | Validator.register_validator('errfile', file_err_output, | ||||||
|                              'Error output file validator (return stderr if filename is "-")') |                              'Error output file validator (return stderr if filename is "-")') | ||||||
| 
 |  | ||||||
| Validator.register_validator('directory', directory_val, | Validator.register_validator('directory', directory_val, | ||||||
|                              'Directory path validator') |                              'Directory path validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator') | Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('path', path_val, 'path validator') | Validator.register_validator('path', path_val, 'path validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('host', host_val, 'host validator') | Validator.register_validator('host', host_val, 'host validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator('regex', regex_val, | Validator.register_validator('regex', regex_val, | ||||||
|                              'RegEx name validator (take re as argument)') |                              'RegEx name validator (take re as argument)') | ||||||
| 
 |  | ||||||
| Validator.create_list_validator('list', Validator('strip'), description="Simple list validator. Validate a list of values separated by ','", | Validator.create_list_validator('list', Validator('strip'), description="Simple list validator. Validate a list of values separated by ','", | ||||||
|                                 separator=',') |                                 separator=',') | ||||||
| 
 |  | ||||||
| Validator.create_list_validator( | Validator.create_list_validator( | ||||||
|     'directory_list', |     'directory_list', | ||||||
|     Validator('directory'), |     Validator('directory'), | ||||||
|     description="Validator for a list of directory path separated with ','", |     description="Validator for a list of directory path separated with ','", | ||||||
|     separator=',') |     separator=',') | ||||||
| 
 |  | ||||||
| Validator.create_write_list_validator( | Validator.create_write_list_validator( | ||||||
|     'write_list', |     'write_list', | ||||||
|     Validator('directory'), |     Validator('directory'), | ||||||
|     description="Validator for an array of values \ |     description="Validator for an array of values \ | ||||||
|         which will be set in a string, separated by ','", |         which will be set in a string, separated by ','", | ||||||
|     separator=',') |     separator=',') | ||||||
| 
 |  | ||||||
| Validator.create_re_validator( | Validator.create_re_validator( | ||||||
|     r'^https?://[^\./]+.[^\./]+/?.*$', |     r'^https?://[^\./]+.[^\./]+/?.*$', | ||||||
|     'http_url', |     'http_url', | ||||||
|     'Url validator') |     '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 | # Designed to validate a conf that indicates a class.field in an EM | ||||||
| #@todo modified the hardcoded dyncode import (it's a warning) | # | ||||||
| 
 | # @todo modify the hardcoded dyncode import (it's a warning) | ||||||
| 
 |  | ||||||
| def emfield_val(value): | def emfield_val(value): | ||||||
|     LodelContext.expose_modules(globals(), |     LodelContext.expose_modules(globals(), | ||||||
|                                 {'lodel.plugin.hooks': ['LodelHook']}) |                                 {'lodel.plugin.hooks': ['LodelHook']}) | ||||||
|  | @ -359,11 +376,11 @@ def emfield_val(value): | ||||||
|             raise ValidationError(msg % value) |             raise ValidationError(msg % value) | ||||||
|     return 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 | # Able to check that the value is a plugin and if it is of a specific type | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def plugin_validator(value, ptype=None): | def plugin_validator(value, ptype=None): | ||||||
|     if value: |     if value: | ||||||
|         LodelContext.expose_modules(globals(), { |         LodelContext.expose_modules(globals(), { | ||||||
|  | @ -396,26 +413,25 @@ Validator.register_validator( | ||||||
|     'plugin', |     'plugin', | ||||||
|     plugin_validator, |     plugin_validator, | ||||||
|     'plugin name & type validator') |     'plugin name & type validator') | ||||||
| 
 |  | ||||||
| Validator.register_validator( | Validator.register_validator( | ||||||
|     'emfield', |     'emfield', | ||||||
|     emfield_val, |     emfield_val, | ||||||
|     'EmField name validator') |     'EmField name validator') | ||||||
| 
 | 
 | ||||||
| # | 
 | ||||||
|  | ## | ||||||
| #   Lodel 2 configuration specification | #   Lodel 2 configuration specification | ||||||
| # | # | ||||||
| 
 |  | ||||||
| # @brief Append a piece of confspec | # @brief Append a piece of confspec | ||||||
| #@note orig is modified during the process | # | ||||||
| #@param orig dict : the confspec to update | # @param orig dict : the confspec to update | ||||||
| #@param section str : section name | # @param section str : section name | ||||||
| #@param key str | # @param key str | ||||||
| #@param validator Validator : the validator to use to check this configuration key's value | # @param validator Validator : the validator to use to check this configuration key's value | ||||||
| #@param default | # @param default | ||||||
| #@return new confspec | # @return new confspec | ||||||
| 
 | # | ||||||
| 
 | # @note orig is modified during the process | ||||||
| def confspec_append(orig, section, key, validator, default): | def confspec_append(orig, section, key, validator, default): | ||||||
|     if section not in orig: |     if section not in orig: | ||||||
|         orig[section] = dict() |         orig[section] = dict() | ||||||
|  | @ -423,6 +439,8 @@ def confspec_append(orig, section, key, validator, default): | ||||||
|         orig[section][key] = (default, validator) |         orig[section][key] = (default, validator) | ||||||
|     return orig |     return orig | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## | ||||||
| # @brief Global specifications for lodel2 settings | # @brief Global specifications for lodel2 settings | ||||||
| LODEL2_CONF_SPECS = { | LODEL2_CONF_SPECS = { | ||||||
|     'lodel2': { |     'lodel2': { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Quentin Bonaventure
				Quentin Bonaventure