Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jsonschema/_types.py: 64%

59 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

1from __future__ import annotations 

2 

3from typing import Any, Callable, Mapping 

4import numbers 

5 

6from attrs import evolve, field, frozen 

7from rpds import HashTrieMap 

8 

9from jsonschema.exceptions import UndefinedTypeCheck 

10 

11 

12# unfortunately, the type of HashTrieMap is generic, and if used as an attrs 

13# converter, the generic type is presented to mypy, which then fails to match 

14# the concrete type of a type checker mapping 

15# this "do nothing" wrapper presents the correct information to mypy 

16def _typed_map_converter( 

17 init_val: Mapping[str, Callable[[TypeChecker, Any], bool]], 

18) -> HashTrieMap[str, Callable[[TypeChecker, Any], bool]]: 

19 return HashTrieMap.convert(init_val) 

20 

21 

22def is_array(checker, instance): 

23 return isinstance(instance, list) 

24 

25 

26def is_bool(checker, instance): 

27 return isinstance(instance, bool) 

28 

29 

30def is_integer(checker, instance): 

31 # bool inherits from int, so ensure bools aren't reported as ints 

32 if isinstance(instance, bool): 

33 return False 

34 return isinstance(instance, int) 

35 

36 

37def is_null(checker, instance): 

38 return instance is None 

39 

40 

41def is_number(checker, instance): 

42 # bool inherits from int, so ensure bools aren't reported as ints 

43 if isinstance(instance, bool): 

44 return False 

45 return isinstance(instance, numbers.Number) 

46 

47 

48def is_object(checker, instance): 

49 return isinstance(instance, dict) 

50 

51 

52def is_string(checker, instance): 

53 return isinstance(instance, str) 

54 

55 

56def is_any(checker, instance): 

57 return True 

58 

59 

60@frozen(repr=False) 

61class TypeChecker: 

62 """ 

63 A :kw:`type` property checker. 

64 

65 A `TypeChecker` performs type checking for a `Validator`, converting 

66 between the defined JSON Schema types and some associated Python types or 

67 objects. 

68 

69 Modifying the behavior just mentioned by redefining which Python objects 

70 are considered to be of which JSON Schema types can be done using 

71 `TypeChecker.redefine` or `TypeChecker.redefine_many`, and types can be 

72 removed via `TypeChecker.remove`. Each of these return a new `TypeChecker`. 

73 

74 Arguments: 

75 

76 type_checkers: 

77 

78 The initial mapping of types to their checking functions. 

79 """ 

80 

81 _type_checkers: HashTrieMap[ 

82 str, Callable[[TypeChecker, Any], bool], 

83 ] = field(default=HashTrieMap(), converter=_typed_map_converter) 

84 

85 def __repr__(self): 

86 types = ", ".join(repr(k) for k in sorted(self._type_checkers)) 

87 return f"<{self.__class__.__name__} types={{{types}}}>" 

88 

89 def is_type(self, instance, type: str) -> bool: 

90 """ 

91 Check if the instance is of the appropriate type. 

92 

93 Arguments: 

94 

95 instance: 

96 

97 The instance to check 

98 

99 type: 

100 

101 The name of the type that is expected. 

102 

103 Raises: 

104 

105 `jsonschema.exceptions.UndefinedTypeCheck`: 

106 

107 if ``type`` is unknown to this object. 

108 """ 

109 try: 

110 fn = self._type_checkers[type] 

111 except KeyError: 

112 raise UndefinedTypeCheck(type) from None 

113 

114 return fn(self, instance) 

115 

116 def redefine(self, type: str, fn) -> TypeChecker: 

117 """ 

118 Produce a new checker with the given type redefined. 

119 

120 Arguments: 

121 

122 type: 

123 

124 The name of the type to check. 

125 

126 fn (collections.abc.Callable): 

127 

128 A callable taking exactly two parameters - the type 

129 checker calling the function and the instance to check. 

130 The function should return true if instance is of this 

131 type and false otherwise. 

132 """ 

133 return self.redefine_many({type: fn}) 

134 

135 def redefine_many(self, definitions=()) -> TypeChecker: 

136 """ 

137 Produce a new checker with the given types redefined. 

138 

139 Arguments: 

140 

141 definitions (dict): 

142 

143 A dictionary mapping types to their checking functions. 

144 """ 

145 type_checkers = self._type_checkers.update(definitions) 

146 return evolve(self, type_checkers=type_checkers) 

147 

148 def remove(self, *types) -> TypeChecker: 

149 """ 

150 Produce a new checker with the given types forgotten. 

151 

152 Arguments: 

153 

154 types: 

155 

156 the names of the types to remove. 

157 

158 Raises: 

159 

160 `jsonschema.exceptions.UndefinedTypeCheck`: 

161 

162 if any given type is unknown to this object 

163 """ 

164 type_checkers = self._type_checkers 

165 for each in types: 

166 try: 

167 type_checkers = type_checkers.remove(each) 

168 except KeyError: 

169 raise UndefinedTypeCheck(each) 

170 return evolve(self, type_checkers=type_checkers) 

171 

172 

173draft3_type_checker = TypeChecker( 

174 { 

175 "any": is_any, 

176 "array": is_array, 

177 "boolean": is_bool, 

178 "integer": is_integer, 

179 "object": is_object, 

180 "null": is_null, 

181 "number": is_number, 

182 "string": is_string, 

183 }, 

184) 

185draft4_type_checker = draft3_type_checker.remove("any") 

186draft6_type_checker = draft4_type_checker.redefine( 

187 "integer", 

188 lambda checker, instance: ( 

189 is_integer(checker, instance) 

190 or isinstance(instance, float) and instance.is_integer() 

191 ), 

192) 

193draft7_type_checker = draft6_type_checker 

194draft201909_type_checker = draft7_type_checker 

195draft202012_type_checker = draft201909_type_checker