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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

59 statements  

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 

82 _type_checkers: HashTrieMap[ 

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

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

85 

86 def __repr__(self): 

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

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

89 

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

91 """ 

92 Check if the instance is of the appropriate type. 

93 

94 Arguments: 

95 

96 instance: 

97 

98 The instance to check 

99 

100 type: 

101 

102 The name of the type that is expected. 

103 

104 Raises: 

105 

106 `jsonschema.exceptions.UndefinedTypeCheck`: 

107 

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

109 

110 """ 

111 try: 

112 fn = self._type_checkers[type] 

113 except KeyError: 

114 raise UndefinedTypeCheck(type) from None 

115 

116 return fn(self, instance) 

117 

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

119 """ 

120 Produce a new checker with the given type redefined. 

121 

122 Arguments: 

123 

124 type: 

125 

126 The name of the type to check. 

127 

128 fn (collections.abc.Callable): 

129 

130 A callable taking exactly two parameters - the type 

131 checker calling the function and the instance to check. 

132 The function should return true if instance is of this 

133 type and false otherwise. 

134 

135 """ 

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

137 

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

139 """ 

140 Produce a new checker with the given types redefined. 

141 

142 Arguments: 

143 

144 definitions (dict): 

145 

146 A dictionary mapping types to their checking functions. 

147 

148 """ 

149 type_checkers = self._type_checkers.update(definitions) 

150 return evolve(self, type_checkers=type_checkers) 

151 

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

153 """ 

154 Produce a new checker with the given types forgotten. 

155 

156 Arguments: 

157 

158 types: 

159 

160 the names of the types to remove. 

161 

162 Raises: 

163 

164 `jsonschema.exceptions.UndefinedTypeCheck`: 

165 

166 if any given type is unknown to this object 

167 

168 """ 

169 type_checkers = self._type_checkers 

170 for each in types: 

171 try: 

172 type_checkers = type_checkers.remove(each) 

173 except KeyError: 

174 raise UndefinedTypeCheck(each) from None 

175 return evolve(self, type_checkers=type_checkers) 

176 

177 

178draft3_type_checker = TypeChecker( 

179 { 

180 "any": is_any, 

181 "array": is_array, 

182 "boolean": is_bool, 

183 "integer": is_integer, 

184 "object": is_object, 

185 "null": is_null, 

186 "number": is_number, 

187 "string": is_string, 

188 }, 

189) 

190draft4_type_checker = draft3_type_checker.remove("any") 

191draft6_type_checker = draft4_type_checker.redefine( 

192 "integer", 

193 lambda checker, instance: ( 

194 is_integer(checker, instance) 

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

196 ), 

197) 

198draft7_type_checker = draft6_type_checker 

199draft201909_type_checker = draft7_type_checker 

200draft202012_type_checker = draft201909_type_checker