Skip to content

Commit ee77589

Browse files
committed
Reimplement contains.
Fixes a bug where maxContains was being ignored if minContains was set to 0. Also now short circuits as soon as more than maxContains is reached rather than continuing to validate.
1 parent 00031cb commit ee77589

File tree

2 files changed

+25
-51
lines changed

2 files changed

+25
-51
lines changed

jsonschema/_validators.py

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -119,55 +119,31 @@ def contains(validator, contains, instance, schema):
119119
if not validator.is_type(instance, "array"):
120120
return
121121

122-
min_contains = max_contains = None
123-
124-
if "minContains" in schema:
125-
min_contains = schema["minContains"]
126-
127-
if "maxContains" in schema:
128-
max_contains = schema["maxContains"]
129-
130-
# minContains set to 0 will ignore contains
131-
if min_contains == 0:
132-
return
133-
134-
matches = sum(1 for each in instance if validator.is_valid(each, contains))
135-
136-
if not matches:
137-
yield ValidationError(
138-
f"{instance!r} does not contain items matching the given schema",
139-
)
140-
return
141-
142-
if min_contains and max_contains is None:
143-
if matches < min_contains:
144-
yield ValidationError(
145-
"Too few items match the given schema "
146-
f"(expected {min_contains} but only {matches} matched)",
147-
)
148-
return
149-
150-
if min_contains is None and max_contains:
151-
if matches > max_contains:
122+
matches = 0
123+
min_contains = schema.get("minContains", 1)
124+
max_contains = schema.get("maxContains", len(instance))
125+
126+
for each in instance:
127+
if validator.is_valid(each, contains):
128+
matches += 1
129+
if matches > max_contains:
130+
yield ValidationError(
131+
"Too many items match the given schema "
132+
f"(expected at most {max_contains})",
133+
)
134+
return
135+
136+
if matches < min_contains:
137+
if not matches:
152138
yield ValidationError(
153-
"Too many items match the given schema "
154-
f"(expected at most {max_contains} but {matches} matched)",
139+
f"{instance!r} does not contain items "
140+
"matching the given schema",
155141
)
156-
return
157-
158-
if min_contains and max_contains:
159-
if matches < min_contains:
160-
only = "only "
161-
elif matches > max_contains:
162-
only = ""
163142
else:
164-
only = None
165-
if only is not None:
166143
yield ValidationError(
167-
f"Expected between {min_contains} and {max_contains} items "
168-
f"to match the given schema but {only}{matches} matched",
144+
"Too few items match the given schema (expected at least "
145+
f"{min_contains} but only {matches} matched)",
169146
)
170-
return
171147

172148

173149
def exclusiveMinimum(validator, minimum, instance, schema):

jsonschema/tests/test_validators.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ def test_contains_too_few(self):
464464
self.assertEqual(
465465
message,
466466
"Too few items match the given schema "
467-
"(expected 2 but only 1 matched)",
467+
"(expected at least 2 but only 1 matched)",
468468
)
469469

470470
def test_contains_too_few_both_constrained(self):
@@ -478,8 +478,8 @@ def test_contains_too_few_both_constrained(self):
478478
)
479479
self.assertEqual(
480480
message,
481-
"Expected between 2 and 4 items to match the given schema but "
482-
"only 1 matched",
481+
"Too few items match the given schema (expected at least 2 but "
482+
"only 1 matched)",
483483
)
484484

485485
def test_contains_too_many(self):
@@ -489,8 +489,7 @@ def test_contains_too_many(self):
489489
)
490490
self.assertEqual(
491491
message,
492-
"Too many items match the given schema "
493-
"(expected at most 2 but 4 matched)",
492+
"Too many items match the given schema (expected at most 2)",
494493
)
495494

496495
def test_contains_too_many_both_constrained(self):
@@ -504,8 +503,7 @@ def test_contains_too_many_both_constrained(self):
504503
)
505504
self.assertEqual(
506505
message,
507-
"Expected between 2 and 4 items to match the given schema but "
508-
"7 matched",
506+
"Too many items match the given schema (expected at most 4)",
509507
)
510508

511509
def test_exclusiveMinimum(self):

0 commit comments

Comments
 (0)