From c9a311e17c6274164efc47e1d4319f4c28fd4ebf Mon Sep 17 00:00:00 2001 From: Alexander Shadchin Date: Sun, 23 Aug 2026 14:42:29 +0300 Subject: [PATCH] Fix relative refs in handler-loaded schemas Avoid resolving relative refs twice when walking a remotely loaded schema. Fixes horejsek/python-fastjsonschema#218 --- fastjsonschema/ref_resolver.py | 13 +++++++------ tests/test_integration.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/fastjsonschema/ref_resolver.py b/fastjsonschema/ref_resolver.py index 838119a..d8dc20f 100644 --- a/fastjsonschema/ref_resolver.py +++ b/fastjsonschema/ref_resolver.py @@ -163,7 +163,7 @@ def _ensure_walked(self, uri, schema): normalized = normalize(uri) if uri else '' if normalized in self._walked_uris: return - self.walk(schema) + self.walk(schema, rewrite_refs=False) self._walked_uris.add(normalized) def get_uri(self): @@ -178,7 +178,7 @@ def get_scope_name(self): name = name.lower().rstrip('_') return name - def walk(self, node: dict, depth=0): + def walk(self, node: dict, depth=0, rewrite_refs=True): """ Walk thru schema and dereferencing ``id`` and ``$ref`` instances """ @@ -190,15 +190,16 @@ def walk(self, node: dict, depth=0): if isinstance(node, bool): pass elif '$ref' in node and isinstance(node['$ref'], str): - ref = node['$ref'] - node['$ref'] = urlparse.urljoin(self.resolution_scope, ref) + if rewrite_refs: + ref = node['$ref'] + node['$ref'] = urlparse.urljoin(self.resolution_scope, ref) elif ('$id' in node or 'id' in node) and isinstance(get_id(node), str): with self.in_scope(get_id(node)): self.store[normalize(self.resolution_scope)] = node for _, item in node.items(): if isinstance(item, dict): - self.walk(item, depth + 1) + self.walk(item, depth + 1, rewrite_refs) else: for _, item in node.items(): if isinstance(item, dict): - self.walk(item, depth + 1) + self.walk(item, depth + 1, rewrite_refs) diff --git a/tests/test_integration.py b/tests/test_integration.py index 4ff8ee6..1ee578b 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -146,3 +146,24 @@ def test_swap_handlers(): } validator2 = compile({"$ref": "sch://schema"}, handlers={"sch": repo2.__getitem__}) assert validator2("hello world") is not None + + +def test_relative_ref_in_remote_schema_is_resolved_once(): + repo = { + 'schemas/main.json': { + 'definitions': { + 'object': { + 'type': 'object', + 'properties': {'value': {'$ref': 'main.json#/definitions/string'}}, + }, + 'string': {'type': 'string'}, + }, + }, + } + + validator = compile( + {'$ref': 'schemas/main.json#/definitions/object'}, + handlers={'': repo.__getitem__}, + ) + + assert validator({'value': 'ok'}) == {'value': 'ok'}