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

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

158 statements  

1from collections.abc import Mapping, MutableMapping, Sequence 

2from urllib.parse import urlsplit 

3import itertools 

4import re 

5 

6 

7class URIDict(MutableMapping): 

8 """ 

9 Dictionary which uses normalized URIs as keys. 

10 """ 

11 

12 def normalize(self, uri): 

13 return urlsplit(uri).geturl() 

14 

15 def __init__(self, *args, **kwargs): 

16 self.store = dict() 

17 self.store.update(*args, **kwargs) 

18 

19 def __getitem__(self, uri): 

20 return self.store[self.normalize(uri)] 

21 

22 def __setitem__(self, uri, value): 

23 self.store[self.normalize(uri)] = value 

24 

25 def __delitem__(self, uri): 

26 del self.store[self.normalize(uri)] 

27 

28 def __iter__(self): 

29 return iter(self.store) 

30 

31 def __len__(self): # pragma: no cover -- untested, but to be removed 

32 return len(self.store) 

33 

34 def __repr__(self): # pragma: no cover -- untested, but to be removed 

35 return repr(self.store) 

36 

37 

38class Unset: 

39 """ 

40 An as-of-yet unset attribute or unprovided default parameter. 

41 """ 

42 

43 def __repr__(self): # pragma: no cover 

44 return "<unset>" 

45 

46 

47def format_as_index(container, indices): 

48 """ 

49 Construct a single string containing indexing operations for the indices. 

50 

51 For example for a container ``bar``, [1, 2, "foo"] -> bar[1][2]["foo"] 

52 

53 Arguments: 

54 

55 container (str): 

56 

57 A word to use for the thing being indexed 

58 

59 indices (sequence): 

60 

61 The indices to format. 

62 

63 """ 

64 if not indices: 

65 return container 

66 return f"{container}[{']['.join(repr(index) for index in indices)}]" 

67 

68 

69def find_additional_properties(instance, schema): 

70 """ 

71 Return the set of additional properties for the given ``instance``. 

72 

73 Weeds out properties that should have been validated by ``properties`` and 

74 / or ``patternProperties``. 

75 

76 Assumes ``instance`` is dict-like already. 

77 """ 

78 properties = schema.get("properties", {}) 

79 patterns = "|".join(schema.get("patternProperties", {})) 

80 for property in instance: 

81 if property not in properties: 

82 if patterns and re.search(patterns, property): 

83 continue 

84 yield property 

85 

86 

87def extras_msg(extras): 

88 """ 

89 Create an error message for extra items or properties. 

90 """ 

91 verb = "was" if len(extras) == 1 else "were" 

92 return ", ".join(repr(extra) for extra in extras), verb 

93 

94 

95def ensure_list(thing): 

96 """ 

97 Wrap ``thing`` in a list if it's a single str. 

98 

99 Otherwise, return it unchanged. 

100 """ 

101 if isinstance(thing, str): 

102 return [thing] 

103 return thing 

104 

105 

106def _mapping_equal(one, two): 

107 """ 

108 Check if two mappings are equal using the semantics of `equal`. 

109 """ 

110 if len(one) != len(two): 

111 return False 

112 return all( 

113 key in two and equal(value, two[key]) 

114 for key, value in one.items() 

115 ) 

116 

117 

118def _sequence_equal(one, two): 

119 """ 

120 Check if two sequences are equal using the semantics of `equal`. 

121 """ 

122 if len(one) != len(two): 

123 return False 

124 return all(equal(i, j) for i, j in zip(one, two)) 

125 

126 

127def equal(one, two): 

128 """ 

129 Check if two things are equal evading some Python type hierarchy semantics. 

130 

131 Specifically in JSON Schema, evade `bool` inheriting from `int`, 

132 recursing into sequences to do the same. 

133 """ 

134 if one is two: 

135 return True 

136 if isinstance(one, str) or isinstance(two, str): 

137 return one == two 

138 if isinstance(one, Sequence) and isinstance(two, Sequence): 

139 return _sequence_equal(one, two) 

140 if isinstance(one, Mapping) and isinstance(two, Mapping): 

141 return _mapping_equal(one, two) 

142 return unbool(one) == unbool(two) 

143 

144 

145def unbool(element, true=object(), false=object()): 

146 """ 

147 A hack to make True and 1 and False and 0 unique for ``uniq``. 

148 """ 

149 if element is True: 

150 return true 

151 elif element is False: 

152 return false 

153 return element 

154 

155 

156def uniq(container): 

157 """ 

158 Check if all of a container's elements are unique. 

159 

160 Tries to rely on the container being recursively sortable, or otherwise 

161 falls back on (slow) brute force. 

162 """ 

163 try: 

164 sort = sorted(unbool(i) for i in container) 

165 sliced = itertools.islice(sort, 1, None) 

166 

167 for i, j in zip(sort, sliced): 

168 if equal(i, j): 

169 return False 

170 

171 except (NotImplementedError, TypeError): 

172 seen = [] 

173 for e in container: 

174 e = unbool(e) 

175 

176 for i in seen: 

177 if equal(i, e): 

178 return False 

179 

180 seen.append(e) 

181 return True 

182 

183 

184def find_evaluated_item_indexes_by_schema(validator, instance, schema): 

185 """ 

186 Get all indexes of items that get evaluated under the current schema. 

187 

188 Covers all keywords related to unevaluatedItems: items, prefixItems, if, 

189 then, else, contains, unevaluatedItems, allOf, oneOf, anyOf 

190 """ 

191 if validator.is_type(schema, "boolean"): 

192 return [] 

193 evaluated_indexes = [] 

194 

195 if "items" in schema: 

196 return list(range(len(instance))) 

197 

198 ref = schema.get("$ref") 

199 if ref is not None: 

200 resolved = validator._resolver.lookup(ref) 

201 evaluated_indexes.extend( 

202 find_evaluated_item_indexes_by_schema( 

203 validator.evolve( 

204 schema=resolved.contents, 

205 _resolver=resolved.resolver, 

206 ), 

207 instance, 

208 resolved.contents, 

209 ), 

210 ) 

211 

212 dynamicRef = schema.get("$dynamicRef") 

213 if dynamicRef is not None: 

214 resolved = validator._resolver.lookup(dynamicRef) 

215 evaluated_indexes.extend( 

216 find_evaluated_item_indexes_by_schema( 

217 validator.evolve( 

218 schema=resolved.contents, 

219 _resolver=resolved.resolver, 

220 ), 

221 instance, 

222 resolved.contents, 

223 ), 

224 ) 

225 

226 if "prefixItems" in schema: 

227 evaluated_indexes += list(range(len(schema["prefixItems"]))) 

228 

229 if "if" in schema: 

230 if validator.evolve(schema=schema["if"]).is_valid(instance): 

231 evaluated_indexes += find_evaluated_item_indexes_by_schema( 

232 validator, instance, schema["if"], 

233 ) 

234 if "then" in schema: 

235 evaluated_indexes += find_evaluated_item_indexes_by_schema( 

236 validator, instance, schema["then"], 

237 ) 

238 elif "else" in schema: 

239 evaluated_indexes += find_evaluated_item_indexes_by_schema( 

240 validator, instance, schema["else"], 

241 ) 

242 

243 for keyword in ["contains", "unevaluatedItems"]: 

244 if keyword in schema: 

245 for k, v in enumerate(instance): 

246 if validator.evolve(schema=schema[keyword]).is_valid(v): 

247 evaluated_indexes.append(k) 

248 

249 for keyword in ["allOf", "oneOf", "anyOf"]: 

250 if keyword in schema: 

251 for subschema in schema[keyword]: 

252 errs = next(validator.descend(instance, subschema), None) 

253 if errs is None: 

254 evaluated_indexes += find_evaluated_item_indexes_by_schema( 

255 validator, instance, subschema, 

256 ) 

257 

258 return evaluated_indexes 

259 

260 

261def find_evaluated_property_keys_by_schema(validator, instance, schema): 

262 """ 

263 Get all keys of items that get evaluated under the current schema. 

264 

265 Covers all keywords related to unevaluatedProperties: properties, 

266 additionalProperties, unevaluatedProperties, patternProperties, 

267 dependentSchemas, allOf, oneOf, anyOf, if, then, else 

268 """ 

269 if validator.is_type(schema, "boolean"): 

270 return [] 

271 evaluated_keys = [] 

272 

273 ref = schema.get("$ref") 

274 if ref is not None: 

275 resolved = validator._resolver.lookup(ref) 

276 evaluated_keys.extend( 

277 find_evaluated_property_keys_by_schema( 

278 validator.evolve( 

279 schema=resolved.contents, 

280 _resolver=resolved.resolver, 

281 ), 

282 instance, 

283 resolved.contents, 

284 ), 

285 ) 

286 

287 dynamicRef = schema.get("$dynamicRef") 

288 if dynamicRef is not None: 

289 resolved = validator._resolver.lookup(dynamicRef) 

290 evaluated_keys.extend( 

291 find_evaluated_property_keys_by_schema( 

292 validator.evolve( 

293 schema=resolved.contents, 

294 _resolver=resolved.resolver, 

295 ), 

296 instance, 

297 resolved.contents, 

298 ), 

299 ) 

300 

301 for keyword in [ 

302 "properties", "additionalProperties", "unevaluatedProperties", 

303 ]: 

304 if keyword in schema: 

305 schema_value = schema[keyword] 

306 if validator.is_type(schema_value, "boolean") and schema_value: 

307 evaluated_keys += instance.keys() 

308 

309 elif validator.is_type(schema_value, "object"): 

310 for property in schema_value: 

311 if property in instance: 

312 evaluated_keys.append(property) 

313 

314 if "patternProperties" in schema: 

315 for property in instance: 

316 for pattern in schema["patternProperties"]: 

317 if re.search(pattern, property): 

318 evaluated_keys.append(property) 

319 

320 if "dependentSchemas" in schema: 

321 for property, subschema in schema["dependentSchemas"].items(): 

322 if property not in instance: 

323 continue 

324 evaluated_keys += find_evaluated_property_keys_by_schema( 

325 validator, instance, subschema, 

326 ) 

327 

328 for keyword in ["allOf", "oneOf", "anyOf"]: 

329 if keyword in schema: 

330 for subschema in schema[keyword]: 

331 errs = next(validator.descend(instance, subschema), None) 

332 if errs is None: 

333 evaluated_keys += find_evaluated_property_keys_by_schema( 

334 validator, instance, subschema, 

335 ) 

336 

337 if "if" in schema: 

338 if validator.evolve(schema=schema["if"]).is_valid(instance): 

339 evaluated_keys += find_evaluated_property_keys_by_schema( 

340 validator, instance, schema["if"], 

341 ) 

342 if "then" in schema: 

343 evaluated_keys += find_evaluated_property_keys_by_schema( 

344 validator, instance, schema["then"], 

345 ) 

346 elif "else" in schema: 

347 evaluated_keys += find_evaluated_property_keys_by_schema( 

348 validator, instance, schema["else"], 

349 ) 

350 

351 return evaluated_keys