From 8d4851ad24b4720143c158e83f2653a97a04069d Mon Sep 17 00:00:00 2001 From: Abdullah Alaqeel Date: Fri, 12 Jun 2026 17:31:05 +0300 Subject: [PATCH 1/2] fix: resolve $dynamicRef for anonymous root schemas When a root schema has no $id, its base URI is "" (empty string). _evolve() uses a truthiness guard that treats "" as falsy, so the anonymous root never appears in dynamic_scope(). This causes $dynamicRef to miss $dynamicAnchor overrides defined in the root schema's $defs, falling back to the template's own $dynamicAnchor. Add an explicit lookup for the anonymous root ("") in the registry after iterating dynamic_scope(), so dynamic anchors on root schemas without $id are found. Fixes #366 --- referencing/jsonschema.py | 7 ++++++ referencing/tests/test_jsonschema.py | 35 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/referencing/jsonschema.py b/referencing/jsonschema.py index 93e77a7..2336212 100644 --- a/referencing/jsonschema.py +++ b/referencing/jsonschema.py @@ -613,6 +613,13 @@ def resolve(self, resolver: _Resolver[Schema]) -> _Resolved[Schema]: continue if isinstance(anchor, DynamicAnchor): last = anchor.resource + try: + anchor = resolver._registry.anchor("", self.name).value + except (exceptions.NoSuchAnchor, exceptions.NoSuchResource): + pass + else: + if isinstance(anchor, DynamicAnchor): + last = anchor.resource return _Resolved( contents=last.contents, resolver=resolver.in_subresource(last), diff --git a/referencing/tests/test_jsonschema.py b/referencing/tests/test_jsonschema.py index c80714d..3cb5a9e 100644 --- a/referencing/tests/test_jsonschema.py +++ b/referencing/tests/test_jsonschema.py @@ -272,6 +272,41 @@ def test_multiple_lookup_dynamic_ref_to_nondynamic_ref(): assert fourth.contents == two.contents +def test_dynamic_ref_with_anonymous_root_schema(): + template = referencing.jsonschema.DRAFT202012.create_resource( + { + "$id": "https://example.com/PaginatedTemplate", + "$defs": { + "itemType": {"$dynamicAnchor": "itemType", "not": {}}, + }, + "type": "object", + "required": ["items"], + "properties": { + "items": { + "type": "array", + "items": {"$dynamicRef": "#itemType"}, + }, + }, + }, + ) + anonymous = referencing.jsonschema.DRAFT202012.create_resource( + { + "$defs": { + "itemType": {"$dynamicAnchor": "itemType", "type": "string"}, + }, + "$ref": "https://example.com/PaginatedTemplate", + }, + ) + registry = Registry().with_resource( + "https://example.com/PaginatedTemplate", + template, + ) + resolver = registry.resolver_with_root(anonymous) + resolved = resolver.lookup("https://example.com/PaginatedTemplate") + item_type = resolved.resolver.lookup("#itemType") + assert item_type.contents == {"$dynamicAnchor": "itemType", "type": "string"} + + def test_lookup_trivial_recursive_ref(): one = referencing.jsonschema.DRAFT201909.create_resource( {"$recursiveAnchor": True}, From 9d37a33a189b62e788819b57bbea794a7a578ae3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jun 2026 14:32:17 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- referencing/tests/test_jsonschema.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/referencing/tests/test_jsonschema.py b/referencing/tests/test_jsonschema.py index 3cb5a9e..67f9949 100644 --- a/referencing/tests/test_jsonschema.py +++ b/referencing/tests/test_jsonschema.py @@ -304,7 +304,10 @@ def test_dynamic_ref_with_anonymous_root_schema(): resolver = registry.resolver_with_root(anonymous) resolved = resolver.lookup("https://example.com/PaginatedTemplate") item_type = resolved.resolver.lookup("#itemType") - assert item_type.contents == {"$dynamicAnchor": "itemType", "type": "string"} + assert item_type.contents == { + "$dynamicAnchor": "itemType", + "type": "string", + } def test_lookup_trivial_recursive_ref():