|
@@ -6,42 +6,50 @@ import re
|
6
|
6
|
import socket
|
7
|
7
|
import inspect
|
8
|
8
|
import copy
|
9
|
|
-
|
10
|
9
|
from lodel.context import LodelContext
|
|
10
|
+
|
|
11
|
+
|
11
|
12
|
LodelContext.expose_modules(globals(), {
|
12
|
13
|
'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
|
13
|
14
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
14
|
15
|
'LodelFatalError', 'FieldValidationError']})
|
15
|
16
|
|
16
|
|
-# @package lodel.settings.validator Lodel2 settings validators/cast module
|
|
17
|
+
|
|
18
|
+##
|
|
19
|
+# @package lodel.settings.validator Lodel2 settings validators/cast module.
|
17
|
20
|
#
|
18
|
21
|
# Validator are registered in the Validator class.
|
19
|
22
|
# @note to get a list of registered default validators just run
|
20
|
23
|
# <pre>$ python scripts/settings_validator.py</pre>
|
21
|
|
-
|
22
|
|
-# @brief Exception class that should be raised when a validation fails
|
|
24
|
+#
|
|
25
|
+# @remarks Should we reconsider specifying conf right in this module?
|
23
|
26
|
|
24
|
27
|
|
|
28
|
+##
|
|
29
|
+# @brief Exception class that should be raised when a validation fails
|
25
|
30
|
class ValidationError(Exception):
|
26
|
31
|
pass
|
27
|
32
|
|
|
33
|
+
|
|
34
|
+##
|
28
|
35
|
# @brief Handles settings validators
|
29
|
36
|
#
|
30
|
|
-# Class instance are callable objects that takes a value argument (the value to validate). It raises
|
31
|
|
-# a ValidationError if validation fails, else it returns a properly
|
32
|
|
-# casted value.
|
33
|
|
-#@todo implement an IP validator and use it in multisite confspec
|
34
|
|
-
|
35
|
|
-
|
|
37
|
+# Class instance are callable objects that takes a value argument (the value
|
|
38
|
+# to validate). It raises a ValidationError if validation fails, else it returns
|
|
39
|
+# a properly cast value.
|
|
40
|
+#
|
|
41
|
+#@todo Implement an IP validator for use in the multisite confspec
|
36
|
42
|
class Validator(MlNamedObject):
|
37
|
43
|
|
38
|
44
|
_validators = dict()
|
39
|
45
|
_description = dict()
|
40
|
46
|
|
41
|
|
- # @brief Instanciate a validator
|
42
|
|
- #@param name str : validator name
|
43
|
|
- #@param none_is_valid bool : if True None will be validated
|
44
|
|
- #@param **kwargs : more arguement for the validator
|
|
47
|
+ ##
|
|
48
|
+ # @brief Instantiate a validator
|
|
49
|
+ #
|
|
50
|
+ #@param name str: validator name
|
|
51
|
+ #@param none_is_valid bool: if True None will be validated
|
|
52
|
+ #@param **kwargs: more arguments for the validator
|
45
|
53
|
def __init__(self, name, none_is_valid=False, display_name=None, help_text=None, **kwargs):
|
46
|
54
|
if name is not None and name not in self._validators:
|
47
|
55
|
raise LodelFatalError("No validator named '%s'" % name)
|
|
@@ -52,9 +60,11 @@ class Validator(MlNamedObject):
|
52
|
60
|
display_name = name
|
53
|
61
|
super().__init__(display_name, help_text)
|
54
|
62
|
|
55
|
|
- # @brief Call the validator
|
56
|
|
- # @param value *
|
57
|
|
- # @return properly casted value
|
|
63
|
+ ##
|
|
64
|
+ # @brief Calls the validator.
|
|
65
|
+ #
|
|
66
|
+ # @param value mixed:
|
|
67
|
+ # @return mixed: The properly casted value
|
58
|
68
|
# @throw ValidationError
|
59
|
69
|
def __call__(self, value):
|
60
|
70
|
if value is None:
|
|
@@ -70,31 +80,38 @@ class Validator(MlNamedObject):
|
70
|
80
|
except Exception as exp:
|
71
|
81
|
raise ValidationError(exp)
|
72
|
82
|
|
|
83
|
+ ##
|
73
|
84
|
# @brief Register a new validator
|
74
|
|
- # @param name str : validator name
|
75
|
|
- # @param callback callable : the function that will validate a value
|
76
|
|
- # @param description str
|
|
85
|
+ #
|
|
86
|
+ # @param name string: validator name
|
|
87
|
+ # @param callback callable: the function that will validate a value
|
|
88
|
+ # @param description string:
|
77
|
89
|
@classmethod
|
78
|
90
|
def register_validator(cls, name, callback, description=None):
|
79
|
91
|
if name in cls._validators:
|
80
|
92
|
raise NameError("A validator named '%s' allready exists" % name)
|
81
|
|
- # Broken test for callable
|
|
93
|
+ ##
|
|
94
|
+ # @todo Broken test for callable.
|
82
|
95
|
if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
|
83
|
96
|
raise TypeError("Callable expected but got %s" % type(callback))
|
84
|
97
|
cls._validators[name] = callback
|
85
|
98
|
cls._description[name] = description
|
86
|
99
|
|
|
100
|
+ ##
|
87
|
101
|
# @brief Get the validator list associated with description
|
88
|
102
|
@classmethod
|
89
|
103
|
def validators_list(cls):
|
90
|
104
|
return copy.copy(cls._description)
|
91
|
105
|
|
92
|
|
- # @brief Create and register a list validator
|
93
|
|
- # @param elt_validator callable : The validator that will be used for validate each elt value
|
94
|
|
- # @param validator_name str
|
95
|
|
- # @param description None | str
|
96
|
|
- # @param separator str : The element separator
|
97
|
|
- # @return A Validator instance
|
|
106
|
+ ##
|
|
107
|
+ # @brief Creates and registers an iterative list validator
|
|
108
|
+ #
|
|
109
|
+ # @param elt_validator callable: The validator that will be used to validate
|
|
110
|
+ # each of the list values.
|
|
111
|
+ # @param validator_name string:
|
|
112
|
+ # @param description None | string:
|
|
113
|
+ # @param separator string: The element separator.
|
|
114
|
+ # @return A Validator instance.
|
98
|
115
|
@classmethod
|
99
|
116
|
def create_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
100
|
117
|
def list_validator(value):
|
|
@@ -111,11 +128,14 @@ class Validator(MlNamedObject):
|
111
|
128
|
cls.register_validator(validator_name, list_validator, description)
|
112
|
129
|
return cls(validator_name)
|
113
|
130
|
|
114
|
|
- # @brief Create and register a list validator which reads an array and returns a string
|
115
|
|
- # @param elt_validator callable : The validator that will be used for validate each elt value
|
116
|
|
- # @param validator_name str
|
117
|
|
- # @param description None | str
|
118
|
|
- # @param separator str : The element separator
|
|
131
|
+ ##
|
|
132
|
+ # @brief Creates and registers a list validator that reads an array
|
|
133
|
+ # and returns a string
|
|
134
|
+ # @param elt_validator callable: The validator that will be used to validate
|
|
135
|
+ # each elt value
|
|
136
|
+ # @param validator_name string:
|
|
137
|
+ # @param description None | string:
|
|
138
|
+ # @param separator string: The element separator
|
119
|
139
|
# @return A Validator instance
|
120
|
140
|
@classmethod
|
121
|
141
|
def create_write_list_validator(cls, validator_name, elt_validator, description=None, separator=','):
|
|
@@ -128,10 +148,12 @@ class Validator(MlNamedObject):
|
128
|
148
|
cls.register_validator(validator_name, write_list_validator, description)
|
129
|
149
|
return cls(validator_name)
|
130
|
150
|
|
|
151
|
+ ##
|
131
|
152
|
# @brief Create and register a regular expression validator
|
132
|
|
- # @param pattern str : regex pattern
|
133
|
|
- # @param validator_name str : The validator name
|
134
|
|
- # @param description str : Validator description
|
|
153
|
+ #
|
|
154
|
+ # @param pattern str: regex pattern
|
|
155
|
+ # @param validator_name str: The validator name
|
|
156
|
+ # @param description str: Validator description
|
135
|
157
|
# @return a Validator instance
|
136
|
158
|
@classmethod
|
137
|
159
|
def create_re_validator(cls, pattern, validator_name, description=None):
|
|
@@ -147,7 +169,8 @@ class Validator(MlNamedObject):
|
147
|
169
|
if description is None else description)
|
148
|
170
|
return cls(validator_name)
|
149
|
171
|
|
150
|
|
- # @return a list of registered validators
|
|
172
|
+ ##
|
|
173
|
+ # @return The list of registered validators.
|
151
|
174
|
@classmethod
|
152
|
175
|
def validators_list_str(cls):
|
153
|
176
|
result = ''
|
|
@@ -158,16 +181,20 @@ class Validator(MlNamedObject):
|
158
|
181
|
result += "\n"
|
159
|
182
|
return result
|
160
|
183
|
|
161
|
|
-# @brief Integer value validator callback
|
162
|
|
-
|
163
|
184
|
|
|
185
|
+##
|
|
186
|
+# @brief Integer value validator callback
|
|
187
|
+#
|
|
188
|
+# @remarks Is it intended that this function simply tries casting to an integer?
|
|
189
|
+# Is it also intended that it does not use any try/catch block?
|
164
|
190
|
def int_val(value):
|
165
|
191
|
return int(value)
|
166
|
192
|
|
|
193
|
+
|
|
194
|
+##
|
167
|
195
|
# @brief Output file validator callback
|
|
196
|
+#
|
168
|
197
|
# @return A file object (if filename is '-' return sys.stderr)
|
169
|
|
-
|
170
|
|
-
|
171
|
198
|
def file_err_output(value):
|
172
|
199
|
if not isinstance(value, str):
|
173
|
200
|
raise ValidationError("A string was expected but got '%s' " % value)
|
|
@@ -175,9 +202,9 @@ def file_err_output(value):
|
175
|
202
|
return None
|
176
|
203
|
return value
|
177
|
204
|
|
178
|
|
-# @brief Boolean value validator callback
|
179
|
|
-
|
180
|
205
|
|
|
206
|
+##
|
|
207
|
+# @brief Boolean validator callback
|
181
|
208
|
def boolean_val(value):
|
182
|
209
|
if isinstance(value, bool):
|
183
|
210
|
return value
|
|
@@ -189,18 +216,18 @@ def boolean_val(value):
|
189
|
216
|
raise ValidationError("A boolean was expected but got '%s' " % value)
|
190
|
217
|
return bool(value)
|
191
|
218
|
|
192
|
|
-# @brief Validate a directory path
|
193
|
|
-
|
194
|
219
|
|
|
220
|
+##
|
|
221
|
+# @brief Validate a directory path
|
195
|
222
|
def directory_val(value):
|
196
|
223
|
res = Validator('strip')(value)
|
197
|
224
|
if not os.path.isdir(res):
|
198
|
225
|
raise ValidationError("Following path don't exists or is not a directory : '%s'" % res)
|
199
|
226
|
return res
|
200
|
227
|
|
201
|
|
-# @brief Validate a loglevel value
|
202
|
|
-
|
203
|
228
|
|
|
229
|
+##
|
|
230
|
+# @brief Validates a loglevel value
|
204
|
231
|
def loglevel_val(value):
|
205
|
232
|
valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
|
206
|
233
|
if value.upper() not in valids:
|
|
@@ -208,44 +235,49 @@ def loglevel_val(value):
|
208
|
235
|
"The value '%s' is not a valid loglevel" % value)
|
209
|
236
|
return value.upper()
|
210
|
237
|
|
211
|
|
-# @brief Validate a path
|
212
|
|
-
|
213
|
238
|
|
|
239
|
+##
|
|
240
|
+# @brief Validates a path
|
|
241
|
+#
|
|
242
|
+# @remarks is it intended to have both @ref directory_val and this function
|
|
243
|
+# right here?
|
214
|
244
|
def path_val(value):
|
215
|
245
|
if value is None or not os.path.exists(value):
|
216
|
246
|
raise ValidationError(
|
217
|
247
|
"path '%s' doesn't exists" % value)
|
218
|
248
|
return value
|
219
|
249
|
|
220
|
|
-# @brief Validate None
|
221
|
|
-
|
222
|
250
|
|
|
251
|
+##
|
|
252
|
+# @brief Validates None
|
|
253
|
+#
|
|
254
|
+# @remarks Purpose?
|
223
|
255
|
def none_val(value):
|
224
|
256
|
if value is None:
|
225
|
257
|
return None
|
226
|
258
|
raise ValidationError("This settings cannot be set in configuration file")
|
227
|
259
|
|
228
|
|
-# @brief Validate a string
|
229
|
|
-
|
230
|
260
|
|
|
261
|
+##
|
|
262
|
+# @brief Validates a string
|
231
|
263
|
def str_val(value):
|
232
|
264
|
try:
|
233
|
265
|
return str(value)
|
234
|
266
|
except Exception as exp:
|
235
|
267
|
raise ValidationError("Can't to convert value to string: " + str(exp))
|
236
|
268
|
|
237
|
|
-# @brief Validate using a regex
|
238
|
|
-
|
239
|
269
|
|
|
270
|
+##
|
|
271
|
+# @brief Validate using a regex
|
240
|
272
|
def regex_val(value, pattern):
|
241
|
273
|
if re.match(pattern, value) is None:
|
242
|
274
|
raise ValidationError("The value '%s' is not validated by : \
|
243
|
275
|
r\"%s\"" % (value, pattern))
|
244
|
276
|
return value
|
245
|
277
|
|
246
|
|
-# @brief Validate a hostname (ipv4 or ipv6)
|
247
|
|
-
|
248
|
278
|
|
|
279
|
+##
|
|
280
|
+# @brief Validate a hostname (ipv4 or ipv6)
|
249
|
281
|
def host_val(value):
|
250
|
282
|
if value == 'localhost':
|
251
|
283
|
return value
|
|
@@ -275,67 +307,52 @@ def custom_list_validator(value, validator_name, validator_kwargs=None):
|
275
|
307
|
validator(item)
|
276
|
308
|
return value.split()
|
277
|
309
|
|
|
310
|
+
|
278
|
311
|
#
|
279
|
312
|
# Default validators registration
|
280
|
313
|
#
|
281
|
|
-
|
282
|
314
|
Validator.register_validator('custom_list', custom_list_validator,
|
283
|
315
|
'A list validator that takes a "validator_name" as argument')
|
284
|
|
-
|
285
|
316
|
Validator.register_validator('dummy', lambda value: value, 'Validate anything')
|
286
|
|
-
|
287
|
317
|
Validator.register_validator('none', none_val, 'Validate None')
|
288
|
|
-
|
289
|
318
|
Validator.register_validator('string', str_val, 'Validate string values')
|
290
|
|
-
|
291
|
319
|
Validator.register_validator('strip', str.strip, 'String trim')
|
292
|
|
-
|
293
|
320
|
Validator.register_validator('int', int_val, 'Integer value validator')
|
294
|
|
-
|
295
|
321
|
Validator.register_validator('bool', boolean_val, 'Boolean value validator')
|
296
|
|
-
|
297
|
322
|
Validator.register_validator('errfile', file_err_output,
|
298
|
323
|
'Error output file validator (return stderr if filename is "-")')
|
299
|
|
-
|
300
|
324
|
Validator.register_validator('directory', directory_val,
|
301
|
325
|
'Directory path validator')
|
302
|
|
-
|
303
|
326
|
Validator.register_validator('loglevel', loglevel_val, 'Loglevel validator')
|
304
|
|
-
|
305
|
327
|
Validator.register_validator('path', path_val, 'path validator')
|
306
|
|
-
|
307
|
328
|
Validator.register_validator('host', host_val, 'host validator')
|
308
|
|
-
|
309
|
329
|
Validator.register_validator('regex', regex_val,
|
310
|
330
|
'RegEx name validator (take re as argument)')
|
311
|
|
-
|
312
|
331
|
Validator.create_list_validator('list', Validator('strip'), description="Simple list validator. Validate a list of values separated by ','",
|
313
|
332
|
separator=',')
|
314
|
|
-
|
315
|
333
|
Validator.create_list_validator(
|
316
|
334
|
'directory_list',
|
317
|
335
|
Validator('directory'),
|
318
|
336
|
description="Validator for a list of directory path separated with ','",
|
319
|
337
|
separator=',')
|
320
|
|
-
|
321
|
338
|
Validator.create_write_list_validator(
|
322
|
339
|
'write_list',
|
323
|
340
|
Validator('directory'),
|
324
|
341
|
description="Validator for an array of values \
|
325
|
342
|
which will be set in a string, separated by ','",
|
326
|
343
|
separator=',')
|
327
|
|
-
|
328
|
344
|
Validator.create_re_validator(
|
329
|
345
|
r'^https?://[^\./]+.[^\./]+/?.*$',
|
330
|
346
|
'http_url',
|
331
|
347
|
'Url validator')
|
332
|
348
|
|
333
|
|
-# @brief Validator for Editorial model component
|
334
|
|
-#
|
335
|
|
-# Designed to validate a conf that indicate a class.field in an EM
|
336
|
|
-#@todo modified the hardcoded dyncode import (it's a warning)
|
337
|
|
-
|
338
|
349
|
|
|
350
|
+##
|
|
351
|
+# @brief Validator for Editorial Model components.
|
|
352
|
+#
|
|
353
|
+# Designed to validate a conf that indicates a class.field in an EM
|
|
354
|
+#
|
|
355
|
+# @todo modify the hardcoded dyncode import (it's a warning)
|
339
|
356
|
def emfield_val(value):
|
340
|
357
|
LodelContext.expose_modules(globals(),
|
341
|
358
|
{'lodel.plugin.hooks': ['LodelHook']})
|
|
@@ -359,11 +376,11 @@ def emfield_val(value):
|
359
|
376
|
raise ValidationError(msg % value)
|
360
|
377
|
return value
|
361
|
378
|
|
362
|
|
-# @brief Validator for plugin name & optionnaly type
|
|
379
|
+
|
|
380
|
+##
|
|
381
|
+# @brief Validator for plugin name & its type optionally
|
363
|
382
|
#
|
364
|
383
|
# Able to check that the value is a plugin and if it is of a specific type
|
365
|
|
-
|
366
|
|
-
|
367
|
384
|
def plugin_validator(value, ptype=None):
|
368
|
385
|
if value:
|
369
|
386
|
LodelContext.expose_modules(globals(), {
|
|
@@ -396,26 +413,25 @@ Validator.register_validator(
|
396
|
413
|
'plugin',
|
397
|
414
|
plugin_validator,
|
398
|
415
|
'plugin name & type validator')
|
399
|
|
-
|
400
|
416
|
Validator.register_validator(
|
401
|
417
|
'emfield',
|
402
|
418
|
emfield_val,
|
403
|
419
|
'EmField name validator')
|
404
|
420
|
|
405
|
|
-#
|
|
421
|
+
|
|
422
|
+##
|
406
|
423
|
# Lodel 2 configuration specification
|
407
|
424
|
#
|
408
|
|
-
|
409
|
425
|
# @brief Append a piece of confspec
|
410
|
|
-#@note orig is modified during the process
|
411
|
|
-#@param orig dict : the confspec to update
|
412
|
|
-#@param section str : section name
|
413
|
|
-#@param key str
|
414
|
|
-#@param validator Validator : the validator to use to check this configuration key's value
|
415
|
|
-#@param default
|
416
|
|
-#@return new confspec
|
417
|
|
-
|
418
|
|
-
|
|
426
|
+#
|
|
427
|
+# @param orig dict : the confspec to update
|
|
428
|
+# @param section str : section name
|
|
429
|
+# @param key str
|
|
430
|
+# @param validator Validator : the validator to use to check this configuration key's value
|
|
431
|
+# @param default
|
|
432
|
+# @return new confspec
|
|
433
|
+#
|
|
434
|
+# @note orig is modified during the process
|
419
|
435
|
def confspec_append(orig, section, key, validator, default):
|
420
|
436
|
if section not in orig:
|
421
|
437
|
orig[section] = dict()
|
|
@@ -423,6 +439,8 @@ def confspec_append(orig, section, key, validator, default):
|
423
|
439
|
orig[section][key] = (default, validator)
|
424
|
440
|
return orig
|
425
|
441
|
|
|
442
|
+
|
|
443
|
+##
|
426
|
444
|
# @brief Global specifications for lodel2 settings
|
427
|
445
|
LODEL2_CONF_SPECS = {
|
428
|
446
|
'lodel2': {
|