Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jsonschema/_utils.py: 22%
150 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:30 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:30 +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):
32 return len(self.store)
34 def __repr__(self):
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 sorted(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 if "$ref" in schema:
201 resolved = validator._resolver.lookup(schema["$ref"])
202 evaluated_indexes.extend(
203 find_evaluated_item_indexes_by_schema(
204 validator.evolve(
205 schema=resolved.contents,
206 _resolver=resolved.resolver,
207 ),
208 instance,
209 resolved.contents,
210 ),
211 )
213 if "prefixItems" in schema:
214 evaluated_indexes += list(range(0, len(schema["prefixItems"])))
216 if "if" in schema:
217 if validator.evolve(schema=schema["if"]).is_valid(instance):
218 evaluated_indexes += find_evaluated_item_indexes_by_schema(
219 validator, instance, schema["if"],
220 )
221 if "then" in schema:
222 evaluated_indexes += find_evaluated_item_indexes_by_schema(
223 validator, instance, schema["then"],
224 )
225 else:
226 if "else" in schema:
227 evaluated_indexes += find_evaluated_item_indexes_by_schema(
228 validator, instance, schema["else"],
229 )
231 for keyword in ["contains", "unevaluatedItems"]:
232 if keyword in schema:
233 for k, v in enumerate(instance):
234 if validator.evolve(schema=schema[keyword]).is_valid(v):
235 evaluated_indexes.append(k)
237 for keyword in ["allOf", "oneOf", "anyOf"]:
238 if keyword in schema:
239 for subschema in schema[keyword]:
240 errs = next(validator.descend(instance, subschema), None)
241 if errs is None:
242 evaluated_indexes += find_evaluated_item_indexes_by_schema(
243 validator, instance, subschema,
244 )
246 return evaluated_indexes
249def find_evaluated_property_keys_by_schema(validator, instance, schema):
250 """
251 Get all keys of items that get evaluated under the current schema
253 Covers all keywords related to unevaluatedProperties: properties,
254 additionalProperties, unevaluatedProperties, patternProperties,
255 dependentSchemas, allOf, oneOf, anyOf, if, then, else
256 """
257 if validator.is_type(schema, "boolean"):
258 return []
259 evaluated_keys = []
261 if "$ref" in schema:
262 resolved = validator._resolver.lookup(schema["$ref"])
263 evaluated_keys.extend(
264 find_evaluated_property_keys_by_schema(
265 validator.evolve(
266 schema=resolved.contents,
267 _resolver=resolved.resolver,
268 ),
269 instance,
270 resolved.contents,
271 ),
272 )
274 for keyword in [
275 "properties", "additionalProperties", "unevaluatedProperties",
276 ]:
277 if keyword in schema:
278 schema_value = schema[keyword]
279 if validator.is_type(schema_value, "boolean") and schema_value:
280 evaluated_keys += instance.keys()
282 elif validator.is_type(schema_value, "object"):
283 for property in schema_value:
284 if property in instance:
285 evaluated_keys.append(property)
287 if "patternProperties" in schema:
288 for property in instance:
289 for pattern in schema["patternProperties"]:
290 if re.search(pattern, property):
291 evaluated_keys.append(property)
293 if "dependentSchemas" in schema:
294 for property, subschema in schema["dependentSchemas"].items():
295 if property not in instance:
296 continue
297 evaluated_keys += find_evaluated_property_keys_by_schema(
298 validator, instance, subschema,
299 )
301 for keyword in ["allOf", "oneOf", "anyOf"]:
302 if keyword in schema:
303 for subschema in schema[keyword]:
304 errs = next(validator.descend(instance, subschema), None)
305 if errs is None:
306 evaluated_keys += find_evaluated_property_keys_by_schema(
307 validator, instance, subschema,
308 )
310 if "if" in schema:
311 if validator.evolve(schema=schema["if"]).is_valid(instance):
312 evaluated_keys += find_evaluated_property_keys_by_schema(
313 validator, instance, schema["if"],
314 )
315 if "then" in schema:
316 evaluated_keys += find_evaluated_property_keys_by_schema(
317 validator, instance, schema["then"],
318 )
319 else:
320 if "else" in schema:
321 evaluated_keys += find_evaluated_property_keys_by_schema(
322 validator, instance, schema["else"],
323 )
325 return evaluated_keys