Browse Source

Class validator

prieto 8 years ago
parent
commit
ac2312dad0

+ 2
- 2
lodel/settings/settings_loader.py View File

@@ -9,7 +9,7 @@ from lodel.context import LodelContext
9 9
 LodelContext.expose_modules(globals(), {
10 10
     'lodel.logger': 'logger',
11 11
     'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
12
-    'lodel.settings.validator': ['SettingsValidationError']})
12
+    'lodel.settings.validator': ['SettingValidationError']})
13 13
 
14 14
 ##@brief Merges and loads configuration files
15 15
 class SettingsLoader(object):
@@ -107,7 +107,7 @@ class SettingsLoader(object):
107 107
                                         key_id = section+'.'+keyname)
108 108
                 self.__errors_list.append(expt)
109 109
             else:
110
-                expt = SettingsValidationError(
110
+                expt = ValidationError(
111 111
                                                 "For %s.%s : %s" % 
112 112
                                                 (section, keyname,e)
113 113
                 )

+ 7
- 296
lodel/settings/validator.py View File

@@ -10,7 +10,8 @@ import copy
10 10
 from lodel.context import LodelContext
11 11
 LodelContext.expose_modules(globals(), {
12 12
     'lodel.exceptions': ['LodelException', 'LodelExceptions',
13
-        'LodelFatalError', 'FieldValidationError']})
13
+        'LodelFatalError', 'FieldValidationError'],
14
+    'lodel.validator.validator': ['Validator', 'ValidationError']})
14 15
 
15 16
 ## @package lodel.settings.validator Lodel2 settings validators/cast module
16 17
 #
@@ -19,7 +20,7 @@ LodelContext.expose_modules(globals(), {
19 20
 # <pre>$ python scripts/settings_validator.py</pre>
20 21
 
21 22
 ##@brief Exception class that should be raised when a validation fails
22
-class SettingsValidationError(Exception):
23
+class SettingValidationError(ValidationError):
23 24
     pass
24 25
 
25 26
 ##@brief Handles settings validators
@@ -28,216 +29,21 @@ class SettingsValidationError(Exception):
28 29
 # a SettingsValidationError if validation fails, else it returns a properly
29 30
 # casted value.
30 31
 #@todo implement an IP validator and use it in multisite confspec
31
-class SettingValidator(object):
32
-    
33
-    _validators = dict()
34
-    _description = dict()
35
-    
32
+class SettingValidator(Validator):
33
+
36 34
     ##@brief Instanciate a validator
37 35
     #@param name str : validator name
38 36
     #@param none_is_valid bool : if True None will be validated
39 37
     #@param **kwargs : more arguement for the validator
40 38
     def __init__(self, name, none_is_valid = False, **kwargs):
41
-        if name is not None and name not in self._validators:
42
-            raise LodelFatalError("No validator named '%s'" % name)
43
-        self.__none_is_valid = none_is_valid
44
-        self.__name = name
45
-        self._opt_args = kwargs
39
+        super().__init__(name, none_is_valid = False, **kwargs)
46 40
 
47 41
     ##@brief Call the validator
48 42
     # @param value *
49 43
     # @return properly casted value
50 44
     # @throw SettingsValidationError
51 45
     def __call__(self, value):
52
-        if self.__none_is_valid and value is None:
53
-            return None
54
-        try:
55
-            ret = self._validators[self.__name](value, **self._opt_args)
56
-            return ret
57
-        except Exception as e:
58
-            raise SettingsValidationError(e)
59
-    
60
-    ##@brief Register a new validator
61
-    # @param name str : validator name
62
-    # @param callback callable : the function that will validate a value
63
-    # @param description str
64
-    @classmethod
65
-    def register_validator(cls, name, callback, description=None):
66
-        if name in cls._validators:
67
-            raise NameError("A validator named '%s' allready exists" % name)
68
-        # Broken test for callable
69
-        if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
70
-            raise TypeError("Callable expected but got %s" % type(callback))
71
-        cls._validators[name] = callback
72
-        cls._description[name] = description
73
-    
74
-    ##@brief Get the validator list associated with description
75
-    @classmethod
76
-    def validators_list(cls):
77
-        return copy.copy(cls._description)
78
-
79
-    ##@brief Create and register a list validator
80
-    # @param elt_validator callable : The validator that will be used for validate each elt value
81
-    # @param validator_name str
82
-    # @param description None | str
83
-    # @param separator str : The element separator
84
-    # @return A SettingValidator instance
85
-    @classmethod
86
-    def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
87
-        def list_validator(value):
88
-            res = list()
89
-            errors = list()
90
-            for elt in value.split(separator):
91
-                elt = elt_validator(elt)
92
-                if len(elt) > 0:
93
-                    res.append(elt)
94
-            return res
95
-        description = "Convert value to an array" if description is None else description
96
-        cls.register_validator(
97
-                                validator_name,
98
-                                list_validator,
99
-                                description)
100
-        return cls(validator_name)
101
- 
102
-    ##@brief Create and register a list validator which reads an array and returns a string
103
-    # @param elt_validator callable : The validator that will be used for validate each elt value
104
-    # @param validator_name str
105
-    # @param description None | str
106
-    # @param separator str : The element separator
107
-    # @return A SettingValidator instance
108
-    @classmethod
109
-    def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
110
-        def write_list_validator(value):
111
-            res = ''
112
-            errors = list()
113
-            for elt in value:
114
-                res += elt_validator(elt) + ','
115
-            return res[:len(res)-1]
116
-        description = "Convert value to a string" if description is None else description
117
-        cls.register_validator(
118
-                                validator_name,
119
-                                write_list_validator,
120
-                                description)
121
-        return cls(validator_name)
122
-    
123
-    ##@brief Create and register a regular expression validator
124
-    # @param pattern str : regex pattern
125
-    # @param validator_name str : The validator name
126
-    # @param description str : Validator description
127
-    # @return a SettingValidator instance
128
-    @classmethod
129
-    def create_re_validator(cls, pattern, validator_name, description = None):
130
-        def re_validator(value):
131
-            if not re.match(pattern, value):
132
-                raise SettingsValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
133
-            return value
134
-        #registering the validator
135
-        cls.register_validator(
136
-                                validator_name,
137
-                                re_validator,
138
-                                ("Match value to '%s'" % pattern) if description is None else description)
139
-        return cls(validator_name)
140
-
141
-    
142
-    ## @return a list of registered validators
143
-    @classmethod
144
-    def validators_list_str(cls):
145
-        result = ''
146
-        for name in sorted(cls._validators.keys()):
147
-            result += "\t%016s" % name
148
-            if name in cls._description and cls._description[name] is not None:
149
-                result += ": %s" % cls._description[name]
150
-            result += "\n"
151
-        return result
152
-
153
-##@brief Integer value validator callback
154
-def int_val(value):
155
-    return int(value)
156
-
157
-##@brief Output file validator callback
158
-# @return A file object (if filename is '-' return sys.stderr)
159
-def file_err_output(value):
160
-    if not isinstance(value, str):
161
-        raise SettingsValidationError("A string was expected but got '%s' " % value)
162
-    if value == '-':
163
-        return None
164
-    return value
165
-
166
-##@brief Boolean value validator callback
167
-def boolean_val(value):
168
-    if isinstance(value, bool):
169
-        return value
170
-    if value.strip().lower() == 'true' or value.strip() == '1':
171
-        value = True
172
-    elif value.strip().lower() == 'false' or value.strip() == '0':
173
-        value = False
174
-    else:
175
-        raise SettingsValidationError("A boolean was expected but got '%s' " % value)
176
-    return bool(value)
177
-
178
-##@brief Validate a directory path
179
-def directory_val(value):
180
-    res = SettingValidator('strip')(value)
181
-    if not os.path.isdir(res):
182
-        raise SettingsValidationError("Folowing path don't exists or is not a directory : '%s'"%res)
183
-    return res
184
-
185
-##@brief Validate a loglevel value
186
-def loglevel_val(value):
187
-    valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
188
-    if value.upper() not in valids:
189
-        raise SettingsValidationError(
190
-                "The value '%s' is not a valid loglevel" % value)
191
-    return value.upper()
192
-
193
-##@brief Validate a path
194
-def path_val(value):
195
-    if value is None or not os.path.exists(value):
196
-        raise SettingsValidationError(
197
-                "path '%s' doesn't exists" % value)
198
-    return value
199
-
200
-##@brief Validate None
201
-def none_val(value):
202
-    if value is None:
203
-        return None
204
-    raise SettingsValidationError("This settings cannot be set in configuration file")
205
-
206
-##@brief Validate a string
207
-def str_val(value):
208
-    try:
209
-        return str(value)
210
-    except Exception as e:
211
-        raise SettingsValidationError("Not able to convert value to string : " + str(e))
212
-
213
-##@brief Validate using a regex
214
-def regex_val(value, pattern):
215
-    if re.match(pattern, value) is None:
216
-        raise SettingsValidationError("The value '%s' is not validated by : \
217
-r\"%s\"" %(value, pattern))
218
-    return value
219
-
220
-##@brief Validate a hostname (ipv4 or ipv6)
221
-def host_val(value):
222
-    if value == 'localhost':
223
-        return value
224
-    ok = False
225
-    try:
226
-        socket.inet_aton(value)
227
-        return value
228
-    except (TypeError,OSError):
229
-        pass
230
-    try:
231
-        socket.inet_pton(socket.AF_INET6, value)
232
-        return value
233
-    except (TypeError,OSError):
234
-        pass
235
-    try:
236
-        socket.getaddrinfo(value, 80)
237
-        return value
238
-    except (TypeError,socket.gaierror):
239
-        msg = "The value '%s' is not a valid host"
240
-        raise SettingsValidationError(msg % value)
46
+        super().__call__(value)
241 47
 
242 48
 ##@brief Validator for Editorial model component
243 49
 #
@@ -292,113 +98,18 @@ named  '%s' that is a '%s' plugin"
292 98
             raise SettingsValidationError(msg)
293 99
     return value
294 100
 
295
-def custom_list_validator(value, validator_name, validator_kwargs = None):
296
-    validator_kwargs = dict() if validator_kwargs is None else validator_kwargs
297
-    validator = SettingValidator(validator_name, **validator_kwargs)
298
-    for item in value.split():
299
-        validator(item)
300
-    return value.split()
301 101
 
302
-#
303
-#   Default validators registration
304
-#
305 102
 
306 103
 SettingValidator.register_validator(
307 104
     'plugin',
308 105
     plugin_validator,
309 106
     'plugin name & type validator')
310 107
 
311
-SettingValidator.register_validator(
312
-    'custom_list',
313
-    custom_list_validator,
314
-    'A list validator that takes a "validator_name" as argument')
315
-
316
-SettingValidator.register_validator(
317
-    'dummy',
318
-    lambda value:value,
319
-    'Validate anything')
320
-
321
-SettingValidator.register_validator(
322
-    'none',
323
-    none_val,
324
-    'Validate None')
325
-
326
-SettingValidator.register_validator(
327
-    'string',
328
-    str_val,
329
-    'Validate string values')
330
-
331
-SettingValidator.register_validator(
332
-    'strip',
333
-    str.strip,
334
-    'String trim')
335
-
336
-SettingValidator.register_validator(
337
-    'int',
338
-    int_val,
339
-    'Integer value validator')
340
-
341
-SettingValidator.register_validator(
342
-    'bool',
343
-    boolean_val,
344
-    'Boolean value validator')
345
-
346
-SettingValidator.register_validator(
347
-    'errfile',
348
-    file_err_output,
349
-    'Error output file validator (return stderr if filename is "-")')
350
-
351
-SettingValidator.register_validator(
352
-    'directory',
353
-    directory_val,
354
-    'Directory path validator')
355
-
356
-SettingValidator.register_validator(
357
-    'loglevel',
358
-    loglevel_val,
359
-    'Loglevel validator')
360
-
361
-SettingValidator.register_validator(
362
-    'path',
363
-    path_val,
364
-    'path validator')
365
-
366
-SettingValidator.register_validator(
367
-    'host',
368
-    host_val,
369
-    'host validator')
370
-
371 108
 SettingValidator.register_validator(
372 109
     'emfield',
373 110
     emfield_val,
374 111
     'EmField name validator')
375 112
 
376
-SettingValidator.register_validator(
377
-    'regex',
378
-    regex_val,
379
-    'RegEx name validator (take re as argument)')
380
-
381
-SettingValidator.create_list_validator(
382
-    'list',
383
-    SettingValidator('strip'),
384
-    description = "Simple list validator. Validate a list of values separated by ','",
385
-    separator = ',')
386
-
387
-SettingValidator.create_list_validator(
388
-    'directory_list',
389
-    SettingValidator('directory'),
390
-    description = "Validator for a list of directory path separated with ','",
391
-    separator = ',')
392
-SettingValidator.create_write_list_validator(
393
-    'write_list',
394
-    SettingValidator('directory'),
395
-    description = "Validator for an array of values which will be set in a string, separated by ','",
396
-    separator = ',')
397
-SettingValidator.create_re_validator(
398
-    r'^https?://[^\./]+.[^\./]+/?.*$',
399
-    'http_url',
400
-    'Url validator')
401
-
402 113
 #
403 114
 #   Lodel 2 configuration specification
404 115
 #

+ 6
- 0
lodel/validator/__init__.py View File

@@ -0,0 +1,6 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+## @package lodel.validator Lodel2 validator package
4
+#
5
+
6
+

+ 339
- 0
lodel/validator/validator.py View File

@@ -0,0 +1,339 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import sys
4
+import os.path
5
+import re
6
+import socket
7
+import inspect
8
+import copy
9
+
10
+from lodel.context import LodelContext
11
+LodelContext.expose_modules(globals(), {
12
+    'lodel.exceptions': ['LodelException', 'LodelExceptions',
13
+        'LodelFatalError', 'FieldValidationError']})
14
+
15
+## @package lodel.settings.validator Lodel2 settings validators/cast module
16
+#
17
+# Validator are registered in the Validator class.
18
+# @note to get a list of registered default validators just run
19
+# <pre>$ python scripts/settings_validator.py</pre>
20
+
21
+##@brief Exception class that should be raised when a validation fails
22
+class ValidationError(Exception):
23
+    pass
24
+
25
+##@brief Handles settings validators
26
+#
27
+# Class instance are callable objects that takes a value argument (the value to validate). It raises
28
+# a ValidationError if validation fails, else it returns a properly
29
+# casted value.
30
+#@todo implement an IP validator and use it in multisite confspec
31
+class Validator(object):
32
+    
33
+    _validators = dict()
34
+    _description = dict()
35
+    
36
+    ##@brief Instanciate a validator
37
+    #@param name str : validator name
38
+    #@param none_is_valid bool : if True None will be validated
39
+    #@param **kwargs : more arguement for the validator
40
+    def __init__(self, name, none_is_valid = False, **kwargs):
41
+        if name is not None and name not in self._validators:
42
+            raise LodelFatalError("No validator named '%s'" % name)
43
+        self.__none_is_valid = none_is_valid
44
+        self.__name = name
45
+        self._opt_args = kwargs
46
+
47
+    ##@brief Call the validator
48
+    # @param value *
49
+    # @return properly casted value
50
+    # @throw ValidationError
51
+    def __call__(self, value):
52
+        if self.__none_is_valid and value is None:
53
+            return None
54
+        try:
55
+            ret = self._validators[self.__name](value, **self._opt_args)
56
+            return ret
57
+        except Exception as e:
58
+            raise ValidationError(e)
59
+    
60
+    ##@brief Register a new validator
61
+    # @param name str : validator name
62
+    # @param callback callable : the function that will validate a value
63
+    # @param description str
64
+    @classmethod
65
+    def register_validator(cls, name, callback, description=None):
66
+        if name in cls._validators:
67
+            raise NameError("A validator named '%s' allready exists" % name)
68
+        # Broken test for callable
69
+        if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
70
+            raise TypeError("Callable expected but got %s" % type(callback))
71
+        cls._validators[name] = callback
72
+        cls._description[name] = description
73
+    
74
+    ##@brief Get the validator list associated with description
75
+    @classmethod
76
+    def validators_list(cls):
77
+        return copy.copy(cls._description)
78
+
79
+    ##@brief Create and register a list validator
80
+    # @param elt_validator callable : The validator that will be used for validate each elt value
81
+    # @param validator_name str
82
+    # @param description None | str
83
+    # @param separator str : The element separator
84
+    # @return A Validator instance
85
+    @classmethod
86
+    def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
87
+        def list_validator(value):
88
+            res = list()
89
+            errors = list()
90
+            for elt in value.split(separator):
91
+                elt = elt_validator(elt)
92
+                if len(elt) > 0:
93
+                    res.append(elt)
94
+            return res
95
+        description = "Convert value to an array" if description is None else description
96
+        cls.register_validator(
97
+                                validator_name,
98
+                                list_validator,
99
+                                description)
100
+        return cls(validator_name)
101
+ 
102
+    ##@brief Create and register a list validator which reads an array and returns a string
103
+    # @param elt_validator callable : The validator that will be used for validate each elt value
104
+    # @param validator_name str
105
+    # @param description None | str
106
+    # @param separator str : The element separator
107
+    # @return A Validator instance
108
+    @classmethod
109
+    def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
110
+        def write_list_validator(value):
111
+            res = ''
112
+            errors = list()
113
+            for elt in value:
114
+                res += elt_validator(elt) + ','
115
+            return res[:len(res)-1]
116
+        description = "Convert value to a string" if description is None else description
117
+        cls.register_validator(
118
+                                validator_name,
119
+                                write_list_validator,
120
+                                description)
121
+        return cls(validator_name)
122
+    
123
+    ##@brief Create and register a regular expression validator
124
+    # @param pattern str : regex pattern
125
+    # @param validator_name str : The validator name
126
+    # @param description str : Validator description
127
+    # @return a Validator instance
128
+    @classmethod
129
+    def create_re_validator(cls, pattern, validator_name, description = None):
130
+        def re_validator(value):
131
+            if not re.match(pattern, value):
132
+                raise ValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
133
+            return value
134
+        #registering the validator
135
+        cls.register_validator(
136
+                                validator_name,
137
+                                re_validator,
138
+                                ("Match value to '%s'" % pattern) if description is None else description)
139
+        return cls(validator_name)
140
+
141
+    
142
+    ## @return a list of registered validators
143
+    @classmethod
144
+    def validators_list_str(cls):
145
+        result = ''
146
+        for name in sorted(cls._validators.keys()):
147
+            result += "\t%016s" % name
148
+            if name in cls._description and cls._description[name] is not None:
149
+                result += ": %s" % cls._description[name]
150
+            result += "\n"
151
+        return result
152
+
153
+##@brief Integer value validator callback
154
+def int_val(value):
155
+    return int(value)
156
+
157
+##@brief Output file validator callback
158
+# @return A file object (if filename is '-' return sys.stderr)
159
+def file_err_output(value):
160
+    if not isinstance(value, str):
161
+        raise ValidationError("A string was expected but got '%s' " % value)
162
+    if value == '-':
163
+        return None
164
+    return value
165
+
166
+##@brief Boolean value validator callback
167
+def boolean_val(value):
168
+    if isinstance(value, bool):
169
+        return value
170
+    if value.strip().lower() == 'true' or value.strip() == '1':
171
+        value = True
172
+    elif value.strip().lower() == 'false' or value.strip() == '0':
173
+        value = False
174
+    else:
175
+        raise ValidationError("A boolean was expected but got '%s' " % value)
176
+    return bool(value)
177
+
178
+##@brief Validate a directory path
179
+def directory_val(value):
180
+    res = Validator('strip')(value)
181
+    if not os.path.isdir(res):
182
+        raise ValidationError("Folowing path don't exists or is not a directory : '%s'"%res)
183
+    return res
184
+
185
+##@brief Validate a loglevel value
186
+def loglevel_val(value):
187
+    valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
188
+    if value.upper() not in valids:
189
+        raise ValidationError(
190
+                "The value '%s' is not a valid loglevel" % value)
191
+    return value.upper()
192
+
193
+##@brief Validate a path
194
+def path_val(value):
195
+    if value is None or not os.path.exists(value):
196
+        raise ValidationError(
197
+                "path '%s' doesn't exists" % value)
198
+    return value
199
+
200
+##@brief Validate None
201
+def none_val(value):
202
+    if value is None:
203
+        return None
204
+    raise ValidationError("This settings cannot be set in configuration file")
205
+
206
+##@brief Validate a string
207
+def str_val(value):
208
+    try:
209
+        return str(value)
210
+    except Exception as e:
211
+        raise ValidationError("Not able to convert value to string : " + str(e))
212
+
213
+##@brief Validate using a regex
214
+def regex_val(value, pattern):
215
+    if re.match(pattern, value) is None:
216
+        raise ValidationError("The value '%s' is not validated by : \
217
+r\"%s\"" %(value, pattern))
218
+    return value
219
+
220
+##@brief Validate a hostname (ipv4 or ipv6)
221
+def host_val(value):
222
+    if value == 'localhost':
223
+        return value
224
+    ok = False
225
+    try:
226
+        socket.inet_aton(value)
227
+        return value
228
+    except (TypeError,OSError):
229
+        pass
230
+    try:
231
+        socket.inet_pton(socket.AF_INET6, value)
232
+        return value
233
+    except (TypeError,OSError):
234
+        pass
235
+    try:
236
+        socket.getaddrinfo(value, 80)
237
+        return value
238
+    except (TypeError,socket.gaierror):
239
+        msg = "The value '%s' is not a valid host"
240
+        raise ValidationError(msg % value)
241
+
242
+def custom_list_validator(value, validator_name, validator_kwargs = None):
243
+    validator_kwargs = dict() if validator_kwargs is None else validator_kwargs
244
+    validator = Validator(validator_name, **validator_kwargs)
245
+    for item in value.split():
246
+        validator(item)
247
+    return value.split()
248
+
249
+#
250
+#   Default validators registration
251
+#
252
+
253
+Validator.register_validator(
254
+    'custom_list',
255
+    custom_list_validator,
256
+    'A list validator that takes a "validator_name" as argument')
257
+
258
+Validator.register_validator(
259
+    'dummy',
260
+    lambda value:value,
261
+    'Validate anything')
262
+
263
+Validator.register_validator(
264
+    'none',
265
+    none_val,
266
+    'Validate None')
267
+
268
+Validator.register_validator(
269
+    'string',
270
+    str_val,
271
+    'Validate string values')
272
+
273
+Validator.register_validator(
274
+    'strip',
275
+    str.strip,
276
+    'String trim')
277
+
278
+Validator.register_validator(
279
+    'int',
280
+    int_val,
281
+    'Integer value validator')
282
+
283
+Validator.register_validator(
284
+    'bool',
285
+    boolean_val,
286
+    'Boolean value validator')
287
+
288
+Validator.register_validator(
289
+    'errfile',
290
+    file_err_output,
291
+    'Error output file validator (return stderr if filename is "-")')
292
+
293
+Validator.register_validator(
294
+    'directory',
295
+    directory_val,
296
+    'Directory path validator')
297
+
298
+Validator.register_validator(
299
+    'loglevel',
300
+    loglevel_val,
301
+    'Loglevel validator')
302
+
303
+Validator.register_validator(
304
+    'path',
305
+    path_val,
306
+    'path validator')
307
+
308
+Validator.register_validator(
309
+    'host',
310
+    host_val,
311
+    'host validator')
312
+
313
+Validator.register_validator(
314
+    'regex',
315
+    regex_val,
316
+    'RegEx name validator (take re as argument)')
317
+
318
+Validator.create_list_validator(
319
+    'list',
320
+    Validator('strip'),
321
+    description = "Simple list validator. Validate a list of values separated by ','",
322
+    separator = ',')
323
+
324
+Validator.create_list_validator(
325
+    'directory_list',
326
+    Validator('directory'),
327
+    description = "Validator for a list of directory path separated with ','",
328
+    separator = ',')
329
+
330
+Validator.create_write_list_validator(
331
+    'write_list',
332
+    Validator('directory'),
333
+    description = "Validator for an array of values which will be set in a string, separated by ','",
334
+    separator = ',')
335
+
336
+Validator.create_re_validator(
337
+    r'^https?://[^\./]+.[^\./]+/?.*$',
338
+    'http_url',
339
+    'Url validator')

+ 341
- 0
lodel/validator/validator.py.sav View File

@@ -0,0 +1,341 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import sys
4
+import os.path
5
+import re
6
+import socket
7
+import inspect
8
+import copy
9
+
10
+from lodel.context import LodelContext
11
+LodelContext.expose_modules(globals(), {
12
+    'lodel.exceptions': ['LodelException', 'LodelExceptions',
13
+        'LodelFatalError', 'FieldValidationError']})
14
+
15
+## @package lodel.settings.validator Lodel2 settings validators/cast module
16
+#
17
+# Validator are registered in the Validator class.
18
+# @note to get a list of registered default validators just run
19
+# <pre>$ python scripts/settings_validator.py</pre>
20
+
21
+##@brief Exception class that should be raised when a validation fails
22
+class ValidationError(Exception):
23
+    pass
24
+
25
+##@brief Handles settings validators
26
+#
27
+# Class instance are callable objects that takes a value argument (the value to validate). It raises
28
+# a ValidationError if validation fails, else it returns a properly
29
+# casted value.
30
+
31
+class Validator(object):
32
+    
33
+    _validators = dict()
34
+    _description = dict()
35
+    
36
+    ##@brief Instanciate a validator
37
+    #@param name str : validator name
38
+    #@param none_is_valid bool : if True None will be validated
39
+    #@param **kwargs : more arguement for the validator
40
+    def __init__(self, name, none_is_valid = False, **kwargs):
41
+        if name is not None and name not in self._validators:
42
+            raise LodelFatalError("No validator named '%s'" % name)
43
+        self.__none_is_valid = none_is_valid
44
+        self.__name = name
45
+        self._opt_args = kwargs
46
+
47
+    ##@brief Call the validator
48
+    # @param value *
49
+    # @return properly casted value
50
+    # @throw ValidationError
51
+    def __call__(self, value):
52
+        if self.__none_is_valid and value is None:
53
+            return None
54
+        try:
55
+            ret = self._validators[self.__name](value, **self._opt_args)
56
+            return ret
57
+        except Exception as e:
58
+            raise ValidationError(e)
59
+    
60
+    ##@brief Register a new validator
61
+    # @param name str : validator name
62
+    # @param callback callable : the function that will validate a value
63
+    # @param description str
64
+    @classmethod
65
+    def register_validator(cls, name, callback, description=None):
66
+        if name in cls._validators:
67
+            raise NameError("A validator named '%s' allready exists" % name)
68
+        # Broken test for callable
69
+        if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
70
+            raise TypeError("Callable expected but got %s" % type(callback))
71
+        cls._validators[name] = callback
72
+        cls._description[name] = description
73
+    
74
+    ##@brief Get the validator list associated with description
75
+    @classmethod
76
+    def validators_list(cls):
77
+        return copy.copy(cls._description)
78
+
79
+    ##@brief Create and register a list validator
80
+    # @param elt_validator callable : The validator that will be used for validate each elt value
81
+    # @param validator_name str
82
+    # @param description None | str
83
+    # @param separator str : The element separator
84
+    # @return A SettingValidator instance
85
+    @classmethod
86
+    def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
87
+        def list_validator(value):
88
+            res = list()
89
+            errors = list()
90
+            for elt in value.split(separator):
91
+                elt = elt_validator(elt)
92
+                if len(elt) > 0:
93
+                    res.append(elt)
94
+            return res
95
+        description = "Convert value to an array" if description is None else description
96
+        cls.register_validator(
97
+                                validator_name,
98
+                                list_validator,
99
+                                description)
100
+        return cls(validator_name)
101
+ 
102
+    ##@brief Create and register a list validator which reads an array and returns a string
103
+    # @param elt_validator callable : The validator that will be used for validate each elt value
104
+    # @param validator_name str
105
+    # @param description None | str
106
+    # @param separator str : The element separator
107
+    # @return A SettingValidator instance
108
+    @classmethod
109
+    def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
110
+        def write_list_validator(value):
111
+            res = ''
112
+            errors = list()
113
+            for elt in value:
114
+                res += elt_validator(elt) + ','
115
+            return res[:len(res)-1]
116
+        description = "Convert value to a string" if description is None else description
117
+        cls.register_validator(
118
+                                validator_name,
119
+                                write_list_validator,
120
+                                description)
121
+        return cls(validator_name)
122
+    
123
+    ##@brief Create and register a regular expression validator
124
+    # @param pattern str : regex pattern
125
+    # @param validator_name str : The validator name
126
+    # @param description str : Validator description
127
+    # @return a SettingValidator instance
128
+    @classmethod
129
+    def create_re_validator(cls, pattern, validator_name, description = None):
130
+        def re_validator(value):
131
+            if not re.match(pattern, value):
132
+                raise ValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
133
+            return value
134
+        #registering the validator
135
+        cls.register_validator(
136
+                                validator_name,
137
+                                re_validator,
138
+                                ("Match value to '%s'" % pattern) if description is None else description)
139
+        return cls(validator_name)
140
+
141
+    
142
+    ## @return a list of registered validators
143
+    @classmethod
144
+    def validators_list_str(cls):
145
+        result = ''
146
+        for name in sorted(cls._validators.keys()):
147
+            result += "\t%016s" % name
148
+            if name in cls._description and cls._description[name] is not None:
149
+                result += ": %s" % cls._description[name]
150
+            result += "\n"
151
+        return result
152
+
153
+##@brief Integer value validator callback
154
+def int_val(value):
155
+    return int(value)
156
+
157
+##@brief Output file validator callback
158
+# @return A file object (if filename is '-' return sys.stderr)
159
+def file_err_output(value):
160
+    if not isinstance(value, str):
161
+        raise ValidationError("A string was expected but got '%s' " % value)
162
+    if value == '-':
163
+        return None
164
+    return value
165
+
166
+##@brief Boolean value validator callback
167
+def boolean_val(value):
168
+    if isinstance(value, bool):
169
+        return value
170
+    if value.strip().lower() == 'true' or value.strip() == '1':
171
+        value = True
172
+    elif value.strip().lower() == 'false' or value.strip() == '0':
173
+        value = False
174
+    else:
175
+        raise ValidationError("A boolean was expected but got '%s' " % value)
176
+    return bool(value)
177
+
178
+##@brief Validate a directory path
179
+def directory_val(value):
180
+    res = SettingValidator('strip')(value)
181
+    if not os.path.isdir(res):
182
+        raise SettingsValidationError("Following path doesn't exist or is not a directory : '%s'"%res)
183
+    return res
184
+
185
+##@brief Validate a loglevel value
186
+def loglevel_val(value):
187
+    valids = ['DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL']
188
+    if value.upper() not in valids:
189
+        raise SettingsValidationError(
190
+                "The value '%s' is not a valid loglevel" % value)
191
+    return value.upper()
192
+
193
+##@brief Validate a path
194
+def path_val(value):
195
+    if value is None or not os.path.exists(value):
196
+        raise ValidationError(
197
+                "path '%s' doesn't exists" % value)
198
+    return value
199
+
200
+##@brief Validate None
201
+def none_val(value):
202
+    if value is None:
203
+        return None
204
+    raise ValidationError("None value is required to be validated")
205
+
206
+##@brief Validate a string
207
+def str_val(value):
208
+    try:
209
+        return str(value)
210
+    except Exception as e:
211
+        raise ValidationError("Not able to convert value to string : " + str(e))
212
+
213
+##@brief Validate using a regex
214
+def regex_val(value, pattern):
215
+    if re.match(pattern, value) is None:
216
+        raise ValidationError("The value '%s' is not validated by : \
217
+r\"%s\"" %(value, pattern))
218
+    return value
219
+
220
+##@brief Validate a hostname (ipv4 or ipv6)
221
+def host_val(value):
222
+    if value == 'localhost':
223
+        return value
224
+    ok = False
225
+    try:
226
+        socket.inet_aton(value)
227
+        return value
228
+    except (TypeError,OSError):
229
+        pass
230
+    try:
231
+        socket.inet_pton(socket.AF_INET6, value)
232
+        return value
233
+    except (TypeError,OSError):
234
+        pass
235
+    try:
236
+        socket.getaddrinfo(value, 80)
237
+        return value
238
+    except (TypeError,socket.gaierror):
239
+        msg = "The value '%s' is not a valid host"
240
+        raise ValidationError(msg % value)
241
+
242
+
243
+def custom_list_validator(value, validator_name, validator_kwargs = None):
244
+    validator_kwargs = dict() if validator_kwargs is None else validator_kwargs
245
+    validator = Validator(validator_name, **validator_kwargs)
246
+    for item in value.split():
247
+        validator(item)
248
+    return value.split()
249
+
250
+#
251
+#   Default validators registration
252
+#
253
+
254
+
255
+Validator.register_validator(
256
+    'custom_list',
257
+    custom_list_validator,
258
+    'A list validator that takes a "validator_name" as argument')
259
+
260
+Validator.register_validator(
261
+    'dummy',
262
+    lambda value:value,
263
+    'Validate anything')
264
+
265
+Validator.register_validator(
266
+    'none',
267
+    none_val,
268
+    'Validate None')
269
+
270
+Validator.register_validator(
271
+    'string',
272
+    str_val,
273
+    'Validate string values')
274
+
275
+Validator.register_validator(
276
+    'strip',
277
+    str.strip,
278
+    'String trim')
279
+
280
+Validator.register_validator(
281
+    'int',
282
+    int_val,
283
+    'Integer value validator')
284
+
285
+Validator.register_validator(
286
+    'bool',
287
+    boolean_val,
288
+    'Boolean value validator')
289
+
290
+Validator.register_validator(
291
+    'errfile',
292
+    file_err_output,
293
+    'Error output file validator (return stderr if filename is "-")')
294
+
295
+Validator.register_validator(
296
+    'directory',
297
+    directory_val,
298
+    'Directory path validator')
299
+
300
+Validator.register_validator(
301
+    'loglevel',
302
+    loglevel_val,
303
+    'Loglevel validator')
304
+
305
+Validator.register_validator(
306
+    'path',
307
+    path_val,
308
+    'path validator')
309
+
310
+Validator.register_validator(
311
+    'host',
312
+    host_val,
313
+    'host validator')
314
+
315
+Validator.register_validator(
316
+    'regex',
317
+    regex_val,
318
+    'RegEx name validator (take re as argument)')
319
+
320
+Validator.create_list_validator(
321
+    'list',
322
+    Validator('strip'),
323
+    description = "Simple list validator. Validate a list of values separated by ','",
324
+    separator = ',')
325
+
326
+Validator.create_list_validator(
327
+    'directory_list',
328
+    Validator('directory'),
329
+    description = "Validator for a list of directory path separated with ','",
330
+    separator = ',')
331
+    
332
+Validator.create_write_list_validator(
333
+    'write_list',
334
+    Validator('directory'),
335
+    description = "Validator for an array of values which will be set in a string, separated by ','",
336
+    separator = ',')
337
+    
338
+Validator.create_re_validator(
339
+    r'^https?://[^\./]+.[^\./]+/?.*$',
340
+    'http_url',
341
+    'Url validator')

Loading…
Cancel
Save