Skip to content

Commit 5911287

Browse files
authored
Merge pull request #721 from willson-chen/fix_issue669
Fix issue #669 and add test case.
2 parents e48d56c + d106511 commit 5911287

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

jsonschema/cli.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from jsonschema import __version__
1616
from jsonschema._reflect import namedAny
1717
from jsonschema.exceptions import SchemaError
18-
from jsonschema.validators import validator_for
18+
from jsonschema.validators import RefResolver, validator_for
1919

2020

2121
class _CannotLoadFile(Exception):
@@ -178,6 +178,14 @@ def _namedAnyWithDefault(name):
178178
of the class.
179179
""",
180180
)
181+
parser.add_argument(
182+
"--base-uri",
183+
help="""
184+
a base URI to assign to the provided schema, even if it does not
185+
declare one (via e.g. $id). This option can be used if you wish to
186+
resolve relative references to a particular URI (or local path)
187+
""",
188+
)
181189
parser.add_argument(
182190
"--version",
183191
action="version",
@@ -252,7 +260,12 @@ def load(_):
252260
raise _CannotLoadFile()
253261
instances = ["<stdin>"]
254262

255-
validator = arguments["validator"](schema)
263+
resolver = RefResolver(
264+
base_uri=arguments["base_uri"],
265+
referrer=schema,
266+
) if arguments["base_uri"] is not None else None
267+
268+
validator = arguments["validator"](schema, resolver=resolver)
256269
exit_code = 0
257270
for each in instances:
258271
try:

jsonschema/tests/test_cli.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
from io import StringIO
22
from json import JSONDecodeError
3+
from pathlib import Path
34
from textwrap import dedent
45
from unittest import TestCase
56
import errno
67
import json
78
import os
89
import subprocess
910
import sys
11+
import tempfile
1012

1113
from jsonschema import Draft4Validator, Draft7Validator, __version__, cli
12-
from jsonschema.exceptions import SchemaError, ValidationError
14+
from jsonschema.exceptions import (
15+
RefResolutionError,
16+
SchemaError,
17+
ValidationError,
18+
)
1319
from jsonschema.tests._helpers import captured_output
1420
from jsonschema.validators import _LATEST_VERSION, validate
1521

@@ -683,6 +689,87 @@ def test_successful_validation_of_just_the_schema_pretty_output(self):
683689
stderr="",
684690
)
685691

692+
def test_successful_validation_via_explicit_base_uri(self):
693+
ref_schema_file = tempfile.NamedTemporaryFile(delete=False)
694+
self.addCleanup(os.remove, ref_schema_file.name)
695+
696+
ref_path = Path(ref_schema_file.name)
697+
ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}')
698+
699+
schema = f'{{"$ref": "{ref_path.name}#definitions/num"}}'
700+
701+
self.assertOutputs(
702+
files=dict(some_schema=schema, some_instance='1'),
703+
argv=[
704+
"-i", "some_instance",
705+
"--base-uri", ref_path.parent.as_uri() + "/",
706+
"some_schema",
707+
],
708+
stdout="",
709+
stderr="",
710+
)
711+
712+
def test_unsuccessful_validation_via_explicit_base_uri(self):
713+
ref_schema_file = tempfile.NamedTemporaryFile(delete=False)
714+
self.addCleanup(os.remove, ref_schema_file.name)
715+
716+
ref_path = Path(ref_schema_file.name)
717+
ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}')
718+
719+
schema = f'{{"$ref": "{ref_path.name}#definitions/num"}}'
720+
721+
self.assertOutputs(
722+
files=dict(some_schema=schema, some_instance='"1"'),
723+
argv=[
724+
"-i", "some_instance",
725+
"--base-uri", ref_path.parent.as_uri() + "/",
726+
"some_schema",
727+
],
728+
exit_code=1,
729+
stdout="",
730+
stderr="1: '1' is not of type 'integer'\n",
731+
)
732+
733+
def test_nonexistent_file_with_explicit_base_uri(self):
734+
schema = '{"$ref": "someNonexistentFile.json#definitions/num"}'
735+
instance = "1"
736+
737+
with self.assertRaises(RefResolutionError) as e:
738+
self.assertOutputs(
739+
files=dict(
740+
some_schema=schema,
741+
some_instance=instance,
742+
),
743+
argv=[
744+
"-i", "some_instance",
745+
"--base-uri", Path.cwd().as_uri(),
746+
"some_schema",
747+
],
748+
)
749+
error = str(e.exception)
750+
self.assertIn("/someNonexistentFile.json'", error)
751+
752+
def test_invalid_exlicit_base_uri(self):
753+
schema = '{"$ref": "foo.json#definitions/num"}'
754+
instance = "1"
755+
756+
with self.assertRaises(RefResolutionError) as e:
757+
self.assertOutputs(
758+
files=dict(
759+
some_schema=schema,
760+
some_instance=instance,
761+
),
762+
argv=[
763+
"-i", "some_instance",
764+
"--base-uri", "not@UR1",
765+
"some_schema",
766+
],
767+
)
768+
error = str(e.exception)
769+
self.assertEqual(
770+
error, "unknown url type: 'foo.json'",
771+
)
772+
686773
def test_it_validates_using_the_latest_validator_when_unspecified(self):
687774
# There isn't a better way now I can think of to ensure that the
688775
# latest version was used, given that the call to validator_for

0 commit comments

Comments
 (0)