Skip to content

Commit ba9375c

Browse files
committed
Enhance best match to prefer errors from matching types.
Closes: #728
1 parent 28a7598 commit ba9375c

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

jsonschema/exceptions.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def __init__(
3131
schema=_unset,
3232
schema_path=(),
3333
parent=None,
34+
type_checker=_unset,
3435
):
3536
super(_Error, self).__init__(
3637
message,
@@ -54,6 +55,7 @@ def __init__(
5455
self.instance = instance
5556
self.schema = schema
5657
self.parent = parent
58+
self._type_checker = type_checker
5759

5860
for error in context:
5961
error.parent = self
@@ -124,7 +126,10 @@ def json_path(self):
124126
path += "." + elem
125127
return path
126128

127-
def _set(self, **kwargs):
129+
def _set(self, type_checker=None, **kwargs):
130+
if type_checker is not None and self._type_checker is _unset:
131+
self._type_checker = type_checker
132+
128133
for k, v in kwargs.items():
129134
if getattr(self, k) is _unset:
130135
setattr(self, k, v)
@@ -136,6 +141,14 @@ def _contents(self):
136141
)
137142
return dict((attr, getattr(self, attr)) for attr in attrs)
138143

144+
def _matches_type(self):
145+
try:
146+
expected_type = self.schema["type"]
147+
except (KeyError, TypeError):
148+
return False
149+
else:
150+
return self._type_checker.is_type(self.instance, expected_type)
151+
139152

140153
class ValidationError(_Error):
141154
"""
@@ -307,7 +320,12 @@ def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES):
307320
"""
308321
def relevance(error):
309322
validator = error.validator
310-
return -len(error.path), validator not in weak, validator in strong
323+
return (
324+
-len(error.path),
325+
validator not in weak,
326+
validator in strong,
327+
not error._matches_type(),
328+
)
311329
return relevance
312330

313331

jsonschema/tests/test_exceptions.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,38 @@ def test_nested_context_for_oneOf(self):
131131
best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema)
132132
self.assertEqual(best.validator_value, "array")
133133

134+
def test_it_prioritizes_matching_types(self):
135+
schema = {
136+
"properties": {
137+
"foo": {
138+
"anyOf": [
139+
{"type": "array", "minItems": 2},
140+
{"type": "string", "minLength": 10},
141+
],
142+
},
143+
},
144+
}
145+
best = self.best_match_of(instance={"foo": "bar"}, schema=schema)
146+
self.assertEqual(best.validator, "minLength")
147+
148+
reordered = {
149+
"properties": {
150+
"foo": {
151+
"anyOf": [
152+
{"type": "string", "minLength": 10},
153+
{"type": "array", "minItems": 2},
154+
],
155+
},
156+
},
157+
}
158+
best = self.best_match_of(instance={"foo": "bar"}, schema=reordered)
159+
self.assertEqual(best.validator, "minLength")
160+
161+
def test_boolean_schemas(self):
162+
schema = {"properties": {"foo": False}}
163+
best = self.best_match_of(instance={"foo": "bar"}, schema=schema)
164+
self.assertIsNone(best.validator)
165+
134166
def test_one_error(self):
135167
validator = _LATEST_VERSION({"minProperties": 2})
136168
error, = validator.iter_errors({})

jsonschema/validators.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def iter_errors(self, instance, _schema=None):
246246
validator_value=v,
247247
instance=instance,
248248
schema=_schema,
249+
type_checker=self.TYPE_CHECKER,
249250
)
250251
if k not in {"if", "$ref"}:
251252
error.schema_path.appendleft(k)

0 commit comments

Comments
 (0)