Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jsonschema/_utils.py: 15%
156 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-22 06:29 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-22 06:29 +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 """
63 if not indices:
64 return container
65 return f"{container}[{']['.join(repr(index) for index in indices)}]"
68def find_additional_properties(instance, schema):
69 """
70 Return the set of additional properties for the given ``instance``.
72 Weeds out properties that should have been validated by ``properties`` and
73 / or ``patternProperties``.
75 Assumes ``instance`` is dict-like already.
76 """
77 properties = schema.get("properties", {})
78 patterns = "|".join(schema.get("patternProperties", {}))
79 for property in instance:
80 if property not in properties:
81 if patterns and re.search(patterns, property):
82 continue
83 yield property
86def extras_msg(extras):
87 """
88 Create an error message for extra items or properties.
89 """
90 verb = "was" if len(extras) == 1 else "were"
91 return ", ".join(repr(extra) for extra in extras), verb
94def ensure_list(thing):
95 """
96 Wrap ``thing`` in a list if it's a single str.
98 Otherwise, return it unchanged.
99 """
100 if isinstance(thing, str):
101 return [thing]
102 return thing
105def _mapping_equal(one, two):
106 """
107 Check if two mappings are equal using the semantics of `equal`.
108 """
109 if len(one) != len(two):
110 return False
111 return all(
112 key in two and equal(value, two[key])
113 for key, value in one.items()
114 )
117def _sequence_equal(one, two):
118 """
119 Check if two sequences are equal using the semantics of `equal`.
120 """
121 if len(one) != len(two):
122 return False
123 return all(equal(i, j) for i, j in zip(one, two))
126def equal(one, two):
127 """
128 Check if two things are equal evading some Python type hierarchy semantics.
130 Specifically in JSON Schema, evade `bool` inheriting from `int`,
131 recursing into sequences to do the same.
132 """
133 if isinstance(one, str) or isinstance(two, str):
134 return one == two
135 if isinstance(one, Sequence) and isinstance(two, Sequence):
136 return _sequence_equal(one, two)
137 if isinstance(one, Mapping) and isinstance(two, Mapping):
138 return _mapping_equal(one, two)
139 return unbool(one) == unbool(two)
142def unbool(element, true=object(), false=object()):
143 """
144 A hack to make True and 1 and False and 0 unique for ``uniq``.
145 """
146 if element is True:
147 return true
148 elif element is False:
149 return false
150 return element
153def uniq(container):
154 """
155 Check if all of a container's elements are unique.
157 Tries to rely on the container being recursively sortable, or otherwise
158 falls back on (slow) brute force.
159 """
160 try:
161 sort = sorted(unbool(i) for i in container)
162 sliced = itertools.islice(sort, 1, None)
164 for i, j in zip(sort, sliced):
165 if equal(i, j):
166 return False
168 except (NotImplementedError, TypeError):
169 seen = []
170 for e in container:
171 e = unbool(e)
173 for i in seen:
174 if equal(i, e):
175 return False
177 seen.append(e)
178 return True
181def find_evaluated_item_indexes_by_schema(validator, instance, schema):
182 """
183 Get all indexes of items that get evaluated under the current schema.
185 Covers all keywords related to unevaluatedItems: items, prefixItems, if,
186 then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
187 """
188 if validator.is_type(schema, "boolean"):
189 return []
190 evaluated_indexes = []
192 if "items" in schema:
193 return list(range(len(instance)))
195 ref = schema.get("$ref")
196 if ref is not None:
197 resolved = validator._resolver.lookup(ref)
198 evaluated_indexes.extend(
199 find_evaluated_item_indexes_by_schema(
200 validator.evolve(
201 schema=resolved.contents,
202 _resolver=resolved.resolver,
203 ),
204 instance,
205 resolved.contents,
206 ),
207 )
209 dynamicRef = schema.get("$dynamicRef")
210 if dynamicRef is not None:
211 resolved = validator._resolver.lookup(dynamicRef)
212 evaluated_indexes.extend(
213 find_evaluated_item_indexes_by_schema(
214 validator.evolve(
215 schema=resolved.contents,
216 _resolver=resolved.resolver,
217 ),
218 instance,
219 resolved.contents,
220 ),
221 )
223 if "prefixItems" in schema:
224 evaluated_indexes += list(range(len(schema["prefixItems"])))
226 if "if" in schema:
227 if validator.evolve(schema=schema["if"]).is_valid(instance):
228 evaluated_indexes += find_evaluated_item_indexes_by_schema(
229 validator, instance, schema["if"],
230 )
231 if "then" in schema:
232 evaluated_indexes += find_evaluated_item_indexes_by_schema(
233 validator, instance, schema["then"],
234 )
235 elif "else" in schema:
236 evaluated_indexes += find_evaluated_item_indexes_by_schema(
237 validator, instance, schema["else"],
238 )
240 for keyword in ["contains", "unevaluatedItems"]:
241 if keyword in schema:
242 for k, v in enumerate(instance):
243 if validator.evolve(schema=schema[keyword]).is_valid(v):
244 evaluated_indexes.append(k)
246 for keyword in ["allOf", "oneOf", "anyOf"]:
247 if keyword in schema:
248 for subschema in schema[keyword]:
249 errs = next(validator.descend(instance, subschema), None)
250 if errs is None:
251 evaluated_indexes += find_evaluated_item_indexes_by_schema(
252 validator, instance, subschema,
253 )
255 return evaluated_indexes
258def find_evaluated_property_keys_by_schema(validator, instance, schema):
259 """
260 Get all keys of items that get evaluated under the current schema.
262 Covers all keywords related to unevaluatedProperties: properties,
263 additionalProperties, unevaluatedProperties, patternProperties,
264 dependentSchemas, allOf, oneOf, anyOf, if, then, else
265 """
266 if validator.is_type(schema, "boolean"):
267 return []
268 evaluated_keys = []
270 ref = schema.get("$ref")
271 if ref is not None:
272 resolved = validator._resolver.lookup(ref)
273 evaluated_keys.extend(
274 find_evaluated_property_keys_by_schema(
275 validator.evolve(
276 schema=resolved.contents,
277 _resolver=resolved.resolver,
278 ),
279 instance,
280 resolved.contents,
281 ),
282 )
284 dynamicRef = schema.get("$dynamicRef")
285 if dynamicRef is not None:
286 resolved = validator._resolver.lookup(dynamicRef)
287 evaluated_keys.extend(
288 find_evaluated_property_keys_by_schema(
289 validator.evolve(
290 schema=resolved.contents,
291 _resolver=resolved.resolver,
292 ),
293 instance,
294 resolved.contents,
295 ),
296 )
298 for keyword in [
299 "properties", "additionalProperties", "unevaluatedProperties",
300 ]:
301 if keyword in schema:
302 schema_value = schema[keyword]
303 if validator.is_type(schema_value, "boolean") and schema_value:
304 evaluated_keys += instance.keys()
306 elif validator.is_type(schema_value, "object"):
307 for property in schema_value:
308 if property in instance:
309 evaluated_keys.append(property)
311 if "patternProperties" in schema:
312 for property in instance:
313 for pattern in schema["patternProperties"]:
314 if re.search(pattern, property):
315 evaluated_keys.append(property)
317 if "dependentSchemas" in schema:
318 for property, subschema in schema["dependentSchemas"].items():
319 if property not in instance:
320 continue
321 evaluated_keys += find_evaluated_property_keys_by_schema(
322 validator, instance, subschema,
323 )
325 for keyword in ["allOf", "oneOf", "anyOf"]:
326 if keyword in schema:
327 for subschema in schema[keyword]:
328 errs = next(validator.descend(instance, subschema), None)
329 if errs is None:
330 evaluated_keys += find_evaluated_property_keys_by_schema(
331 validator, instance, subschema,
332 )
334 if "if" in schema:
335 if validator.evolve(schema=schema["if"]).is_valid(instance):
336 evaluated_keys += find_evaluated_property_keys_by_schema(
337 validator, instance, schema["if"],
338 )
339 if "then" in schema:
340 evaluated_keys += find_evaluated_property_keys_by_schema(
341 validator, instance, schema["then"],
342 )
343 elif "else" in schema:
344 evaluated_keys += find_evaluated_property_keys_by_schema(
345 validator, instance, schema["else"],
346 )
348 return evaluated_keys