Skip to content

refactor: migrate from jsonschema.RefResolver to referencing#2716

Open
henryiii wants to merge 1 commit into
mainfrom
refactor/jsonschema-referencing
Open

refactor: migrate from jsonschema.RefResolver to referencing#2716
henryiii wants to merge 1 commit into
mainfrom
refactor/jsonschema-referencing

Conversation

@henryiii

@henryiii henryiii commented Jun 12, 2026

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Part of the code review in #2706.
Resolves #2139.

Summary

jsonschema.RefResolver has been deprecated since jsonschema 4.18. This migrates pyhf's schema validation to the referencing library:

  • src/pyhf/schema/validator.py now builds a referencing.Registry with a retrieve callback (_retrieve_schema) that loads referenced schemas from disk, and passes registry=... to the jsonschema validator instead of the deprecated resolver=....
  • The pyhf schemas carry a top-level $ref (e.g. model.json -> defs.json#/definitions/model). Under draft-06, a sibling $ref suppresses $id, so jsonschema cannot infer the root schema's base URI. The resolver base URI is therefore anchored explicitly to the schema $id so relative references like defs.json resolve correctly.
  • A new loader.read_schema reads a schema directly from disk without consulting or mutating SCHEMA_CACHE, so resolving $refs does not pollute the cache. This preserves the existing caching semantics, including the pyhf.schema(new_path) custom-schema-path context manager and the InvalidSpecification exception behavior.
  • Bumped jsonschema>=4.15.0 -> jsonschema>=4.18.0 and added referencing>=0.28.4 as an explicit dependency in both the project and pixi dependency tables (it is now imported directly).
  • Removed the now-unneeded RefResolver DeprecationWarning filter. Since pytest runs with filterwarnings=error, any remaining deprecation path would now fail the suite.

Test plan

  • uv pip install --group dev --editable .
  • pytest tests/test_schema.py (70 passed)
  • pytest tests/test_workspace.py tests/test_pdf.py (308 passed, 6 skipped)
  • pytest tests/test_validation.py tests/test_patchset.py tests/test_public_api.py (all pass)
  • pytest --doctest-modules src/pyhf/schema/ (3 passed)
  • prek -a (lint clean)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved JSON schema validation support for newer schema resolution tooling and $ref handling.
  • Bug Fixes

    • Fixed schema loading so referenced schemas resolve more reliably, including nested relative references.
    • Improved missing-schema errors with clearer messages when a schema file can’t be found.
    • Updated test configuration to remove an obsolete warning filter.

@henryiii henryiii marked this pull request as ready for review June 12, 2026 17:56
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR migrates JSON schema $ref resolution from the deprecated jsonschema.RefResolver to the referencing library's Registry/Resource API. Dependencies are bumped, a read_schema helper is added to the loader, and validator.py is updated to build and anchor a Registry-based resolver.

Changes

Schema resolution migration

Layer / File(s) Summary
Dependency requirement updates
pyproject.toml
Bumps jsonschema to >=4.18.0, adds referencing>=0.28.4 in both project and pixi dependency sections, and removes the now-unneeded jsonschema.RefResolver deprecation warning filter.
Schema disk-read helper
src/pyhf/schema/loader.py
Adds read_schema(schema_id) to read schemas directly from disk without consulting or mutating SCHEMA_CACHE, raising SchemaNotFound if missing; load_schema now calls it on cache misses before populating the cache.
Validator migration to referencing Registry
src/pyhf/schema/validator.py
Adds Registry/Resource/DRAFT6 imports and a _retrieve_schema helper that lazily loads referenced schemas via read_schema; validate now builds a Registry from the root schema and anchors the validator's resolver at the root $id instead of using jsonschema.RefResolver.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Related issues: #2139 (jsonschema v4.18.0 introduces breaking DeprecationWarning changes for RefResolver usage)

Suggested labels: dependencies, schema

Suggested reviewers: kratsg, matthewfeickert

🐰 A resolver retired, deprecated and worn,
A registry rises where refs once were torn,
Schemas now read straight from disk with care,
No more RefResolver warnings in the air,
Hop along, tests green, the migration's done! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing jsonschema.RefResolver with referencing.
Linked Issues check ✅ Passed The PR migrates schema validation off RefResolver to referencing, addressing the warning and compatibility issue in #2139.
Out of Scope Changes check ✅ Passed The dependency bumps and warning-filter removal directly support the RefResolver-to-referencing migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.28%. Comparing base (3ce86e3) to head (63c3f2d).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2716   +/-   ##
=======================================
  Coverage   98.28%   98.28%           
=======================================
  Files          65       65           
  Lines        4305     4315   +10     
  Branches      465      465           
=======================================
+ Hits         4231     4241   +10     
  Misses         46       46           
  Partials       28       28           
Flag Coverage Δ
contrib 98.16% <100.00%> (+<0.01%) ⬆️
doctest 98.28% <100.00%> (+<0.01%) ⬆️
unittests-3.10 96.47% <100.00%> (+<0.01%) ⬆️
unittests-3.11 96.47% <100.00%> (+<0.01%) ⬆️
unittests-3.12 96.47% <100.00%> (+<0.01%) ⬆️
unittests-3.13 96.47% <100.00%> (+<0.01%) ⬆️
unittests-3.9 96.54% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

jsonschema.RefResolver has been deprecated since jsonschema 4.18. Replace
it with a referencing.Registry plus a retrieve callback that loads pyhf
schemas from disk, and pass registry=... to the validator instead of the
deprecated resolver=... argument.

Because the pyhf schemas carry a top-level $ref (e.g. model.json points at
defs.json#/definitions/model), draft-06's sibling-$ref rules suppress the
root $id, so the resolver base URI is set explicitly to the schema $id.
A new loader.read_schema reads schemas without touching SCHEMA_CACHE so
that resolving $refs does not pollute the cache (preserving existing
caching behavior, including the custom schema-path context manager).

Bump jsonschema>=4.18.0 and add referencing as an explicit dependency in
both the project and pixi dependency tables, and drop the now-unneeded
RefResolver DeprecationWarning filter.

Assisted-by: ClaudeCode:claude-opus-4.8
@matthewfeickert matthewfeickert force-pushed the refactor/jsonschema-referencing branch from e55e252 to 63c3f2d Compare July 7, 2026 05:41
@matthewfeickert matthewfeickert added refactor A code change that neither fixes a bug nor adds a feature build Changes that affect the build system or external dependencies labels Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pyhf/schema/validator.py`:
- Around line 118-125: The resolver is being patched onto the Validator after
construction via the private validator._resolver attribute, which should be
avoided. Update the Validator setup in validator.py to pass the anchored
resolver through the supported construction path using the Registry/base URI
mechanism instead of mutating the validator instance afterward. Keep the fix
centered around the Validator(...) initialization and the
registry.resolver(base_uri=schema_id) logic so relative references still resolve
correctly without relying on internals.
- Around line 41-57: In _retrieve_schema, missing referenced schemas are
currently surfaced via read_schema() errors, which causes referencing to treat
them as Unretrievable; update the retrieve callback to catch the missing-schema
case and raise referencing.exceptions.NoSuchResource instead. Keep the existing
uri-to-schema_id resolution and Resource.from_contents flow unchanged, but
ensure the missing-ref path from read_schema/SchemaNotFound maps to
NoSuchResource so the registry handles it correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 20226d4d-2db8-4e0f-942d-a5c49db8f5cb

📥 Commits

Reviewing files that changed from the base of the PR and between 3ce86e3 and 63c3f2d.

📒 Files selected for processing (3)
  • pyproject.toml
  • src/pyhf/schema/loader.py
  • src/pyhf/schema/validator.py

Comment on lines +41 to +57
def _retrieve_schema(uri: str) -> Resource:
"""
A ``referencing`` retrieve callback that loads a pyhf schema by its URI.

Cross-schema ``$ref``\\ s (e.g. ``defs.json``) are resolved against the
referring schema's ``$id``. For the bundled schemas these are absolute URIs
under :data:`pyhf.schema.variables.SCHEMA_BASE`; for custom schemas (see
:class:`pyhf.schema.Schema`) they are paths relative to
:attr:`pyhf.schema.path`. In both cases stripping the base leaves the disk
path relative to :attr:`pyhf.schema.path`.

The schema is read directly from disk via :func:`read_schema` so that
resolving references does not pollute :data:`pyhf.schema.variables.SCHEMA_CACHE`.
"""
schema_id = uri.removeprefix(variables.SCHEMA_BASE)
schema = read_schema(schema_id)
return Resource.from_contents(schema, default_specification=DRAFT6)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'SCHEMA_BASE' src/pyhf/schema/variables.py
rg -n '"\$id"' -g '*.json' src/pyhf/schema | head -n 20

Repository: scikit-hep/pyhf

Length of output: 208


🌐 Web query:

referencing library retrieve callback NoSuchResource exception contract

💡 Result:

The term NoSuchResourceException (or similar variants like NoSuchResource) is not a standardized contract or built-in exception associated with the generic "retrieve callback" pattern in programming. Instead, it appears as a domain-specific exception in distinct, unrelated software libraries and services [1][2][3][4]. In the context of the popular framework LangChain, which heavily utilizes retrieval and callback patterns, there is no generic NoSuchResource exception defined for retriever callbacks [5][6][7]. LangChain's retriever callbacks (such as CallbackManagerForRetrieverRun) utilize methods like on_retriever_end and on_retriever_error to manage lifecycle events [5]. If a retrieval operation fails, it generally catches generic Exception types and reports them via on_retriever_error [6]. Outside of generic patterns, NoSuchResourceException is explicitly found in: 1. AWS SDKs: A specific exception used by the Service Quotas service to indicate that a requested resource does not exist [2][4]. 2. Rust Libraries: Defined in crates like resources and buck_resources to signal that a specific resource could not be found within a container [1][3][8]. 3. Other APIs: Historical or domain-specific APIs, such as the Geneious Public API, use RetrieveCallback interfaces, but these also do not establish a universal contract involving a NoSuchResource exception [9]. If you are encountering this error, it is likely being raised by a specific underlying service (such as an AWS API call) or a custom implementation within your dependency tree, rather than as part of a standard interface contract for callback retrieval [2][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== validator.py ==\n'
cat -n src/pyhf/schema/validator.py | sed -n '1,140p'

printf '\n== variables.py ==\n'
cat -n src/pyhf/schema/variables.py | sed -n '1,120p'

printf '\n== schema loader references ==\n'
rg -n "def read_schema|SchemaNotFound|NoSuchResource|retrieve" src/pyhf/schema src/pyhf -g '!**/__pycache__/**'

printf '\n== dependency pins ==\n'
rg -n "referencing|referencing==" pyproject.toml poetry.lock requirements*.txt setup.cfg setup.py uv.lock pdm.lock package-lock.json 2>/dev/null || true

Repository: scikit-hep/pyhf

Length of output: 8809


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path('src/pyhf/schema/validator.py'), Path('src/pyhf/schema/variables.py')]:
    print(f"\n== {p} ==")
    text = p.read_text().splitlines()
    for i, line in enumerate(text, 1):
        if i <= 220:
            print(f"{i}:{line}")
PY

Repository: scikit-hep/pyhf

Length of output: 5790


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== schema files ==\n'
find src/pyhf/schemas -name '*.json' -maxdepth 3 | sort

printf '\n== $id values ==\n'
python3 - <<'PY'
import json
from pathlib import Path
for p in sorted(Path('src/pyhf/schemas').rglob('*.json')):
    data = json.loads(p.read_text())
    print(f"{p}: {data.get('$id')}")
PY

printf '\n== loader.py ==\n'
cat -n src/pyhf/schema/loader.py | sed -n '1,220p'

printf '\n== referencing package location ==\n'
python3 - <<'PY'
import referencing, inspect
from referencing import Registry
print(referencing.__file__)
print(inspect.getsourcefile(Registry))
PY

Repository: scikit-hep/pyhf

Length of output: 4062


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import json
import traceback

from referencing import Registry
from referencing.jsonschema import DRAFT6

def probe(exc):
    calls = []
    def retrieve(uri):
        calls.append(uri)
        raise exc(f"missing: {uri}")

    reg = Registry(retrieve=retrieve)
    resource = reg.get("https://example.com/schema.json")
    print("Registry.get missing resource ->", resource)

    try:
        reg.contents("https://example.com/schema.json")
    except Exception as e:
        print(type(e).__name__, str(e))
        print("is exc?", isinstance(e, type(exc("x"))))
    else:
        print("contents unexpectedly succeeded")

for exc in [FileNotFoundError]:
    probe(exc)

print("\n-- search schema ids --")
for p in sorted(Path("src/pyhf/schemas").rglob("*.json")):
    data = json.loads(p.read_text())
    if "$id" in data:
        print(p, "=>", data["$id"])
PY

Repository: scikit-hep/pyhf

Length of output: 851


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import inspect
import referencing
from referencing import Registry

print("referencing module:", referencing.__file__)
print("Registry source file:", inspect.getsourcefile(Registry))
PY

python3 - <<'PY'
import inspect
import referencing
from pathlib import Path
pkg = Path(referencing.__file__).resolve().parent
print(pkg)
for path in pkg.rglob("*.py"):
    text = path.read_text(errors="ignore")
    if "NoSuchResource" in text or "retrieve" in text and "Registry" in text:
        print("\nFILE:", path)
        for i, line in enumerate(text.splitlines(), 1):
            if "NoSuchResource" in line or "retrieve" in line or "FileNotFoundError" in line:
                print(f"{i}:{line}")
PY

Repository: scikit-hep/pyhf

Length of output: 8469


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path
from referencing import Registry

print("== missing-resource probe ==")
calls = []
def retrieve(uri):
    calls.append(uri)
    raise FileNotFoundError(f"missing: {uri}")

reg = Registry(retrieve=retrieve)
try:
    # Try to force retrieval through registry APIs if available.
    for method_name, args in [
        ("get", ("https://example.com/schema.json",)),
        ("contents", ("https://example.com/schema.json",)),
        ("resolver", tuple()),
    ]:
        if hasattr(reg, method_name):
            method = getattr(reg, method_name)
            try:
                result = method(*args)
                print(method_name, "=>", type(result).__name__, result)
            except Exception as e:
                print(method_name, "raised", type(e).__name__, str(e))
except Exception as e:
    print("unexpected", type(e).__name__, str(e))

print("retrieve_calls:", calls)

print("\n== schema ids ==")
for p in sorted(Path("src/pyhf/schemas").rglob("*.json")):
    data = json.loads(p.read_text())
    print(f"{p}: {data.get('$id')}")
PY

Repository: scikit-hep/pyhf

Length of output: 945


🌐 Web query:

referencing 0.28.4 retrieve callback FileNotFoundError NoSuchResource documentation

💡 Result:

The terms "FileNotFoundError", "NoSuchResource", "retrieve", and "callback" appear across several distinct software contexts, as no single library documentation explicitly links these four terms within version "0.28.4" [1][2]. 1. LangChain: In the LangChain ecosystem, particularly within langchain_core, retrievers utilize callback managers (e.g., CallbackManagerForRetrieverRun) which define on_retriever_error methods to handle exceptions occurring during the retrieval process [3][4][5]. While these components manage errors during document retrieval, they do not specifically use a "NoSuchResource" exception as a primary, universal identifier [6]. 2. AWS / Boto3 / Botocore: "NoSuchResourceException" is a specific exception type in AWS services (e.g., Service Quotas) [7], and "NoSuchKey" is used in S3 [8]. Developers frequently encounter FileNotFoundError when using boto3's download_file method if the local path is invalid or if the underlying transfer process fails to locate resources [9]. 3. OpenAI Assistants API: Historically, users encountered openai.error.FileNotFoundError when the retrieval tool could not find a file ID [10]. However, this was largely deprecated and replaced in newer versions of the OpenAI library, with file management now handled via tool_resources [10]. 4. Version 0.28.4: Documentation or changelogs for this version number typically refer to specific project updates, such as Plotly Dash 0.28.4 (which addressed callback exception handling) [1] or other smaller libraries [2], rather than a unified standard linking the requested error and retrieval terms. If you are encountering this error, it is likely specific to the library you are currently implementing. It is recommended to check the stack trace to determine which library (e.g., boto3, langchain, or an API client) is raising the exception.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== referencing get_or_retrieve ==\n'
python3 - <<'PY'
from pathlib import Path
import inspect
import referencing._core as core

path = Path(inspect.getsourcefile(core))
lines = path.read_text().splitlines()
for start in [410, 420, 428, 438, 445]:
    a = max(1, start-8)
    b = min(len(lines), start+20)
    print(f"\n-- {path}:{a}-{b} --")
    for i in range(a, b+1):
        print(f"{i}:{lines[i-1]}")
PY

printf '\n== SchemaNotFound usage ==\n'
rg -n "SchemaNotFound" src/pyhf -g '!**/__pycache__/**'

Repository: scikit-hep/pyhf

Length of output: 6938


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "Unretrievable|NoSuchResource|SchemaNotFound|InvalidSpecification" src/pyhf -g '!**/__pycache__/**'

Repository: scikit-hep/pyhf

Length of output: 1344


Raise NoSuchResource from _retrieve_schema. read_schema() raises SchemaNotFound (FileNotFoundError) for a missing ref, and referencing wraps non-NoSuchResource errors as Unretrievable. Raising referencing.exceptions.NoSuchResource here keeps missing-schema refs on the registry’s expected path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pyhf/schema/validator.py` around lines 41 - 57, In _retrieve_schema,
missing referenced schemas are currently surfaced via read_schema() errors,
which causes referencing to treat them as Unretrievable; update the retrieve
callback to catch the missing-schema case and raise
referencing.exceptions.NoSuchResource instead. Keep the existing
uri-to-schema_id resolution and Resource.from_contents flow unchanged, but
ensure the missing-ref path from read_schema/SchemaNotFound maps to
NoSuchResource so the registry handles it correctly.

Comment on lines +118 to +125
validator = Validator(schema, registry=registry, format_checker=None)

# The pyhf schemas carry a top-level ``$ref`` (e.g. ``model.json`` points at
# ``defs.json#/definitions/model``). Under draft-06 a sibling ``$ref``
# suppresses ``$id``, so jsonschema cannot infer the base URI of the root
# schema and relative references like ``defs.json`` fail to resolve. Anchor
# the resolver at the schema ``$id`` so those references resolve correctly.
validator._resolver = registry.resolver(base_uri=schema_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

jsonschema referencing set base uri validator registry resolver supported API 4.18

💡 Result:

In the jsonschema Python library version 4.18, the legacy RefResolver has been deprecated in favor of a newer referencing-based API [1]. The referencing package now handles the registry and resolution of JSON schemas [2][1]. Instead of using RefResolver, you should use the referencing.Registry object [2][3]. Key changes and concepts in the 4.18+ API include: 1. Deprecation of RefResolver: The _RefResolver class, formerly used for resolving $ref properties and managing base URIs, is deprecated [1]. 2. Introducing Registry: You should now create a referencing.Registry object, which acts as an immutable set of JSON schemas that can be either in-memory or retrieved externally [2]. 3. Configuring Validators: When instantiating a validator (e.g., Draft202012Validator), you pass the registry directly via the registry keyword argument rather than using the resolver argument [4][2][3]. 4. Base URI and Identification: Schemas are identified by URIs, often via the $id keyword [2][5]. The Registry allows you to explicitly associate a URI with a specific schema resource using with_resource or similar methods, which effectively handles resolution without needing a manual resolver instance [2][3]. Example Migration Pattern: - Old approach: DraftNValidator(..., resolver=_RefResolver(...)) - New approach: DraftNValidator(..., registry=Registry.with_resources(...)) [2] For more advanced customization of resolution behavior, refer to the referencing library documentation, which defines how references resolve against a schema's base URI [2][3].

Citations:


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
import re

paths = [
    "src/pyhf/schema/validator.py",
    "pyproject.toml",
    "setup.py",
    "requirements.txt",
    "requirements-dev.txt",
    "poetry.lock",
    "uv.lock",
]
for p in paths:
    path = Path(p)
    if path.exists():
        print(f"== {p} ==")
        text = path.read_text()
        if p.endswith("validator.py"):
            for i, line in enumerate(text.splitlines(), 1):
                if 1 <= i <= 220:
                    print(f"{i:4}: {line}")
        else:
            for m in re.finditer(r"jsonschema[^\\n]*", text, flags=re.I):
                print(m.group(0))
PY

Repository: scikit-hep/pyhf

Length of output: 6092


🌐 Web query:

jsonschema 4.18 validator _resolver registry.resolver(base_uri) public API Resource root $id draft-06 sibling $ref docs

💡 Result:

In jsonschema version 4.18.0 and later, the _RefResolver class and its associated mechanisms (such as accessing a validator's.resolver property) are deprecated in favor of the referencing library [1][2][3]. The internal _resolver attribute and registry mechanism are not intended for public API use. Instead, schema referencing and resolution should be managed using the referencing.Registry and related objects [1][4]. Regarding the specific behaviors mentioned: 1. Deprecation: As of v4.18.0, Validator.resolver is deprecated [3][5]. The library has moved to the referencing library, which provides more compliant and flexible referencing behavior [3][4]. 2. Registry and Resolver: When a validator is initialized, it internally manages a registry and resolver [3]. If you need to customize how references are resolved, you should pass a custom referencing.Registry instance to the validator constructor rather than attempting to manipulate private internal resolver attributes [1][4]. 3. Draft-06 Sibling $ref Behavior: In JSON Schema Draft 6, the $ref keyword effectively ignores any sibling keywords within the same subschema object [6][7]. This is a specified behavior of the JSON Schema core specification for older drafts, not a quirk of the jsonschema library's implementation [6][8]. To include additional keywords alongside a $ref, you must wrap them in a construct like allOf [6]. 4. $id and URI Resolution: The $id keyword defines the base URI for relative URI references [6][9]. The jsonschema library resolves relative references against the current base URI, which is determined by the closest parent $id or the initial document context [6][9]. If you rely on old behaviors (such as automatic remote resolution, which was supported by the deprecated _RefResolver), the new referencing.Registry does not perform automatic network retrieval by default for security reasons [1][4]. You must explicitly provide a retrieve function to the registry if you require that functionality [1][4].

Citations:


🌐 Web query:

python-jsonschema 4.18 validator constructor base_uri registry resolver resolver(base_uri) draft6 resource with_resource

💡 Result:

In python-jsonschema version 4.18.0 and later, the _RefResolver class and the resolver argument in validator constructors are deprecated in favor of the referencing library's Registry object [1][2][3]. Registry and with_resource The modern approach for managing JSON Schema references is to use referencing.Registry [1][4]. To add resources to a registry, you use the with_resource method [5][4]. This method associates a URI with a specific schema resource [4]. Example usage: from referencing import Registry from referencing.jsonschema import DRAFT202012 from jsonschema import Draft202012Validator # Create a resource using the appropriate specification resource = DRAFT202012.create_resource({"type": "integer"}) # Use with_resource to add it to a Registry registry = Registry.with_resource(uri="http://example.com/schema", resource=resource) # Pass the registry to the validator validator = Draft202012Validator( {"$ref": "http://example.com/schema"}, registry=registry,) Validator Constructor When instantiating a validator (e.g., Draft202012Validator), you now pass a registry argument instead of a resolver [1][6][2]. If no registry is provided, the validator creates one internally [6][2]. The legacy resolver argument remains for backward compatibility but emits a DeprecationWarning, as the library now delegates referencing behavior to the referencing library [2][3][7]. Key Changes - Deprecation: _RefResolver is deprecated [1][2]. - Replacement: Use referencing.Registry [1][5]. - Configuration: Pass registry=... to the validator constructor instead of resolver=... [1][4]. - Resource Creation: Use the create_resource method from the specific JSON Schema draft (e.g., DRAFT202012.create_resource) to prepare schemas for the registry [1][4].

Citations:


🏁 Script executed:

python3 - <<'PY'
import inspect
import jsonschema
from jsonschema import validators
from referencing import Registry
from referencing.jsonschema import DRAFT6

print("jsonschema", jsonschema.__version__)
print("Draft6Validator signature:", inspect.signature(jsonschema.Draft6Validator))
print("Validator init source excerpt:")
src = inspect.getsource(jsonschema.Draft6Validator.__init__)
print(src)
print("Registry methods:", [m for m in dir(Registry) if m in {"resolver","with_resource","with_contents","with_resources"}])
print("Registry.resolver signature:", inspect.signature(Registry.resolver))
PY

Repository: scikit-hep/pyhf

Length of output: 1195


🏁 Script executed:

python3 - <<'PY'
import inspect
from jsonschema import Draft6Validator
import jsonschema.validators as v

print("Draft6Validator.__init__ source:")
print(inspect.getsource(Draft6Validator.__init__))

print("\nWhere _resolver is used in validators.py:")
src = inspect.getsource(v)
for needle in ["_resolver", "resolver=", "Registry("]:
    print(f"\n-- {needle} --")
    for line in src.splitlines():
        if needle in line:
            print(line)
PY

Repository: scikit-hep/pyhf

Length of output: 1917


Pass the anchored resolver at construction, not via validator._resolver. jsonschema exposes base-URI resolution on Registry, but the validator hook is private; threading the resolver into init avoids a post-init patch and keeps this from depending on internals.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pyhf/schema/validator.py` around lines 118 - 125, The resolver is being
patched onto the Validator after construction via the private
validator._resolver attribute, which should be avoided. Update the Validator
setup in validator.py to pass the anchored resolver through the supported
construction path using the Registry/base URI mechanism instead of mutating the
validator instance afterward. Keep the fix centered around the Validator(...)
initialization and the registry.resolver(base_uri=schema_id) logic so relative
references still resolve correctly without relying on internals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Changes that affect the build system or external dependencies refactor A code change that neither fixes a bug nor adds a feature

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

jsonschema v4.18.0 will introduce breaking changes

3 participants