jsonVerify.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. ########################################################################
  2. # Searx-qt - Lightweight desktop application for SearX.
  3. # Copyright (C) 2020 CYBERDEViL
  4. #
  5. # This file is part of Searx-qt.
  6. #
  7. # Searx-qt is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Searx-qt is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. from searxqt.core import log
  22. class ValueBase:
  23. """ Base for evaluatable data type(s).
  24. """
  25. def evaluate(self, valueType):
  26. return False
  27. class Value(ValueBase):
  28. """ Evaluatable data type.
  29. """
  30. def __init__(self, dataType):
  31. """
  32. @param dataType: The data type this should match.
  33. @type dataType: type
  34. """
  35. self.__dataType = dataType
  36. def __repr__(self):
  37. return str(self)
  38. def __str__(self):
  39. return str(self.__dataType)
  40. def evaluate(self, valueType):
  41. if valueType is self.__dataType:
  42. return True
  43. return False
  44. class MultiValue(ValueBase):
  45. """ For when multiple data types may be valid.
  46. """
  47. def __init__(self, dataTypes):
  48. """
  49. @param dataTypes: A tuple with one or more acceptable data types.
  50. @type dataTypes: tuple(type, ..)
  51. """
  52. self.__dataTypes = dataTypes
  53. def __repr__(self):
  54. return str(self)
  55. def __str__(self):
  56. return "({0})".format(
  57. ", ".join([str(dataType) for dataType in self.__dataTypes])
  58. )
  59. def evaluate(self, valueType):
  60. if valueType in self.__dataTypes:
  61. return True
  62. return False
  63. class IgnoreValue:
  64. """ Use a instance of this class to ignore a value.
  65. """
  66. pass
  67. NoneType = type(None)
  68. def verifyStructure(structure, data, path="root"):
  69. """
  70. - Verify data types of defined data structure.
  71. - Warn on unknown keys in dict.
  72. @param structure: Expected data structure
  73. @type structure: any
  74. @param data: Data to compare with the expected structure.
  75. @type data: any
  76. @param path: This is only used to keep track of where a issue occured.
  77. @type path: str
  78. @return: Verification status and error message.
  79. @rtype: tuple(bool, str)
  80. """
  81. dataType = type(data)
  82. structureType = type(structure)
  83. # Verify if the current data has the expected type.
  84. if isinstance(structure, ValueBase):
  85. # Multiple types may be valid.
  86. if not structure.evaluate(dataType):
  87. error = (
  88. "Mismatched! (1) Expected a `{0}` but got `{1}` for {2}"
  89. ).format(str(structure), str(dataType), path)
  90. return (False, error)
  91. elif structureType is IgnoreValue:
  92. # Struct says ignore this check.
  93. return (True, "")
  94. elif dataType != structureType:
  95. # Data value isn't of expected type.
  96. error = (
  97. "Mismatched! (2) Expected a `{0}` but got `{1}` for {2}"
  98. ).format(structureType, dataType, path)
  99. return (False, error)
  100. # Recurse iterable objects.
  101. if dataType is dict:
  102. if not data:
  103. # Empty data dict, nothing to evaluate.
  104. return (True, "")
  105. elif not structure:
  106. # Empty dict structure; skip check.
  107. return (True, "")
  108. if "" in structure:
  109. # The key is variabble
  110. for key in data:
  111. verified, error = verifyStructure(
  112. structure[""],
  113. data[key],
  114. path="{0}['{1}']".format(path, key)
  115. )
  116. if not verified:
  117. return (False, error)
  118. else:
  119. # Constant key
  120. for key in data:
  121. if key not in structure:
  122. log.warning("Unknown key", key, type(data[key]), path)
  123. continue
  124. verified, error = verifyStructure(
  125. structure[key],
  126. data[key],
  127. path="{0}['{1}']".format(path, key)
  128. )
  129. if not verified:
  130. return (False, error)
  131. elif dataType is list:
  132. if not data:
  133. # Empty data list, nothing to evaluate.
  134. return (True, "")
  135. elif not structure:
  136. # Empty list structure; skip check.
  137. return (True, "")
  138. structLen = len(structure)
  139. if structLen == 1:
  140. # Repeated match
  141. index = 0
  142. for value in data:
  143. verified, error = verifyStructure(
  144. structure[0],
  145. value,
  146. path="{0}['{1}']".format(path, index)
  147. )
  148. if not verified:
  149. return (False, error)
  150. index += 1
  151. elif structLen != len(data):
  152. # Mismatch!
  153. return (
  154. False,
  155. (
  156. "Expected a fixed length of {0} elements but got a length"
  157. " of {1} elements instead for path: {2}"
  158. ).format(structLen, len(data), path)
  159. )
  160. else:
  161. # Structure within list (fixed length)
  162. index = 0
  163. for value in structure:
  164. verified, error = verifyStructure(
  165. value,
  166. data[index],
  167. path="{0}['{1}']".format(path, index)
  168. )
  169. if not verified:
  170. return (False, error)
  171. index += 1
  172. return (True, "")