Skip to content

Commit cc12d98

Browse files
committed
Use regex variant when checking schema validity
Rather than relying directly on `validator_cls.check_schema`, reimplement this functionality in-tree here to allow for customizations to the validator class before it is run on the user's schema. A new test uses an invalid regex under the regress unicode engine which is valid in the python engine to ensure that consistent checking is applied. Manual testing revealed that the `_fail()` message production for SchemaError was showing error information twice, which is fixed without a new test to guarantee the new behavior.
1 parent e9aa64e commit cc12d98

File tree

4 files changed

+47
-10
lines changed

4 files changed

+47
-10
lines changed

src/check_jsonschema/checker.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def get_validator(
5959
except SchemaParseError as e:
6060
self._fail("Error: schemafile could not be parsed as JSON", e)
6161
except jsonschema.SchemaError as e:
62-
self._fail(f"Error: schemafile was not valid: {e}\n", e)
62+
self._fail("Error: schemafile was not valid\n", e)
6363
except UnsupportedUrlScheme as e:
6464
self._fail(f"Error: {e}\n", e)
6565
except Exception as e:

src/check_jsonschema/regex_variants.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ def pattern_keyword(
7070
if not validator.is_type(instance, "string"):
7171
return
7272

73-
try:
74-
regress_pattern = self._compile_pattern(pattern)
75-
except regress.RegressError:
76-
yield jsonschema.ValidationError(f"pattern {pattern!r} failed to compile")
73+
regress_pattern = self._compile_pattern(pattern)
7774
if not regress_pattern.find(instance):
7875
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
7976

@@ -120,10 +117,7 @@ def pattern_keyword(
120117
if not validator.is_type(instance, "string"):
121118
return
122119

123-
try:
124-
re_pattern = re.compile(pattern)
125-
except re.error:
126-
yield jsonschema.ValidationError(f"pattern {pattern!r} failed to compile")
120+
re_pattern = re.compile(pattern)
127121
if not re_pattern.search(instance):
128122
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
129123

src/check_jsonschema/schema_loader/main.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,12 @@ def _get_validator(
170170
if self.validator_class is None:
171171
# get the correct validator class and check the schema under its metaschema
172172
validator_cls = jsonschema.validators.validator_for(schema)
173-
validator_cls.check_schema(schema, format_checker=format_checker)
173+
_check_schema(
174+
validator_cls,
175+
schema,
176+
format_checker=format_checker,
177+
regex_impl=regex_impl,
178+
)
174179
else:
175180
# for a user-provided validator class, don't check_schema
176181
# on the grounds that it might *not* be valid but the user wants to use
@@ -197,6 +202,32 @@ def _get_validator(
197202
return t.cast(jsonschema.protocols.Validator, validator)
198203

199204

205+
def _check_schema(
206+
validator_cls: type[jsonschema.protocols.Validator],
207+
schema: dict[str, t.Any],
208+
*,
209+
format_checker: jsonschema.FormatChecker | None,
210+
regex_impl: RegexImplementation,
211+
) -> None:
212+
"""A variant definition of Validator.check_schema which uses the regex
213+
implementation and format checker specified."""
214+
schema_validator_cls = jsonschema.validators.validator_for(
215+
validator_cls.META_SCHEMA, default=validator_cls
216+
)
217+
schema_validator_cls = _extend_with_pattern_implementation(
218+
schema_validator_cls, regex_impl
219+
)
220+
221+
if format_checker is None:
222+
format_checker = schema_validator_cls.FORMAT_CHECKER
223+
224+
schema_validator = schema_validator_cls(
225+
validator_cls.META_SCHEMA, format_checker=format_checker
226+
)
227+
for error in schema_validator.iter_errors(schema):
228+
raise jsonschema.exceptions.SchemaError.create_from(error)
229+
230+
200231
class BuiltinSchemaLoader(SchemaLoader):
201232
def __init__(self, schema_name: str, *, base_uri: str | None = None) -> None:
202233
self.schema_name = schema_name

tests/acceptance/test_invalid_schema_files.py

+12
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,15 @@ def test_checker_invalid_schemafile_scheme(run_line, tmp_path):
2929
res = run_line(["check-jsonschema", "--schemafile", f"ftp://{foo}", str(bar)])
3030
assert res.exit_code == 1
3131
assert "only supports http, https" in res.stderr
32+
33+
34+
def test_checker_invalid_schemafile_due_to_bad_regex(run_line, tmp_path):
35+
foo = tmp_path / "foo.json"
36+
bar = tmp_path / "bar.json"
37+
# too many backslash escapes -- not a valid Unicode-mode regex
38+
foo.write_text(r'{"properties": {"foo": {"pattern": "\\\\p{N}"}}}')
39+
bar.write_text("{}")
40+
41+
res = run_line(["check-jsonschema", "--schemafile", str(foo), str(bar)])
42+
assert res.exit_code == 1
43+
assert "schemafile was not valid" in res.stderr

0 commit comments

Comments
 (0)