jsonVerify.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. ########################################################################
  2. # Searx-Qt - Lightweight desktop application for Searx.
  3. # Copyright (C) 2020-2022 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. f"Mismatched! (1) Expected a `{str(structure)}` but got"
  89. f" `{str(dataType)}` for {path}"
  90. )
  91. return (False, error)
  92. elif structureType is IgnoreValue:
  93. # Struct says ignore this check.
  94. return (True, "")
  95. elif dataType != structureType:
  96. # Data value isn't of expected type.
  97. error = (
  98. f"Mismatched! (2) Expected a `{structureType}` but got"
  99. f" `{dataType}` for {path}"
  100. )
  101. return (False, error)
  102. # Recurse iterable objects.
  103. if dataType is dict:
  104. if not data:
  105. # Empty data dict, nothing to evaluate.
  106. return (True, "")
  107. elif not structure:
  108. # Empty dict structure; skip check.
  109. return (True, "")
  110. if "" in structure:
  111. # The key is variabble
  112. for key in data:
  113. verified, error = verifyStructure(
  114. structure[""],
  115. data[key],
  116. path=f"{path}['{key}']"
  117. )
  118. if not verified:
  119. return (False, error)
  120. else:
  121. # Constant key
  122. for key in data:
  123. if key not in structure:
  124. log.warning(
  125. f"Unknown key '{key}' '{type(data[key])}' '{path}'"
  126. )
  127. continue
  128. verified, error = verifyStructure(
  129. structure[key],
  130. data[key],
  131. path=f"{path}['{key}']"
  132. )
  133. if not verified:
  134. return (False, error)
  135. elif dataType is list:
  136. if not data:
  137. # Empty data list, nothing to evaluate.
  138. return (True, "")
  139. elif not structure:
  140. # Empty list structure; skip check.
  141. return (True, "")
  142. structLen = len(structure)
  143. if structLen == 1:
  144. # Repeated match
  145. index = 0
  146. for value in data:
  147. verified, error = verifyStructure(
  148. structure[0],
  149. value,
  150. path=f"{path}['{index}']"
  151. )
  152. if not verified:
  153. return (False, error)
  154. index += 1
  155. elif structLen != len(data):
  156. # Mismatch!
  157. return (
  158. False,
  159. (
  160. f"Expected a fixed length of {structLen} elements but got"
  161. f" a length of {len(data)} elements instead for path:"
  162. f" {path}"
  163. )
  164. )
  165. else:
  166. # Structure within list (fixed length)
  167. index = 0
  168. for value in structure:
  169. verified, error = verifyStructure(
  170. value,
  171. data[index],
  172. path=f"{path}['{index}']"
  173. )
  174. if not verified:
  175. return (False, error)
  176. index += 1
  177. return (True, "")