From 7feaa0adf63940d866b9e78590d0bbfed79973e5 Mon Sep 17 00:00:00 2001 From: eterna2 Date: Sat, 17 Aug 2019 00:30:53 +0800 Subject: [PATCH 1/4] Fix #570 - resolve relative ref --- jsonschema/tests/test_validators.py | 9 +++++++++ jsonschema/validators.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index e4572c5cd..865ed1667 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -1664,6 +1664,15 @@ def test_it_retrieves_local_refs_via_urlopen(self): with self.resolver.resolving(ref) as resolved: self.assertEqual(resolved, "bar") + def test_it_retrieves_local_relative_refs_via_urlopen(self): + with tempfile.NamedTemporaryFile(delete=False, mode="wt") as tempf: + self.addCleanup(os.remove, tempf.name) + json.dump({"foo": "bar"}, tempf) + + ref = "{}#foo".format(pathname2url(tempf.name)) + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, "bar") + def test_it_can_construct_a_base_uri_from_a_schema(self): schema = {"id": "foo"} resolver = validators.RefResolver.from_schema( diff --git a/jsonschema/validators.py b/jsonschema/validators.py index e1ea871e9..da3b082c3 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -4,6 +4,7 @@ import contextlib import json import numbers +import os from six import add_metaclass @@ -826,9 +827,14 @@ def resolve_remote(self, uri): # json over http result = requests.get(uri).json() else: - # Otherwise, pass off to urllib and assume utf-8 - with urlopen(uri) as url: - result = json.loads(url.read().decode("utf-8")) + try: + # Otherwise, pass off to urllib and assume utf-8 + with urlopen(uri) as url: + result = json.loads(url.read().decode("utf-8")) + except Exception: + # try relative path on local fs + with open(os.path.join(self.base_uri, uri)) as fin: + result = json.load(fin) if self.cache_remote: self.store[uri] = result From 484b41f55606da2797525c3e0165e9569b8e0053 Mon Sep 17 00:00:00 2001 From: eterna2 Date: Sat, 17 Aug 2019 01:45:45 +0800 Subject: [PATCH 2/4] Update to support relative remote url --- jsonschema/tests/test_validators.py | 26 +++++++++++++++++++++++++- jsonschema/validators.py | 15 ++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 865ed1667..ec34af6d9 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -1664,7 +1664,7 @@ def test_it_retrieves_local_refs_via_urlopen(self): with self.resolver.resolving(ref) as resolved: self.assertEqual(resolved, "bar") - def test_it_retrieves_local_relative_refs_via_urlopen(self): + def test_it_retrieves_local_relative_refs_via_open(self): with tempfile.NamedTemporaryFile(delete=False, mode="wt") as tempf: self.addCleanup(os.remove, tempf.name) json.dump({"foo": "bar"}, tempf) @@ -1673,6 +1673,30 @@ def test_it_retrieves_local_relative_refs_via_urlopen(self): with self.resolver.resolving(ref) as resolved: self.assertEqual(resolved, "bar") + def test_it_retrieves_relative_refs_via_urlopen(self): + ref = "fuzz.json#baz" + schema = {"baz": 12} + resolver = validators.RefResolver( + "http://foo.com/bar.json", self.referrer, self.store, + ) + if "requests" in sys.modules: + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = None + + @contextmanager + def fake_urlopen(url): + self.assertEqual(url, "http://foo.com/fuzz.json") + yield BytesIO(json.dumps(schema).encode("utf8")) + + self.addCleanup(setattr, validators, "urlopen", validators.urlopen) + validators.urlopen = fake_urlopen + + with resolver.resolving(ref) as resolved: + pass + self.assertEqual(resolved, 12) + def test_it_can_construct_a_base_uri_from_a_schema(self): schema = {"id": "foo"} resolver = validators.RefResolver.from_schema( diff --git a/jsonschema/validators.py b/jsonschema/validators.py index da3b082c3..61f7fec8b 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -26,6 +26,7 @@ urljoin, urlopen, urlsplit, + urlunsplit, ) # Sigh. https://gitlab.com/pycqa/flake8/issues/280 @@ -832,9 +833,17 @@ def resolve_remote(self, uri): with urlopen(uri) as url: result = json.loads(url.read().decode("utf-8")) except Exception: - # try relative path on local fs - with open(os.path.join(self.base_uri, uri)) as fin: - result = json.load(fin) + try: + # try relative path on local fs + with open(os.path.join(self.base_uri, uri)) as fin: + result = json.load(fin) + except IOError: + scheme, network, path, qs, frag = urlsplit(self.base_uri) + path = "/".join(path.split("/")[0:-1]) # base path + base_url = urlunsplit((scheme, network, path, qs, frag)) + # point to relative url + with urlopen(urljoin(base_url, uri)) as url: + result = json.loads(url.read().decode("utf-8")) if self.cache_remote: self.store[uri] = result From 44aca14992e84930346c8c916d2a443d08a54996 Mon Sep 17 00:00:00 2001 From: eterna2 Date: Sat, 17 Aug 2019 01:55:37 +0800 Subject: [PATCH 3/4] Fix pypy lint error --- jsonschema/tests/test_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index ec34af6d9..5720d43d3 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -1695,7 +1695,7 @@ def fake_urlopen(url): with resolver.resolving(ref) as resolved: pass - self.assertEqual(resolved, 12) + self.assertEqual(resolved, 12) def test_it_can_construct_a_base_uri_from_a_schema(self): schema = {"id": "foo"} From 73f81f959e47d5eb369ac1d35d3fd7333432fdc7 Mon Sep 17 00:00:00 2001 From: eterna2 Date: Sat, 17 Aug 2019 02:21:17 +0800 Subject: [PATCH 4/4] Fix windows unable to get correct relative path for unit test. Fix gitignore. --- .gitignore | 5 +++++ jsonschema/tests/test_validators.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 31236db57..36b41f6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,9 @@ _cache _static _templates +.eggs +.tox + +*.py[cod] + TODO diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 5720d43d3..7d6430b9f 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -1669,7 +1669,7 @@ def test_it_retrieves_local_relative_refs_via_open(self): self.addCleanup(os.remove, tempf.name) json.dump({"foo": "bar"}, tempf) - ref = "{}#foo".format(pathname2url(tempf.name)) + ref = "{}#foo".format(os.path.relpath(tempf.name)) with self.resolver.resolving(ref) as resolved: self.assertEqual(resolved, "bar")