Skip to content

Commit 5a4f899

Browse files
committed
Partially move DynamicAnchor.
For now it's JSON Schema specific and out of scope for the new referencing proposals. The interface though for Registry still needs to change to support different anchor behaviors in drafts.
1 parent 15d7194 commit 5a4f899

File tree

3 files changed

+55
-55
lines changed

3 files changed

+55
-55
lines changed

referencing/_attrs.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import TYPE_CHECKING
2+
3+
if TYPE_CHECKING:
4+
from attrs import define, frozen
5+
else:
6+
from attrs import define as _define, frozen as _frozen
7+
8+
def define(cls):
9+
cls.__init_subclass__ = UnsupportedSubclassing.complain
10+
return _define(cls)
11+
12+
def frozen(cls):
13+
cls.__init_subclass__ = UnsupportedSubclassing.complain
14+
return _frozen(cls)
15+
16+
17+
class UnsupportedSubclassing(Exception):
18+
@classmethod
19+
def complain(this):
20+
raise UnsupportedSubclassing(
21+
"Subclassing is not part of referencing's public API. "
22+
"If no other suitable API exists for what you're trying to do, "
23+
"feel free to file an issue asking for one.",
24+
)

referencing/_core.py

+3-49
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,21 @@
11
from __future__ import annotations
22

33
from collections.abc import Sequence
4-
from typing import TYPE_CHECKING
54
from urllib.parse import unquote, urldefrag, urljoin
65

6+
from attrs import evolve, field
77
from pyrsistent import m, plist, s
88
from pyrsistent.typing import PList, PMap, PSet
99

10+
from referencing._attrs import define, frozen
11+
from referencing.jsonschema import DynamicAnchor, id_of
1012
from referencing.typing import Anchor as AnchorType, Schema, Specification
1113

1214

13-
class UnsupportedSubclassing(Exception):
14-
@classmethod
15-
def complain(this):
16-
raise UnsupportedSubclassing(
17-
"Subclassing is not part of referencing's public API. "
18-
"If no other suitable API exists for what you're trying to do, "
19-
"feel free to file an issue asking for one.",
20-
)
21-
22-
2315
class UnidentifiedResource(Exception):
2416
pass
2517

2618

27-
if TYPE_CHECKING:
28-
from attrs import define, evolve, field, frozen
29-
else:
30-
from attrs import define as _define, evolve, field, frozen as _frozen
31-
32-
def define(cls):
33-
cls.__init_subclass__ = UnsupportedSubclassing.complain
34-
return _define(cls)
35-
36-
def frozen(cls):
37-
cls.__init_subclass__ = UnsupportedSubclassing.complain
38-
return _frozen(cls)
39-
40-
4119
@frozen
4220
class Anchor:
4321

@@ -49,24 +27,6 @@ def resolve(self, dynamic_scope, uri) -> tuple[Schema, str]:
4927
return self.resource, uri
5028

5129

52-
@frozen
53-
class DynamicAnchor:
54-
55-
uri: str
56-
name: str
57-
resource: Schema
58-
59-
def resolve(self, dynamic_scope, uri):
60-
last = self.resource
61-
for resource, anchors in dynamic_scope:
62-
anchor = anchors.get(self.name)
63-
if isinstance(anchor, DynamicAnchor):
64-
last = anchor.resource
65-
elif "$ref" not in resource:
66-
break
67-
return last, id_of(last) or "" # FIXME: consider when this can be None
68-
69-
7030
class OpaqueSpecification:
7131
"""
7232
A non-specification `Specification` which treats resources opaquely.
@@ -234,9 +194,3 @@ def dynamic_scope(self):
234194
for uri in self._previous:
235195
resource, _ = self._registry.resource_at(uri)
236196
yield resource, self._registry.anchors_at(uri)
237-
238-
239-
def id_of(resource) -> str | None:
240-
if resource is True or resource is False:
241-
return None
242-
return resource.get("$id")

referencing/jsonschema.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
from __future__ import annotations
66

7-
from typing import TYPE_CHECKING
8-
9-
if TYPE_CHECKING:
10-
from referencing.typing import ObjectSchema
7+
from referencing._attrs import frozen
8+
from referencing.typing import Schema
119

1210

1311
class Draft202012:
@@ -16,7 +14,7 @@ class Draft202012:
1614
_SUBRESOURCE_ITEMS = {"allOf"}
1715
_SUBRESOURCE_VALUES = {"$defs", "properties"}
1816

19-
def subresources_of(self, resource: ObjectSchema):
17+
def subresources_of(self, resource):
2018
for each in self._SUBRESOURCE:
2119
if each in resource:
2220
yield resource[each]
@@ -28,13 +26,31 @@ def subresources_of(self, resource: ObjectSchema):
2826
yield from resource[each].values()
2927

3028

29+
@frozen
30+
class DynamicAnchor:
31+
32+
uri: str
33+
name: str
34+
resource: Schema
35+
36+
def resolve(self, dynamic_scope, uri):
37+
last = self.resource
38+
for resource, anchors in dynamic_scope:
39+
anchor = anchors.get(self.name)
40+
if isinstance(anchor, DynamicAnchor):
41+
last = anchor.resource
42+
elif "$ref" not in resource:
43+
break
44+
return last, id_of(last) or "" # FIXME: consider when this can be None
45+
46+
3147
class Draft201909:
3248

3349
_SUBRESOURCE = {"not"}
3450
_SUBRESOURCE_ITEMS = {"allOf"}
3551
_SUBRESOURCE_VALUES = {"$defs", "properties"}
3652

37-
def subresources_of(self, resource: ObjectSchema):
53+
def subresources_of(self, resource):
3854
for each in self._SUBRESOURCE:
3955
if each in resource:
4056
yield resource[each]
@@ -52,3 +68,9 @@ def subresources_of(self, resource: ObjectSchema):
5268
yield from items
5369
else:
5470
yield items
71+
72+
73+
def id_of(resource) -> str | None:
74+
if resource is True or resource is False:
75+
return None
76+
return resource.get("$id")

0 commit comments

Comments
 (0)