Fix nested local reference resolution - #700
Conversation
27d581e to
13251e5
Compare
|
Hi, thanks for this! It looks more correct than the last fix someone sent, but I don't quite understand why it should work. The current reference registry is built such that it's supposed to fallback to the retrieval URI if the Do you know why this is effective, when the lines here are meant to have a similar effect already? |
|
You're right. The registry had the root resource under its retrieval URI, but jsonschema built a separate resolver from the schema contents. Without I removed the schema rewrite and now start the resolver at the retrieval URI while retaining the standard specification registry. I also added a failing nested case to prove the third schema is applied. |
| if retrieval_uri is not None and not isinstance(schema.get("$id"), str): | ||
| reference_resolver = SPECIFICATIONS.combine(reference_registry).resolver( | ||
| retrieval_uri | ||
| ) |
There was a problem hiding this comment.
What does the call to SPECIFICATIONS.combine() do here? I was just experimenting with a patch using reference_registry.resolver(), which also seems to work.
It may be correct, but I'd like to know why before proceeding.
Also, if we are adding direct use of jsonschema_specifications, it should be added as a direct dependency, even if the version is not bounded.
|
Assuming this higher level fix is put in place, updating the resolver base URI, the retrieval method should be simplified. I just tested a version with a similar patch to the one in this PR, but with the retrieval callback updated to get rid of redundant path joining, and everything seemed to pass tests and work. As a possible starting point, here's the change I tested for the callbackdiff --git a/src/check_jsonschema/schema_loader/resolver.py b/src/check_jsonschema/schema_loader/resolver.py
index 15344d6..ddf2f80 100644
--- a/src/check_jsonschema/schema_loader/resolver.py
+++ b/src/check_jsonschema/schema_loader/resolver.py
@@ -26,9 +26,7 @@ def make_reference_registry(
# mypy does not recognize that Registry is an `attrs` class and has `retrieve` as an
# argument to its implicit initializer
registry: referencing.Registry = referencing.Registry( # type: ignore[call-arg]
- retrieve=create_retrieve_callable(
- parsers, retrieval_uri, id_attribute, disable_cache
- )
+ retrieve=create_retrieve_callable(parsers, disable_cache)
)
if retrieval_uri is not None:
@@ -41,14 +39,8 @@ def make_reference_registry(
def create_retrieve_callable(
parser_set: ParserSet,
- retrieval_uri: str | None,
- id_attribute: str | None,
disable_cache: bool,
) -> t.Callable[[str], referencing.Resource[Schema]]:
- base_uri = id_attribute
- if base_uri is None:
- base_uri = retrieval_uri
-
cache = ResourceCache()
downloader = CacheDownloader("refs", disable_cache=disable_cache)
@@ -57,33 +49,26 @@ def create_retrieve_callable(
return parser_set.parse_file(path, "json")
def retrieve_reference(uri: str) -> referencing.Resource[Schema]:
- scheme = urllib.parse.urlsplit(uri).scheme
- if scheme == "" and base_uri is not None:
- full_uri = urllib.parse.urljoin(base_uri, uri)
- else:
- full_uri = uri
+ if uri in cache:
+ return cache[uri]
- if full_uri in cache:
- return cache[full_uri]
-
- full_uri_scheme = urllib.parse.urlsplit(full_uri).scheme
- if full_uri_scheme in ("http", "https"):
+ uri_scheme = urllib.parse.urlsplit(uri).scheme
+ if uri_scheme in ("http", "https"):
def validation_callback(content: bytes) -> None:
- parser_set.parse_data_with_path(content, full_uri, "json")
+ parser_set.parse_data_with_path(content, uri, "json")
bound_downloader = downloader.bind(
- full_uri, validation_callback=validation_callback
+ uri, validation_callback=validation_callback
)
with bound_downloader.open() as fp:
data = fp.read()
- parsed_object = parser_set.parse_data_with_path(data, full_uri, "json")
+ parsed_object = parser_set.parse_data_with_path(data, uri, "json")
else:
- parsed_object = get_local_file(full_uri)
+ parsed_object = get_local_file(uri)
cache[full_uri] = parsed_object
return cache[full_uri] |
Uses the root schema's retrieval URI as the referencing resolver base when
$idis absent, so nested local refs resolve from the document that declares them without altering schema contents.Adds acceptance coverage for both valid and invalid values through a three-file nested ref chain.
Local checks: 607 tests passed, 20 skipped; minimum jsonschema 4.18 coverage, mypy, and pre-commit pass.
Fixes #640