Skip to content

Commit 1e6b983

Browse files
committed
Fix unevaluatedItems on draft2019.
This was trivial, other than needing to copy paste the function which anyhow needs removal. It only didn't work previously because of the items -> prefixItems rename in draft2020.
1 parent 79195f2 commit 1e6b983

File tree

4 files changed

+82
-59
lines changed

4 files changed

+82
-59
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ v4.14.0
33

44
* ``FormatChecker.cls_checks`` is deprecated. Use ``FormatChecker.checks`` on
55
an instance of ``FormatChecker`` instead.
6-
* ``unevaluatedItems`` has been fixed on draft 2019. It's nonetheless
7-
discouraged to use draft2019 for any schemas, new or old.
6+
* ``unevaluatedItems`` has been fixed for draft 2019. It's nonetheless
7+
discouraged to use draft 2019 for any schemas, new or old.
88

99
v4.13.0
1010
=======

jsonschema/_legacy_validators.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,81 @@ def recursiveRef(validator, recursiveRef, instance, schema):
226226
# see it.
227227
subschema
228228
return []
229+
230+
231+
def find_evaluated_item_indexes_by_schema(validator, instance, schema):
232+
"""
233+
Get all indexes of items that get evaluated under the current schema
234+
235+
Covers all keywords related to unevaluatedItems: items, prefixItems, if,
236+
then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
237+
"""
238+
if validator.is_type(schema, "boolean"):
239+
return []
240+
evaluated_indexes = []
241+
242+
if "additionalItems" in schema:
243+
return list(range(0, len(instance)))
244+
245+
if "$ref" in schema:
246+
scope, resolved = validator.resolver.resolve(schema["$ref"])
247+
validator.resolver.push_scope(scope)
248+
249+
try:
250+
evaluated_indexes += find_evaluated_item_indexes_by_schema(
251+
validator, instance, resolved,
252+
)
253+
finally:
254+
validator.resolver.pop_scope()
255+
256+
if "items" in schema:
257+
if validator.is_type(schema["items"], "object"):
258+
return list(range(0, len(instance)))
259+
evaluated_indexes += list(range(0, len(schema["items"])))
260+
261+
if "if" in schema:
262+
if validator.evolve(schema=schema["if"]).is_valid(instance):
263+
evaluated_indexes += find_evaluated_item_indexes_by_schema(
264+
validator, instance, schema["if"],
265+
)
266+
if "then" in schema:
267+
evaluated_indexes += find_evaluated_item_indexes_by_schema(
268+
validator, instance, schema["then"],
269+
)
270+
else:
271+
if "else" in schema:
272+
evaluated_indexes += find_evaluated_item_indexes_by_schema(
273+
validator, instance, schema["else"],
274+
)
275+
276+
for keyword in ["contains", "unevaluatedItems"]:
277+
if keyword in schema:
278+
for k, v in enumerate(instance):
279+
if validator.evolve(schema=schema[keyword]).is_valid(v):
280+
evaluated_indexes.append(k)
281+
282+
for keyword in ["allOf", "oneOf", "anyOf"]:
283+
if keyword in schema:
284+
for subschema in schema[keyword]:
285+
errs = list(validator.descend(instance, subschema))
286+
if not errs:
287+
evaluated_indexes += find_evaluated_item_indexes_by_schema(
288+
validator, instance, subschema,
289+
)
290+
291+
return evaluated_indexes
292+
293+
294+
def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema):
295+
if not validator.is_type(instance, "array"):
296+
return
297+
evaluated_item_indexes = find_evaluated_item_indexes_by_schema(
298+
validator, instance, schema,
299+
)
300+
unevaluated_items = [
301+
item for index, item in enumerate(instance)
302+
if index not in evaluated_item_indexes
303+
]
304+
if unevaluated_items:
305+
error = "Unevaluated items are not allowed (%s %s unexpected)"
306+
yield ValidationError(error % _utils.extras_msg(unevaluated_items))

jsonschema/tests/test_jsonschema_test_suite.py

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import sys
1010

11-
from jsonschema.tests._helpers import bug, test_suite_bug
11+
from jsonschema.tests._helpers import bug
1212
from jsonschema.tests._suite import Suite
1313
import jsonschema
1414

@@ -331,61 +331,6 @@ def leap_second(test):
331331
Validator=jsonschema.Draft201909Validator,
332332
skip=lambda test: (
333333
skip(
334-
message="unevaluatedItems is different in 2019-09 (needs work).",
335-
subject="unevaluatedItems",
336-
description="uncle keyword evaluation is not significant",
337-
)(test)
338-
or skip(
339-
message="unevaluatedItems is different in 2019-09 (needs work).",
340-
subject="unevaluatedItems",
341-
description="when one schema matches and has unevaluated items",
342-
)(test)
343-
or skip(
344-
message="unevaluatedItems is different in 2019-09 (needs work).",
345-
subject="unevaluatedItems",
346-
description="when two schemas match and has unevaluated items",
347-
)(test)
348-
or skip(
349-
message="unevaluatedItems is different in 2019-09 (needs work).",
350-
subject="unevaluatedItems",
351-
description="when if matches and it has unevaluated items",
352-
)(test)
353-
or skip(
354-
message="unevaluatedItems is different in 2019-09 (needs work).",
355-
subject="unevaluatedItems",
356-
case_description="unevaluatedItems with nested tuple",
357-
description="with unevaluated items",
358-
)(test)
359-
or skip(
360-
message="unevaluatedItems is different in 2019-09 (needs work).",
361-
subject="unevaluatedItems",
362-
case_description="unevaluatedItems with not",
363-
description="with unevaluated items",
364-
)(test)
365-
or skip(
366-
message="unevaluatedItems is different in 2019-09 (needs work).",
367-
subject="unevaluatedItems",
368-
case_description="unevaluatedItems with oneOf",
369-
description="with unevaluated items",
370-
)(test)
371-
or skip(
372-
message="unevaluatedItems is different in 2019-09 (needs work).",
373-
subject="unevaluatedItems",
374-
case_description="unevaluatedItems with $ref",
375-
description="with unevaluated items",
376-
)(test)
377-
or skip(
378-
message="unevaluatedItems is different in 2019-09 (needs work).",
379-
subject="unevaluatedItems",
380-
case_description="unevaluatedItems with tuple",
381-
description="with unevaluated items",
382-
)(test)
383-
or skip(
384-
message="unevaluatedItems is different in 2019-09 (needs work).",
385-
subject="unevaluatedItems",
386-
description="when if doesn't match and it has unevaluated items",
387-
)(test)
388-
or skip(
389334
message="recursiveRef support isn't working yet.",
390335
subject="recursiveRef",
391336
case_description=(

jsonschema/validators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ def extend(
605605
"propertyNames": _validators.propertyNames,
606606
"required": _validators.required,
607607
"type": _validators.type,
608-
"unevaluatedItems": _validators.unevaluatedItems,
608+
"unevaluatedItems": _legacy_validators.unevaluatedItems_draft2019,
609609
"unevaluatedProperties": _validators.unevaluatedProperties,
610610
"uniqueItems": _validators.uniqueItems,
611611
},

0 commit comments

Comments
 (0)