Açıklama Yok
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 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. class SettingsValidationError(Exception):
  11. pass
  12. ## @brief Handles settings validators
  13. #
  14. # Class instance are callable objects that takes a value argument (the value to validate). It raises
  15. # a SettingsValidationError if validation fails, else it returns a properly
  16. # casted value.
  17. class SettingValidator(object):
  18. _validators = dict()
  19. _description = dict()
  20. ## @brief Instanciate a validator
  21. def __init__(self, name, none_is_valid = False):
  22. if name is not None and name not in self._validators:
  23. raise NameError("No validator named '%s'" % name)
  24. self.__name = name
  25. ## @brief Call the validator
  26. # @param value *
  27. # @return properly casted value
  28. # @throw SettingsValidationError
  29. def __call__(self, value):
  30. if self.__name is None:
  31. return value
  32. try:
  33. return self._validators[self.__name](value)
  34. except Exception as e:
  35. raise SettingsValidationError(e)
  36. ## @brief Register a new validator
  37. # @param name str : validator name
  38. # @param callback callable : the function that will validate a value
  39. @classmethod
  40. def register_validator(cls, name, callback, description=None):
  41. if name in cls._validators:
  42. raise NameError("A validator named '%s' allready exists" % name)
  43. # Broken test for callable
  44. if not inspect.isfunction(callback) and not inspect.ismethod(callback) and not hasattr(callback, '__call__'):
  45. raise TypeError("Callable expected but got %s" % type(callback))
  46. cls._validators[name] = callback
  47. cls._description[name] = description
  48. ## @brief Get the validator list associated with description
  49. @classmethod
  50. def validators_list(cls):
  51. return copy.copy(cls._description)
  52. ## @brief Create and register a list validator
  53. # @param elt_validator callable : The validator that will be used for validate each elt value
  54. # @param validator_name str
  55. # @param description None | str
  56. # @param separator str : The element separator
  57. # @return A SettingValidator instance
  58. @classmethod
  59. def create_list_validator(cls, validator_name, elt_validator, description = None, separator = ','):
  60. def list_validator(value):
  61. res = list()
  62. errors = list()
  63. for elt in value.split(separator):
  64. res.append(elt_validator(elt))
  65. return res
  66. description = "Convert value to an array" if description is None else description
  67. cls.register_validator(
  68. validator_name,
  69. list_validator,
  70. description)
  71. return cls(validator_name)
  72. ## @brief Create and register a regular expression validator
  73. # @param pattern str : regex pattern
  74. # @param validator_name str : The validator name
  75. # @param description str : Validator description
  76. # @return a SettingValidator instance
  77. @classmethod
  78. def create_re_validator(cls, pattern, validator_name, description = None):
  79. def re_validator(value):
  80. if not re.match(pattern, value):
  81. raise SettingsValidationError("The value '%s' doesn't match the following pattern '%s'" % pattern)
  82. return value
  83. #registering the validator
  84. cls.register_validator(
  85. validator_name,
  86. re_validator,
  87. ("Match value to '%s'" % pattern) if description is None else description)
  88. return cls(validator_name)
  89. ## @return a list of registered validators
  90. def validators_list_str(cls):
  91. result = ''
  92. for name in cls._validators:
  93. result += "\t%s" % name
  94. if name in self._description and self._description[name] is not None:
  95. result += "\t: %s" % self._description[name]
  96. result += "\n"
  97. return result
  98. ## @brief Integer value validator callback
  99. def int_val(value):
  100. return int(value)
  101. ## @brief Output file validator callback
  102. # @return A file object (if filename is '-' return sys.stderr)
  103. def file_err_output(value):
  104. if not isinstance(value, str):
  105. raise SettingsValidationError("A string was expected but got '%s' " % value)
  106. if value == '-':
  107. return sys.stderr
  108. return value
  109. ## @brief Boolean value validator callback
  110. def boolean_val(value):
  111. if not (value is True) and not (value is False):
  112. raise SettingsValidationError("A boolean was expected but got '%s' " % value)
  113. return bool(value)
  114. def directory_val(value):
  115. res = SettingValidator('strip')(value)
  116. if not os.path.isdir(res):
  117. raise SettingsValidationError("Folowing path don't exists or is not a directory : '%s'"%res)
  118. return res
  119. def loglevel_val(value):
  120. valids = ['DEBUG', 'INFO', 'SECURITY', 'ERROR', 'CRITICAL']
  121. if value.upper() not in valids:
  122. raise SettingsValidationError("The value '%s' is not a valid loglevel")
  123. #
  124. # Default validators registration
  125. #
  126. SettingValidator.register_validator(
  127. 'strip',
  128. str.strip,
  129. 'String trim')
  130. SettingValidator.register_validator(
  131. 'int',
  132. int_val,
  133. 'Integer value validator')
  134. SettingValidator.register_validator(
  135. 'bool',
  136. boolean_val,
  137. 'Boolean value validator')
  138. SettingValidator.register_validator(
  139. 'errfile',
  140. file_err_output,
  141. 'Error output file validator (return stderr if filename is "-")')
  142. SettingValidator.register_validator(
  143. 'directory',
  144. directory_val,
  145. 'Directory path validator')
  146. SettingValidator.register_validator(
  147. 'loglevel',
  148. loglevel_val,
  149. 'Loglevel validator')
  150. SettingValidator.create_list_validator(
  151. 'list',
  152. SettingValidator('strip'),
  153. description = "Simple list validator. Validate a list of values separated by ','",
  154. separator = ',')
  155. SettingValidator.create_list_validator(
  156. 'directory_list',
  157. SettingValidator('directory'),
  158. description = "Validator for a list of directory path separated with ','",
  159. separator = ',')
  160. SettingValidator.create_re_validator(
  161. r'^https?://[^\./]+.[^\./]+/?.*$',
  162. 'http_url',
  163. 'Url validator')
  164. #
  165. # Lodel 2 configuration specification
  166. #
  167. ## @brief Global specifications for lodel2 settings
  168. LODEL2_CONF_SPECS = {
  169. 'lodel2': {
  170. 'debug': ( True,
  171. SettingValidator('bool')),
  172. 'plugins': ( "",
  173. SettingValidator('list')),
  174. },
  175. 'lodel2.logging.*' : {
  176. 'level': ( 'ERROR',
  177. SettingValidator('loglevel')),
  178. 'context': ( False,
  179. SettingValidator('bool')),
  180. 'filename': ( None,
  181. SettingValidator('errfile', none_is_valid = True)),
  182. 'backupCount': ( None,
  183. SettingValidator('int', none_is_valid = True)),
  184. 'maxBytes': ( None,
  185. SettingValidator('int', none_is_valid = True)),
  186. }
  187. }