No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

validator.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #-*- coding: utf-8 -*-
  2. import sys
  3. import os.path
  4. import re
  5. import inspect
  6. import copy
  7. ## @package lodel.settings.validator Lodel2 settings validators/cast module
  8. #
  9. # Validator are registered in the SettingValidator class.
  10. # @note to get a list of registered default validators just run
  11. # <pre>$ python scripts/settings_validator.py</pre>
  12. ##@brief Exception class that should be raised when a validation fails
  13. class SettingsValidationError(Exception):
  14. pass
  15. ##@brief Handles settings validators
  16. #
  17. # Class instance are callable objects that takes a value argument (the value to validate). It raises
  18. # a SettingsValidationError if validation fails, else it returns a properly
  19. # casted value.
  20. class SettingValidator(object):
  21. _validators = dict()
  22. _description = dict()
  23. ##@brief Instanciate a validator
  24. def __init__(self, name, none_is_valid = False):
  25. if name is not None and name not in self._validators:
  26. raise NameError("No validator named '%s'" % name)
  27. self.__name = name
  28. ##@brief Call the validator
  29. # @param value *
  30. # @return properly casted value
  31. # @throw SettingsValidationError
  32. def __call__(self, value):
  33. if self.__name is None:
  34. return value
  35. try:
  36. return self._validators[self.__name](value)
  37. except Exception as e:
  38. raise SettingsValidationError(e)
  39. ##@brief Register a new validator
  40. # @param name str : validator name
  41. # @param callback callable : the function that will validate a value
  42. @classmethod
  43. def register_validator(cls, name, callback, description=None):
  44. if name in cls._validators:
  45. raise NameError("A validator named '%s' allready exists" % name)
  46. # Broken test for callable
  47. if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
  48. raise TypeError("Callable expected but got %s" % type(callback))
  49. cls._validators[name] = callback
  50. cls._description[name] = description
  51. ##@brief Get the validator list associated with description
  52. @classmethod
  53. def validators_list(cls):
  54. return copy.copy(cls._description)
  55. ##@brief Create and register a list validator
  56. # @param elt_validator callable : The validator that will be used for validate each elt value
  57. # @param validator_name str
  58. # @param description None | str
  59. # @param separator str : The element separator
  60. # @return A SettingValidator instance
  61. @classmethod
  62. def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
  63. def list_validator(value):
  64. res = list()
  65. errors = list()
  66. for elt in value.split(separator):
  67. elt = elt_validator(elt)
  68. if len(elt) > 0:
  69. res.append(elt)
  70. return res
  71. description = "Convert value to an array" if description is None else description
  72. cls.register_validator(
  73. validator_name,
  74. list_validator,
  75. description)
  76. return cls(validator_name)
  77. ##@brief Create and register a list validator which reads an array and returns a string
  78. # @param elt_validator callable : The validator that will be used for validate each elt value
  79. # @param validator_name str
  80. # @param description None | str
  81. # @param separator str : The element separator
  82. # @return A SettingValidator instance
  83. @classmethod
  84. def create_write_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
  85. def write_list_validator(value):
  86. res = ''
  87. errors = list()
  88. for elt in value:
  89. res += elt_validator(elt) + ','
  90. return res[:len(res)-1]
  91. description = "Convert value to a string" if description is None else description
  92. cls.register_validator(
  93. validator_name,
  94. write_list_validator,
  95. description)
  96. return cls(validator_name)
  97. ##@brief Create and register a regular expression validator
  98. # @param pattern str : regex pattern
  99. # @param validator_name str : The validator name
  100. # @param description str : Validator description
  101. # @return a SettingValidator instance
  102. @classmethod
  103. def create_re_validator(cls, pattern, validator_name, description = None):
  104. def re_validator(value):
  105. if not re.match(pattern, value):
  106. raise SettingsValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
  107. return value
  108. #registering the validator
  109. cls.register_validator(
  110. validator_name,
  111. re_validator,
  112. ("Match value to '%s'" % pattern) if description is None else description)
  113. return cls(validator_name)
  114. ## @return a list of registered validators
  115. @classmethod
  116. def validators_list_str(cls):
  117. result = ''
  118. for name in sorted(cls._validators.keys()):
  119. result += "\t%016s" % name
  120. if name in cls._description and cls._description[name] is not None:
  121. result += ": %s" % cls._description[name]
  122. result += "\n"
  123. return result
  124. ##@brief Integer value validator callback
  125. def int_val(value):
  126. return int(value)
  127. ##@brief Output file validator callback
  128. # @return A file object (if filename is '-' return sys.stderr)
  129. def file_err_output(value):
  130. if not isinstance(value, str):
  131. raise SettingsValidationError("A string was expected but got '%s' " % value)
  132. if value == '-':
  133. return None
  134. return value
  135. ##@brief Boolean value validator callback
  136. def boolean_val(value):
  137. if value.strip().lower() == 'true' or value.strip() == '1':
  138. value = True
  139. elif value.strip().lower() == 'false' or value.strip() == '0':
  140. value = False
  141. else:
  142. raise SettingsValidationError("A boolean was expected but got '%s' " % value)
  143. return bool(value)
  144. def directory_val(value):
  145. res = SettingValidator('strip')(value)
  146. if not os.path.isdir(res):
  147. raise SettingsValidationError("Folowing path don't exists or is not a directory : '%s'"%res)
  148. return res
  149. def loglevel_val(value):
  150. valids = ['DEBUG', 'INFO', 'SECURITY', 'ERROR', 'CRITICAL']
  151. if value.upper() not in valids:
  152. raise SettingsValidationError(
  153. "The value '%s' is not a valid loglevel" % value)
  154. return value.upper()
  155. def path_val(value):
  156. if not os.path.exists(value):
  157. raise SettingsValidationError(
  158. "path '%s' doesn't exists" % value)
  159. return value
  160. def none_val(value):
  161. if value is None:
  162. return None
  163. raise SettingsValidationError("This settings cannot be set in configuration file")
  164. def str_val(value):
  165. try:
  166. return str(value)
  167. except Exception as e:
  168. raise SettingsValidationError("Not able to convert value to string : " + str(e))
  169. #
  170. # Default validators registration
  171. #
  172. SettingValidator.register_validator(
  173. 'dummy',
  174. lambda value:value,
  175. 'Validate anything')
  176. SettingValidator.register_validator(
  177. 'none',
  178. none_val,
  179. 'Validate None')
  180. SettingValidator.register_validator(
  181. 'string',
  182. str_val,
  183. 'Validate string values')
  184. SettingValidator.register_validator(
  185. 'strip',
  186. str.strip,
  187. 'String trim')
  188. SettingValidator.register_validator(
  189. 'int',
  190. int_val,
  191. 'Integer value validator')
  192. SettingValidator.register_validator(
  193. 'bool',
  194. boolean_val,
  195. 'Boolean value validator')
  196. SettingValidator.register_validator(
  197. 'errfile',
  198. file_err_output,
  199. 'Error output file validator (return stderr if filename is "-")')
  200. SettingValidator.register_validator(
  201. 'directory',
  202. directory_val,
  203. 'Directory path validator')
  204. SettingValidator.register_validator(
  205. 'loglevel',
  206. loglevel_val,
  207. 'Loglevel validator')
  208. SettingValidator.register_validator(
  209. 'path',
  210. path_val,
  211. 'path validator')
  212. SettingValidator.create_list_validator(
  213. 'list',
  214. SettingValidator('strip'),
  215. description = "Simple list validator. Validate a list of values separated by ','",
  216. separator = ',')
  217. SettingValidator.create_list_validator(
  218. 'directory_list',
  219. SettingValidator('directory'),
  220. description = "Validator for a list of directory path separated with ','",
  221. separator = ',')
  222. SettingValidator.create_write_list_validator(
  223. 'write_list',
  224. SettingValidator('directory'),
  225. description = "Validator for an array of values which will be set in a string, separated by ','",
  226. separator = ',')
  227. SettingValidator.create_re_validator(
  228. r'^https?://[^\./]+.[^\./]+/?.*$',
  229. 'http_url',
  230. 'Url validator')
  231. #
  232. # Lodel 2 configuration specification
  233. #
  234. ##@brief Global specifications for lodel2 settings
  235. LODEL2_CONF_SPECS = {
  236. 'lodel2': {
  237. 'debug': ( True,
  238. SettingValidator('bool')),
  239. 'plugins_path': ( None,
  240. SettingValidator('list')),
  241. 'plugins': ( "",
  242. SettingValidator('list')),
  243. 'sitename': ( 'noname',
  244. SettingValidator('strip')),
  245. 'lib_path': ( None,
  246. SettingValidator('path')),
  247. },
  248. 'lodel2.logging.*' : {
  249. 'level': ( 'ERROR',
  250. SettingValidator('loglevel')),
  251. 'context': ( False,
  252. SettingValidator('bool')),
  253. 'filename': ( None,
  254. SettingValidator('errfile', none_is_valid = True)),
  255. 'backupcount': ( None,
  256. SettingValidator('int', none_is_valid = True)),
  257. 'maxbytes': ( None,
  258. SettingValidator('int', none_is_valid = True)),
  259. },
  260. 'lodel2.editorialmodel': {
  261. 'emfile': ( 'em.pickle',
  262. SettingValidator('strip')),
  263. 'emtranslator': ( 'picklefile',
  264. SettingValidator('strip')),
  265. 'dyncode': ( 'leapi_dyncode.py',
  266. SettingValidator('strip')),
  267. 'groups': ( '',
  268. SettingValidator('list')),
  269. 'editormode': ( False,
  270. SettingValidator('bool')),
  271. },
  272. }