codekingpro/portable-devtools
114k
1import re2 3from referencing.jsonschema import lookup_recursive_ref4 5from jsonschema import _utils6from jsonschema.exceptions import ValidationError7 8 9def ignore_ref_siblings(schema):10 """11 Ignore siblings of ``$ref`` if it is present.12 13 Otherwise, return all keywords.14 15 Suitable for use with `create`'s ``applicable_validators`` argument.16 """17 ref = schema.get("$ref")18 if ref is not None:19 return [("$ref", ref)]20 else:21 return schema.items()22 23 24def dependencies_draft3(validator, dependencies, instance, schema):25 if not validator.is_type(instance, "object"):26 return27 28 for property, dependency in dependencies.items():29 if property not in instance:30 continue31 32 if validator.is_type(dependency, "object"):33 yield from validator.descend(34 instance, dependency, schema_path=property,35 )36 elif validator.is_type(dependency, "string"):37 if dependency not in instance:38 message = f"{dependency!r} is a dependency of {property!r}"39 yield ValidationError(message)40 else:41 for each in dependency:42 if each not in instance:43 message = f"{each!r} is a dependency of {property!r}"44 yield ValidationError(message)45 46 47def dependencies_draft4_draft6_draft7(48 validator,49 dependencies,50 instance,51 schema,52):53 """54 Support for the ``dependencies`` keyword from pre-draft 2019-09.55 56 In later drafts, the keyword was split into separate57 ``dependentRequired`` and ``dependentSchemas`` validators.58 """59 if not validator.is_type(instance, "object"):60 return61 62 for property, dependency in dependencies.items():63 if property not in instance:64 continue65 66 if validator.is_type(dependency, "array"):67 for each in dependency:68 if each not in instance:69 message = f"{each!r} is a dependency of {property!r}"70 yield ValidationError(message)71 else:72 yield from validator.descend(73 instance, dependency, schema_path=property,74 )75 76 77def disallow_draft3(validator, disallow, instance, schema):78 for disallowed in _utils.ensure_list(disallow):79 if validator.evolve(schema={"type": [disallowed]}).is_valid(instance):80 message = f"{disallowed!r} is disallowed for {instance!r}"81 yield ValidationError(message)82 83 84def extends_draft3(validator, extends, instance, schema):85 if validator.is_type(extends, "object"):86 yield from validator.descend(instance, extends)87 return88 for index, subschema in enumerate(extends):89 yield from validator.descend(instance, subschema, schema_path=index)90 91 92def items_draft3_draft4(validator, items, instance, schema):93 if not validator.is_type(instance, "array"):94 return95 96 if validator.is_type(items, "object"):97 for index, item in enumerate(instance):98 yield from validator.descend(item, items, path=index)99 else:100 for (index, item), subschema in zip(enumerate(instance), items):101 yield from validator.descend(102 item, subschema, path=index, schema_path=index,103 )104 105 106def additionalItems(validator, aI, instance, schema):107 if (108 not validator.is_type(instance, "array")109 or validator.is_type(schema.get("items", {}), "object")110 ):111 return112 113 len_items = len(schema.get("items", []))114 if validator.is_type(aI, "object"):115 for index, item in enumerate(instance[len_items:], start=len_items):116 yield from validator.descend(item, aI, path=index)117 elif not aI and len(instance) > len(schema.get("items", [])):118 error = "Additional items are not allowed (%s %s unexpected)"119 yield ValidationError(120 error % _utils.extras_msg(instance[len(schema.get("items", [])):]),121 )122 123 124def items_draft6_draft7_draft201909(validator, items, instance, schema):125 if not validator.is_type(instance, "array"):126 return127 128 if validator.is_type(items, "array"):129 for (index, item), subschema in zip(enumerate(instance), items):130 yield from validator.descend(131 item, subschema, path=index, schema_path=index,132 )133 else:134 for index, item in enumerate(instance):135 yield from validator.descend(item, items, path=index)136 137 138def minimum_draft3_draft4(validator, minimum, instance, schema):139 if not validator.is_type(instance, "number"):140 return141 142 if schema.get("exclusiveMinimum", False):143 failed = instance <= minimum144 cmp = "less than or equal to"145 else:146 failed = instance < minimum147 cmp = "less than"148 149 if failed:150 message = f"{instance!r} is {cmp} the minimum of {minimum!r}"151 yield ValidationError(message)152 153 154def maximum_draft3_draft4(validator, maximum, instance, schema):155 if not validator.is_type(instance, "number"):156 return157 158 if schema.get("exclusiveMaximum", False):159 failed = instance >= maximum160 cmp = "greater than or equal to"161 else:162 failed = instance > maximum163 cmp = "greater than"164 165 if failed:166 message = f"{instance!r} is {cmp} the maximum of {maximum!r}"167 yield ValidationError(message)168 169 170def properties_draft3(validator, properties, instance, schema):171 if not validator.is_type(instance, "object"):172 return173 174 for property, subschema in properties.items():175 if property in instance:176 yield from validator.descend(177 instance[property],178 subschema,179 path=property,180 schema_path=property,181 )182 elif subschema.get("required", False):183 error = ValidationError(f"{property!r} is a required property")184 error._set(185 validator="required",186 validator_value=subschema["required"],187 instance=instance,188 schema=schema,189 )190 error.path.appendleft(property)191 error.schema_path.extend([property, "required"])192 yield error193 194 195def type_draft3(validator, types, instance, schema):196 types = _utils.ensure_list(types)197 198 all_errors = []199 for index, type in enumerate(types):200 if validator.is_type(type, "object"):201 errors = list(validator.descend(instance, type, schema_path=index))202 if not errors:203 return204 all_errors.extend(errors)205 elif validator.is_type(instance, type):206 return207 208 reprs = []209 for type in types:210 try:211 reprs.append(repr(type["name"]))212 except Exception: # noqa: BLE001213 reprs.append(repr(type))214 yield ValidationError(215 f"{instance!r} is not of type {', '.join(reprs)}",216 context=all_errors,217 )218 219 220def contains_draft6_draft7(validator, contains, instance, schema):221 if not validator.is_type(instance, "array"):222 return223 224 if not any(225 validator.evolve(schema=contains).is_valid(element)226 for element in instance227 ):228 yield ValidationError(229 f"None of {instance!r} are valid under the given schema",230 )231 232 233def recursiveRef(validator, recursiveRef, instance, schema):234 resolved = lookup_recursive_ref(validator._resolver)235 yield from validator.descend(236 instance,237 resolved.contents,238 resolver=resolved.resolver,239 )240 241 242def find_evaluated_item_indexes_by_schema(validator, instance, schema):243 """244 Get all indexes of items that get evaluated under the current schema.245 246 Covers all keywords related to unevaluatedItems: items, prefixItems, if,247 then, else, contains, unevaluatedItems, allOf, oneOf, anyOf248 """249 if validator.is_type(schema, "boolean"):250 return []251 evaluated_indexes = []252 253 ref = schema.get("$ref")254 if ref is not None:255 resolved = validator._resolver.lookup(ref)256 evaluated_indexes.extend(257 find_evaluated_item_indexes_by_schema(258 validator.evolve(259 schema=resolved.contents,260 _resolver=resolved.resolver,261 ),262 instance,263 resolved.contents,264 ),265 )266 267 if "$recursiveRef" in schema:268 resolved = lookup_recursive_ref(validator._resolver)269 evaluated_indexes.extend(270 find_evaluated_item_indexes_by_schema(271 validator.evolve(272 schema=resolved.contents,273 _resolver=resolved.resolver,274 ),275 instance,276 resolved.contents,277 ),278 )279 280 if "items" in schema:281 if "additionalItems" in schema:282 return list(range(len(instance)))283 284 if validator.is_type(schema["items"], "object"):285 return list(range(len(instance)))286 evaluated_indexes += list(range(len(schema["items"])))287 288 if "if" in schema:289 if validator.evolve(schema=schema["if"]).is_valid(instance):290 evaluated_indexes += find_evaluated_item_indexes_by_schema(291 validator, instance, schema["if"],292 )293 if "then" in schema:294 evaluated_indexes += find_evaluated_item_indexes_by_schema(295 validator, instance, schema["then"],296 )297 elif "else" in schema:298 evaluated_indexes += find_evaluated_item_indexes_by_schema(299 validator, instance, schema["else"],300 )301 302 for keyword in ["contains", "unevaluatedItems"]:303 if keyword in schema:304 for k, v in enumerate(instance):305 if validator.evolve(schema=schema[keyword]).is_valid(v):306 evaluated_indexes.append(k)307 308 for keyword in ["allOf", "oneOf", "anyOf"]:309 if keyword in schema:310 for subschema in schema[keyword]:311 errs = next(validator.descend(instance, subschema), None)312 if errs is None:313 evaluated_indexes += find_evaluated_item_indexes_by_schema(314 validator, instance, subschema,315 )316 317 return evaluated_indexes318 319 320def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema):321 if not validator.is_type(instance, "array"):322 return323 evaluated_item_indexes = find_evaluated_item_indexes_by_schema(324 validator, instance, schema,325 )326 unevaluated_items = [327 item for index, item in enumerate(instance)328 if index not in evaluated_item_indexes329 ]330 if unevaluated_items:331 error = "Unevaluated items are not allowed (%s %s unexpected)"332 yield ValidationError(error % _utils.extras_msg(unevaluated_items))333 334 335def find_evaluated_property_keys_by_schema(validator, instance, schema):336 if validator.is_type(schema, "boolean"):337 return []338 evaluated_keys = []339 340 ref = schema.get("$ref")341 if ref is not None:342 resolved = validator._resolver.lookup(ref)343 evaluated_keys.extend(344 find_evaluated_property_keys_by_schema(345 validator.evolve(346 schema=resolved.contents,347 _resolver=resolved.resolver,348 ),349 instance,350 resolved.contents,351 ),352 )353 354 if "$recursiveRef" in schema:355 resolved = lookup_recursive_ref(validator._resolver)356 evaluated_keys.extend(357 find_evaluated_property_keys_by_schema(358 validator.evolve(359 schema=resolved.contents,360 _resolver=resolved.resolver,361 ),362 instance,363 resolved.contents,364 ),365 )366 367 for keyword in [368 "properties", "additionalProperties", "unevaluatedProperties",369 ]:370 if keyword in schema:371 schema_value = schema[keyword]372 if validator.is_type(schema_value, "boolean") and schema_value:373 evaluated_keys += instance.keys()374 375 elif validator.is_type(schema_value, "object"):376 for property in schema_value:377 if property in instance:378 evaluated_keys.append(property)379 380 if "patternProperties" in schema:381 for property in instance:382 for pattern in schema["patternProperties"]:383 if re.search(pattern, property):384 evaluated_keys.append(property)385 386 if "dependentSchemas" in schema:387 for property, subschema in schema["dependentSchemas"].items():388 if property not in instance:389 continue390 evaluated_keys += find_evaluated_property_keys_by_schema(391 validator, instance, subschema,392 )393 394 for keyword in ["allOf", "oneOf", "anyOf"]:395 if keyword in schema:396 for subschema in schema[keyword]:397 errs = next(validator.descend(instance, subschema), None)398 if errs is None:399 evaluated_keys += find_evaluated_property_keys_by_schema(400 validator, instance, subschema,401 )402 403 if "if" in schema:404 if validator.evolve(schema=schema["if"]).is_valid(instance):405 evaluated_keys += find_evaluated_property_keys_by_schema(406 validator, instance, schema["if"],407 )408 if "then" in schema:409 evaluated_keys += find_evaluated_property_keys_by_schema(410 validator, instance, schema["then"],411 )412 elif "else" in schema:413 evaluated_keys += find_evaluated_property_keys_by_schema(414 validator, instance, schema["else"],415 )416 417 return evaluated_keys418 419 420def unevaluatedProperties_draft2019(validator, uP, instance, schema):421 if not validator.is_type(instance, "object"):422 return423 evaluated_keys = find_evaluated_property_keys_by_schema(424 validator, instance, schema,425 )426 unevaluated_keys = []427 for property in instance:428 if property not in evaluated_keys:429 for _ in validator.descend(430 instance[property],431 uP,432 path=property,433 schema_path=property,434 ):435 # FIXME: Include context for each unevaluated property436 # indicating why it's invalid under the subschema.437 unevaluated_keys.append(property) # noqa: PERF401438 439 if unevaluated_keys:440 if uP is False:441 error = "Unevaluated properties are not allowed (%s %s unexpected)"442 extras = sorted(unevaluated_keys, key=str)443 yield ValidationError(error % _utils.extras_msg(extras))444 else:445 error = (446 "Unevaluated properties are not valid under "447 "the given schema (%s %s unevaluated and invalid)"448 )449 yield ValidationError(error % _utils.extras_msg(unevaluated_keys))450 