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

267 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1from fractions import Fraction 

2from urllib.parse import urldefrag, urljoin 

3import re 

4 

5from jsonschema._utils import ( 

6 ensure_list, 

7 equal, 

8 extras_msg, 

9 find_additional_properties, 

10 find_evaluated_item_indexes_by_schema, 

11 find_evaluated_property_keys_by_schema, 

12 unbool, 

13 uniq, 

14) 

15from jsonschema.exceptions import FormatError, ValidationError 

16 

17 

18def patternProperties(validator, patternProperties, instance, schema): 

19 if not validator.is_type(instance, "object"): 

20 return 

21 

22 for pattern, subschema in patternProperties.items(): 

23 for k, v in instance.items(): 

24 if re.search(pattern, k): 

25 yield from validator.descend( 

26 v, subschema, path=k, schema_path=pattern, 

27 ) 

28 

29 

30def propertyNames(validator, propertyNames, instance, schema): 

31 if not validator.is_type(instance, "object"): 

32 return 

33 

34 for property in instance: 

35 yield from validator.descend(instance=property, schema=propertyNames) 

36 

37 

38def additionalProperties(validator, aP, instance, schema): 

39 if not validator.is_type(instance, "object"): 

40 return 

41 

42 extras = set(find_additional_properties(instance, schema)) 

43 

44 if validator.is_type(aP, "object"): 

45 for extra in extras: 

46 yield from validator.descend(instance[extra], aP, path=extra) 

47 elif not aP and extras: 

48 if "patternProperties" in schema: 

49 if len(extras) == 1: 

50 verb = "does" 

51 else: 

52 verb = "do" 

53 

54 joined = ", ".join(repr(each) for each in sorted(extras)) 

55 patterns = ", ".join( 

56 repr(each) for each in sorted(schema["patternProperties"]) 

57 ) 

58 error = f"{joined} {verb} not match any of the regexes: {patterns}" 

59 yield ValidationError(error) 

60 else: 

61 error = "Additional properties are not allowed (%s %s unexpected)" 

62 yield ValidationError(error % extras_msg(extras)) 

63 

64 

65def items(validator, items, instance, schema): 

66 if not validator.is_type(instance, "array"): 

67 return 

68 

69 prefix = len(schema.get("prefixItems", [])) 

70 total = len(instance) 

71 if items is False and total > prefix: 

72 message = f"Expected at most {prefix} items, but found {total}" 

73 yield ValidationError(message) 

74 else: 

75 for index in range(prefix, total): 

76 yield from validator.descend( 

77 instance=instance[index], 

78 schema=items, 

79 path=index, 

80 ) 

81 

82 

83def additionalItems(validator, aI, instance, schema): 

84 if ( 

85 not validator.is_type(instance, "array") 

86 or validator.is_type(schema.get("items", {}), "object") 

87 ): 

88 return 

89 

90 len_items = len(schema.get("items", [])) 

91 if validator.is_type(aI, "object"): 

92 for index, item in enumerate(instance[len_items:], start=len_items): 

93 yield from validator.descend(item, aI, path=index) 

94 elif not aI and len(instance) > len(schema.get("items", [])): 

95 error = "Additional items are not allowed (%s %s unexpected)" 

96 yield ValidationError( 

97 error % extras_msg(instance[len(schema.get("items", [])):]), 

98 ) 

99 

100 

101def const(validator, const, instance, schema): 

102 if not equal(instance, const): 

103 yield ValidationError(f"{const!r} was expected") 

104 

105 

106def contains(validator, contains, instance, schema): 

107 if not validator.is_type(instance, "array"): 

108 return 

109 

110 matches = 0 

111 min_contains = schema.get("minContains", 1) 

112 max_contains = schema.get("maxContains", len(instance)) 

113 

114 for each in instance: 

115 if validator.evolve(schema=contains).is_valid(each): 

116 matches += 1 

117 if matches > max_contains: 

118 yield ValidationError( 

119 "Too many items match the given schema " 

120 f"(expected at most {max_contains})", 

121 validator="maxContains", 

122 validator_value=max_contains, 

123 ) 

124 return 

125 

126 if matches < min_contains: 

127 if not matches: 

128 yield ValidationError( 

129 f"{instance!r} does not contain items " 

130 "matching the given schema", 

131 ) 

132 else: 

133 yield ValidationError( 

134 "Too few items match the given schema (expected at least " 

135 f"{min_contains} but only {matches} matched)", 

136 validator="minContains", 

137 validator_value=min_contains, 

138 ) 

139 

140 

141def exclusiveMinimum(validator, minimum, instance, schema): 

142 if not validator.is_type(instance, "number"): 

143 return 

144 

145 if instance <= minimum: 

146 yield ValidationError( 

147 f"{instance!r} is less than or equal to " 

148 f"the minimum of {minimum!r}", 

149 ) 

150 

151 

152def exclusiveMaximum(validator, maximum, instance, schema): 

153 if not validator.is_type(instance, "number"): 

154 return 

155 

156 if instance >= maximum: 

157 yield ValidationError( 

158 f"{instance!r} is greater than or equal " 

159 f"to the maximum of {maximum!r}", 

160 ) 

161 

162 

163def minimum(validator, minimum, instance, schema): 

164 if not validator.is_type(instance, "number"): 

165 return 

166 

167 if instance < minimum: 

168 message = f"{instance!r} is less than the minimum of {minimum!r}" 

169 yield ValidationError(message) 

170 

171 

172def maximum(validator, maximum, instance, schema): 

173 if not validator.is_type(instance, "number"): 

174 return 

175 

176 if instance > maximum: 

177 message = f"{instance!r} is greater than the maximum of {maximum!r}" 

178 yield ValidationError(message) 

179 

180 

181def multipleOf(validator, dB, instance, schema): 

182 if not validator.is_type(instance, "number"): 

183 return 

184 

185 if isinstance(dB, float): 

186 quotient = instance / dB 

187 try: 

188 failed = int(quotient) != quotient 

189 except OverflowError: 

190 # When `instance` is large and `dB` is less than one, 

191 # quotient can overflow to infinity; and then casting to int 

192 # raises an error. 

193 # 

194 # In this case we fall back to Fraction logic, which is 

195 # exact and cannot overflow. The performance is also 

196 # acceptable: we try the fast all-float option first, and 

197 # we know that fraction(dB) can have at most a few hundred 

198 # digits in each part. The worst-case slowdown is therefore 

199 # for already-slow enormous integers or Decimals. 

200 failed = (Fraction(instance) / Fraction(dB)).denominator != 1 

201 else: 

202 failed = instance % dB 

203 

204 if failed: 

205 yield ValidationError(f"{instance!r} is not a multiple of {dB}") 

206 

207 

208def minItems(validator, mI, instance, schema): 

209 if validator.is_type(instance, "array") and len(instance) < mI: 

210 yield ValidationError(f"{instance!r} is too short") 

211 

212 

213def maxItems(validator, mI, instance, schema): 

214 if validator.is_type(instance, "array") and len(instance) > mI: 

215 yield ValidationError(f"{instance!r} is too long") 

216 

217 

218def uniqueItems(validator, uI, instance, schema): 

219 if ( 

220 uI 

221 and validator.is_type(instance, "array") 

222 and not uniq(instance) 

223 ): 

224 yield ValidationError(f"{instance!r} has non-unique elements") 

225 

226 

227def pattern(validator, patrn, instance, schema): 

228 if ( 

229 validator.is_type(instance, "string") 

230 and not re.search(patrn, instance) 

231 ): 

232 yield ValidationError(f"{instance!r} does not match {patrn!r}") 

233 

234 

235def format(validator, format, instance, schema): 

236 if validator.format_checker is not None: 

237 try: 

238 validator.format_checker.check(instance, format) 

239 except FormatError as error: 

240 yield ValidationError(error.message, cause=error.cause) 

241 

242 

243def minLength(validator, mL, instance, schema): 

244 if validator.is_type(instance, "string") and len(instance) < mL: 

245 yield ValidationError(f"{instance!r} is too short") 

246 

247 

248def maxLength(validator, mL, instance, schema): 

249 if validator.is_type(instance, "string") and len(instance) > mL: 

250 yield ValidationError(f"{instance!r} is too long") 

251 

252 

253def dependentRequired(validator, dependentRequired, instance, schema): 

254 if not validator.is_type(instance, "object"): 

255 return 

256 

257 for property, dependency in dependentRequired.items(): 

258 if property not in instance: 

259 continue 

260 

261 for each in dependency: 

262 if each not in instance: 

263 message = f"{each!r} is a dependency of {property!r}" 

264 yield ValidationError(message) 

265 

266 

267def dependentSchemas(validator, dependentSchemas, instance, schema): 

268 if not validator.is_type(instance, "object"): 

269 return 

270 

271 for property, dependency in dependentSchemas.items(): 

272 if property not in instance: 

273 continue 

274 yield from validator.descend( 

275 instance, dependency, schema_path=property, 

276 ) 

277 

278 

279def enum(validator, enums, instance, schema): 

280 if instance == 0 or instance == 1: 

281 unbooled = unbool(instance) 

282 if all(unbooled != unbool(each) for each in enums): 

283 yield ValidationError(f"{instance!r} is not one of {enums!r}") 

284 elif instance not in enums: 

285 yield ValidationError(f"{instance!r} is not one of {enums!r}") 

286 

287 

288def ref(validator, ref, instance, schema): 

289 resolve = getattr(validator.resolver, "resolve", None) 

290 if resolve is None: 

291 with validator.resolver.resolving(ref) as resolved: 

292 yield from validator.descend(instance, resolved) 

293 else: 

294 scope, resolved = validator.resolver.resolve(ref) 

295 validator.resolver.push_scope(scope) 

296 

297 try: 

298 yield from validator.descend(instance, resolved) 

299 finally: 

300 validator.resolver.pop_scope() 

301 

302 

303def dynamicRef(validator, dynamicRef, instance, schema): 

304 _, fragment = urldefrag(dynamicRef) 

305 

306 for url in validator.resolver._scopes_stack: 

307 lookup_url = urljoin(url, dynamicRef) 

308 with validator.resolver.resolving(lookup_url) as subschema: 

309 if ("$dynamicAnchor" in subschema 

310 and fragment == subschema["$dynamicAnchor"]): 

311 yield from validator.descend(instance, subschema) 

312 break 

313 else: 

314 with validator.resolver.resolving(dynamicRef) as subschema: 

315 yield from validator.descend(instance, subschema) 

316 

317 

318def type(validator, types, instance, schema): 

319 types = ensure_list(types) 

320 

321 if not any(validator.is_type(instance, type) for type in types): 

322 reprs = ", ".join(repr(type) for type in types) 

323 yield ValidationError(f"{instance!r} is not of type {reprs}") 

324 

325 

326def properties(validator, properties, instance, schema): 

327 if not validator.is_type(instance, "object"): 

328 return 

329 

330 for property, subschema in properties.items(): 

331 if property in instance: 

332 yield from validator.descend( 

333 instance[property], 

334 subschema, 

335 path=property, 

336 schema_path=property, 

337 ) 

338 

339 

340def required(validator, required, instance, schema): 

341 if not validator.is_type(instance, "object"): 

342 return 

343 for property in required: 

344 if property not in instance: 

345 yield ValidationError(f"{property!r} is a required property") 

346 

347 

348def minProperties(validator, mP, instance, schema): 

349 if validator.is_type(instance, "object") and len(instance) < mP: 

350 yield ValidationError(f"{instance!r} does not have enough properties") 

351 

352 

353def maxProperties(validator, mP, instance, schema): 

354 if not validator.is_type(instance, "object"): 

355 return 

356 if validator.is_type(instance, "object") and len(instance) > mP: 

357 yield ValidationError(f"{instance!r} has too many properties") 

358 

359 

360def allOf(validator, allOf, instance, schema): 

361 for index, subschema in enumerate(allOf): 

362 yield from validator.descend(instance, subschema, schema_path=index) 

363 

364 

365def anyOf(validator, anyOf, instance, schema): 

366 all_errors = [] 

367 for index, subschema in enumerate(anyOf): 

368 errs = list(validator.descend(instance, subschema, schema_path=index)) 

369 if not errs: 

370 break 

371 all_errors.extend(errs) 

372 else: 

373 yield ValidationError( 

374 f"{instance!r} is not valid under any of the given schemas", 

375 context=all_errors, 

376 ) 

377 

378 

379def oneOf(validator, oneOf, instance, schema): 

380 subschemas = enumerate(oneOf) 

381 all_errors = [] 

382 for index, subschema in subschemas: 

383 errs = list(validator.descend(instance, subschema, schema_path=index)) 

384 if not errs: 

385 first_valid = subschema 

386 break 

387 all_errors.extend(errs) 

388 else: 

389 yield ValidationError( 

390 f"{instance!r} is not valid under any of the given schemas", 

391 context=all_errors, 

392 ) 

393 

394 more_valid = [ 

395 each for _, each in subschemas 

396 if validator.evolve(schema=each).is_valid(instance) 

397 ] 

398 if more_valid: 

399 more_valid.append(first_valid) 

400 reprs = ", ".join(repr(schema) for schema in more_valid) 

401 yield ValidationError(f"{instance!r} is valid under each of {reprs}") 

402 

403 

404def not_(validator, not_schema, instance, schema): 

405 if validator.evolve(schema=not_schema).is_valid(instance): 

406 message = f"{instance!r} should not be valid under {not_schema!r}" 

407 yield ValidationError(message) 

408 

409 

410def if_(validator, if_schema, instance, schema): 

411 if validator.evolve(schema=if_schema).is_valid(instance): 

412 if "then" in schema: 

413 then = schema["then"] 

414 yield from validator.descend(instance, then, schema_path="then") 

415 elif "else" in schema: 

416 else_ = schema["else"] 

417 yield from validator.descend(instance, else_, schema_path="else") 

418 

419 

420def unevaluatedItems(validator, unevaluatedItems, instance, schema): 

421 if not validator.is_type(instance, "array"): 

422 return 

423 evaluated_item_indexes = find_evaluated_item_indexes_by_schema( 

424 validator, instance, schema, 

425 ) 

426 unevaluated_items = [ 

427 item for index, item in enumerate(instance) 

428 if index not in evaluated_item_indexes 

429 ] 

430 if unevaluated_items: 

431 error = "Unevaluated items are not allowed (%s %s unexpected)" 

432 yield ValidationError(error % extras_msg(unevaluated_items)) 

433 

434 

435def unevaluatedProperties(validator, unevaluatedProperties, instance, schema): 

436 if not validator.is_type(instance, "object"): 

437 return 

438 evaluated_keys = find_evaluated_property_keys_by_schema( 

439 validator, instance, schema, 

440 ) 

441 unevaluated_keys = [] 

442 for property in instance: 

443 if property not in evaluated_keys: 

444 for _ in validator.descend( 

445 instance[property], 

446 unevaluatedProperties, 

447 path=property, 

448 schema_path=property, 

449 ): 

450 # FIXME: Include context for each unevaluated property 

451 # indicating why it's invalid under the subschema. 

452 unevaluated_keys.append(property) 

453 

454 if unevaluated_keys: 

455 if unevaluatedProperties is False: 

456 error = "Unevaluated properties are not allowed (%s %s unexpected)" 

457 yield ValidationError(error % extras_msg(unevaluated_keys)) 

458 else: 

459 error = ( 

460 "Unevaluated properties are not valid under " 

461 "the given schema (%s %s unevaluated and invalid)" 

462 ) 

463 yield ValidationError(error % extras_msg(unevaluated_keys)) 

464 

465 

466def prefixItems(validator, prefixItems, instance, schema): 

467 if not validator.is_type(instance, "array"): 

468 return 

469 

470 for (index, item), subschema in zip(enumerate(instance), prefixItems): 

471 yield from validator.descend( 

472 instance=item, 

473 schema=subschema, 

474 schema_path=index, 

475 path=index, 

476 )