Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jsonschema/_utils.py: 17%
156 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1from collections.abc import Mapping, MutableMapping, Sequence
2from urllib.parse import urlsplit
3import itertools
4import re
7class URIDict(MutableMapping):
8 """
9 Dictionary which uses normalized URIs as keys.
10 """
12 def normalize(self, uri):
13 return urlsplit(uri).geturl()
15 def __init__(self, *args, **kwargs):
16 self.store = dict()
17 self.store.update(*args, **kwargs)
19 def __getitem__(self, uri):
20 return self.store[self.normalize(uri)]
22 def __setitem__(self, uri, value):
23 self.store[self.normalize(uri)] = value
25 def __delitem__(self, uri):
26 del self.store[self.normalize(uri)]
28 def __iter__(self):
29 return iter(self.store)
31 def __len__(self): # pragma: no cover -- untested, but to be removed
32 return len(self.store)
34 def __repr__(self): # pragma: no cover -- untested, but to be removed
35 return repr(self.store)
38class Unset:
39 """
40 An as-of-yet unset attribute or unprovided default parameter.
41 """
43 def __repr__(self): # pragma: no cover
44 return "<unset>"
47def format_as_index(container, indices):
48 """
49 Construct a single string containing indexing operations for the indices.
51 For example for a container ``bar``, [1, 2, "foo"] -> bar[1][2]["foo"]
53 Arguments:
55 container (str):
57 A word to use for the thing being indexed
59 indices (sequence):
61 The indices to format.
62 """
64 if not indices:
65 return container
66 return f"{container}[{']['.join(repr(index) for index in indices)}]"
69def find_additional_properties(instance, schema):
70 """
71 Return the set of additional properties for the given ``instance``.
73 Weeds out properties that should have been validated by ``properties`` and
74 / or ``patternProperties``.
76 Assumes ``instance`` is dict-like already.
77 """
79 properties = schema.get("properties", {})
80 patterns = "|".join(schema.get("patternProperties", {}))
81 for property in instance:
82 if property not in properties:
83 if patterns and re.search(patterns, property):
84 continue
85 yield property
88def extras_msg(extras):
89 """
90 Create an error message for extra items or properties.
91 """
93 verb = "was" if len(extras) == 1 else "were"
94 return ", ".join(repr(extra) for extra in extras), verb
97def ensure_list(thing):
98 """
99 Wrap ``thing`` in a list if it's a single str.
101 Otherwise, return it unchanged.
102 """
104 if isinstance(thing, str):
105 return [thing]
106 return thing
109def _mapping_equal(one, two):
110 """
111 Check if two mappings are equal using the semantics of `equal`.
112 """
113 if len(one) != len(two):
114 return False
115 return all(
116 key in two and equal(value, two[key])
117 for key, value in one.items()
118 )
121def _sequence_equal(one, two):
122 """
123 Check if two sequences are equal using the semantics of `equal`.
124 """
125 if len(one) != len(two):
126 return False
127 return all(equal(i, j) for i, j in zip(one, two))
130def equal(one, two):
131 """
132 Check if two things are equal evading some Python type hierarchy semantics.
134 Specifically in JSON Schema, evade `bool` inheriting from `int`,
135 recursing into sequences to do the same.
136 """
137 if isinstance(one, str) or isinstance(two, str):
138 return one == two
139 if isinstance(one, Sequence) and isinstance(two, Sequence):
140 return _sequence_equal(one, two)
141 if isinstance(one, Mapping) and isinstance(two, Mapping):
142 return _mapping_equal(one, two)
143 return unbool(one) == unbool(two)
146def unbool(element, true=object(), false=object()):
147 """
148 A hack to make True and 1 and False and 0 unique for ``uniq``.
149 """
151 if element is True:
152 return true
153 elif element is False:
154 return false
155 return element
158def uniq(container):
159 """
160 Check if all of a container's elements are unique.
162 Tries to rely on the container being recursively sortable, or otherwise
163 falls back on (slow) brute force.
164 """
165 try:
166 sort = sorted(unbool(i) for i in container)
167 sliced = itertools.islice(sort, 1, None)
169 for i, j in zip(sort, sliced):
170 if equal(i, j):
171 return False
173 except (NotImplementedError, TypeError):
174 seen = []
175 for e in container:
176 e = unbool(e)
178 for i in seen:
179 if equal(i, e):
180 return False
182 seen.append(e)
183 return True
186def find_evaluated_item_indexes_by_schema(validator, instance, schema):
187 """
188 Get all indexes of items that get evaluated under the current schema
190 Covers all keywords related to unevaluatedItems: items, prefixItems, if,
191 then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
192 """
193 if validator.is_type(schema, "boolean"):
194 return []
195 evaluated_indexes = []
197 if "items" in schema:
198 return list(range(0, len(instance)))
200 ref = schema.get("$ref")
201 if ref is not None:
202 resolved = validator._resolver.lookup(ref)
203 evaluated_indexes.extend(
204 find_evaluated_item_indexes_by_schema(
205 validator.evolve(
206 schema=resolved.contents,
207 _resolver=resolved.resolver,
208 ),
209 instance,
210 resolved.contents,
211 ),
212 )
214 dynamicRef = schema.get("$dynamicRef")
215 if dynamicRef is not None:
216 resolved = validator._resolver.lookup(dynamicRef)
217 evaluated_indexes.extend(
218 find_evaluated_item_indexes_by_schema(
219 validator.evolve(
220 schema=resolved.contents,
221 _resolver=resolved.resolver,
222 ),
223 instance,
224 resolved.contents,
225 ),
226 )
228 if "prefixItems" in schema:
229 evaluated_indexes += list(range(0, len(schema["prefixItems"])))
231 if "if" in schema:
232 if validator.evolve(schema=schema["if"]).is_valid(instance):
233 evaluated_indexes += find_evaluated_item_indexes_by_schema(
234 validator, instance, schema["if"],
235 )
236 if "then" in schema:
237 evaluated_indexes += find_evaluated_item_indexes_by_schema(
238 validator, instance, schema["then"],
239 )
240 else:
241 if "else" in schema:
242 evaluated_indexes += find_evaluated_item_indexes_by_schema(
243 validator, instance, schema["else"],
244 )
246 for keyword in ["contains", "unevaluatedItems"]:
247 if keyword in schema:
248 for k, v in enumerate(instance):
249 if validator.evolve(schema=schema[keyword]).is_valid(v):
250 evaluated_indexes.append(k)
252 for keyword in ["allOf", "oneOf", "anyOf"]:
253 if keyword in schema:
254 for subschema in schema[keyword]:
255 errs = next(validator.descend(instance, subschema), None)
256 if errs is None:
257 evaluated_indexes += find_evaluated_item_indexes_by_schema(
258 validator, instance, subschema,
259 )
261 return evaluated_indexes
264def find_evaluated_property_keys_by_schema(validator, instance, schema):
265 """
266 Get all keys of items that get evaluated under the current schema
268 Covers all keywords related to unevaluatedProperties: properties,
269 additionalProperties, unevaluatedProperties, patternProperties,
270 dependentSchemas, allOf, oneOf, anyOf, if, then, else
271 """
272 if validator.is_type(schema, "boolean"):
273 return []
274 evaluated_keys = []
276 ref = schema.get("$ref")
277 if ref is not None:
278 resolved = validator._resolver.lookup(ref)
279 evaluated_keys.extend(
280 find_evaluated_property_keys_by_schema(
281 validator.evolve(
282 schema=resolved.contents,
283 _resolver=resolved.resolver,
284 ),
285 instance,
286 resolved.contents,
287 ),
288 )
290 dynamicRef = schema.get("$dynamicRef")
291 if dynamicRef is not None:
292 resolved = validator._resolver.lookup(dynamicRef)
293 evaluated_keys.extend(
294 find_evaluated_property_keys_by_schema(
295 validator.evolve(
296 schema=resolved.contents,
297 _resolver=resolved.resolver,
298 ),
299 instance,
300 resolved.contents,
301 ),
302 )
304 for keyword in [
305 "properties", "additionalProperties", "unevaluatedProperties",
306 ]:
307 if keyword in schema:
308 schema_value = schema[keyword]
309 if validator.is_type(schema_value, "boolean") and schema_value:
310 evaluated_keys += instance.keys()
312 elif validator.is_type(schema_value, "object"):
313 for property in schema_value:
314 if property in instance:
315 evaluated_keys.append(property)
317 if "patternProperties" in schema:
318 for property in instance:
319 for pattern in schema["patternProperties"]:
320 if re.search(pattern, property):
321 evaluated_keys.append(property)
323 if "dependentSchemas" in schema:
324 for property, subschema in schema["dependentSchemas"].items():
325 if property not in instance:
326 continue
327 evaluated_keys += find_evaluated_property_keys_by_schema(
328 validator, instance, subschema,
329 )
331 for keyword in ["allOf", "oneOf", "anyOf"]:
332 if keyword in schema:
333 for subschema in schema[keyword]:
334 errs = next(validator.descend(instance, subschema), None)
335 if errs is None:
336 evaluated_keys += find_evaluated_property_keys_by_schema(
337 validator, instance, subschema,
338 )
340 if "if" in schema:
341 if validator.evolve(schema=schema["if"]).is_valid(instance):
342 evaluated_keys += find_evaluated_property_keys_by_schema(
343 validator, instance, schema["if"],
344 )
345 if "then" in schema:
346 evaluated_keys += find_evaluated_property_keys_by_schema(
347 validator, instance, schema["then"],
348 )
349 else:
350 if "else" in schema:
351 evaluated_keys += find_evaluated_property_keys_by_schema(
352 validator, instance, schema["else"],
353 )
355 return evaluated_keys