Skip to content

Commit 00b3185

Browse files
committed
Enough additional cowboy coding to get draft 2019-09 working too.
1 parent 714dffe commit 00b3185

File tree

4 files changed

+101
-33
lines changed

4 files changed

+101
-33
lines changed

referencing/__init__.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
from referencing._core import Registry # noqa: F401
1+
from referencing._core import ( # noqa: F401
2+
OpaqueSpecification as _Opaque,
3+
Registry,
4+
)
5+
6+
OPAQUE_SPECIFICATION = _Opaque()

referencing/_core.py

+20-32
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
from __future__ import annotations
22

3-
from collections.abc import Mapping, Sequence
4-
from typing import TYPE_CHECKING, Any, Union
3+
from collections.abc import Sequence
4+
from typing import TYPE_CHECKING
55
from urllib.parse import unquote, urldefrag, urljoin
66

77
from pyrsistent import m, plist, s
88
from pyrsistent.typing import PList, PMap, PSet
99

10-
try:
11-
Mapping[str, str]
12-
except TypeError:
13-
from typing import Mapping
14-
1510

1611
class UnsupportedSubclassing(Exception):
1712
@classmethod
@@ -29,6 +24,8 @@ class UnidentifiedResource(Exception):
2924

3025
if TYPE_CHECKING:
3126
from attrs import define, evolve, field, frozen
27+
28+
from referencing.typing import AnchorType, Schema, Specification
3229
else:
3330
from attrs import define as _define, evolve, field, frozen as _frozen
3431

@@ -41,9 +38,6 @@ def frozen(cls):
4138
return _frozen(cls)
4239

4340

44-
Schema = Union[bool, Mapping[str, Any]]
45-
46-
4741
@frozen
4842
class Anchor:
4943

@@ -73,7 +67,15 @@ def resolve(self, dynamic_scope, uri) -> tuple[Schema, str]:
7367
return last, id_of(last) or "" # FIXME: consider when this can be None
7468

7569

76-
AnchorType = Union[Anchor, DynamicAnchor]
70+
class OpaqueSpecification:
71+
"""
72+
A non-specification `Specification` which treats resources opaquely.
73+
74+
In particular, they have no subresources.
75+
"""
76+
77+
def subresources_of(self, resource: Schema):
78+
return ()
7779

7880

7981
@frozen
@@ -83,7 +85,8 @@ class Registry:
8385
default=m(),
8486
repr=lambda value: f"({len(value)} entries)",
8587
)
86-
_uncrawled: PSet[str] = field(default=s())
88+
_uncrawled: PSet[str] = s()
89+
_specification: Specification = OpaqueSpecification()
8790

8891
def update(self, *registries: Registry) -> Registry:
8992
contents = (each._contents for each in registries)
@@ -167,27 +170,17 @@ def _crawl(self) -> Registry:
167170
resource=resource,
168171
),
169172
)
170-
171-
resources.extend( # TODO: delay finding anchors in subresources...
172-
(uri, resource[k]) for k in SUBRESOURCE if k in resource
173-
)
174-
resources.extend(
175-
(uri, subresource)
176-
for k in SUBRESOURCE_VALUES
177-
if k in resource
178-
for subresource in resource[k].values()
179-
)
180173
resources.extend(
181-
(uri, subresource)
182-
for k in SUBRESOURCE_ITEMS
183-
if k in resource
184-
for subresource in resource[k]
174+
(uri, each)
175+
for each in self._specification.subresources_of(resource)
176+
if each is not True and each is not False
185177
)
186178
return evolve(registry, uncrawled=s())
187179

188-
def resolver(self, root) -> Resolver:
180+
def resolver(self, root, specification) -> Resolver:
189181
uri = id_of(root) or ""
190182
registry = self.with_identified_resource(uri=uri, resource=root)
183+
registry = evolve(registry, specification=specification)
191184
return Resolver(base_uri=uri, registry=registry)
192185

193186

@@ -243,11 +236,6 @@ def dynamic_scope(self):
243236
yield resource, self._registry.anchors_at(uri)
244237

245238

246-
SUBRESOURCE = {"items", "not"}
247-
SUBRESOURCE_ITEMS = {"allOf"}
248-
SUBRESOURCE_VALUES = {"$defs", "properties"}
249-
250-
251239
def id_of(resource) -> str | None:
252240
if resource is True or resource is False:
253241
return None

referencing/jsonschema.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Referencing implementations for JSON Schema specs (historic & current).
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
from referencing.typing import ObjectSchema
11+
12+
13+
class Draft202012:
14+
15+
_SUBRESOURCE = {"items", "not"}
16+
_SUBRESOURCE_ITEMS = {"allOf"}
17+
_SUBRESOURCE_VALUES = {"$defs", "properties"}
18+
19+
def subresources_of(self, resource: ObjectSchema):
20+
for each in self._SUBRESOURCE:
21+
if each in resource:
22+
yield resource[each]
23+
for each in self._SUBRESOURCE_ITEMS:
24+
if each in resource:
25+
yield from resource[each]
26+
for each in self._SUBRESOURCE_VALUES:
27+
if each in resource:
28+
yield from resource[each].values()
29+
30+
31+
class Draft201909:
32+
33+
_SUBRESOURCE = {"not"}
34+
_SUBRESOURCE_ITEMS = {"allOf"}
35+
_SUBRESOURCE_VALUES = {"$defs", "properties"}
36+
37+
def subresources_of(self, resource: ObjectSchema):
38+
for each in self._SUBRESOURCE:
39+
if each in resource:
40+
yield resource[each]
41+
for each in self._SUBRESOURCE_ITEMS:
42+
if each in resource:
43+
yield from resource[each]
44+
for each in self._SUBRESOURCE_VALUES:
45+
if each in resource:
46+
yield from resource[each].values()
47+
48+
items = resource.get("items")
49+
if items is None:
50+
return
51+
elif isinstance(items, list):
52+
yield from items
53+
else:
54+
yield items

referencing/typing.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Any, Iterable, Protocol, Union
2+
3+
try:
4+
from collections.abc import Mapping
5+
6+
Mapping[str, str]
7+
except TypeError:
8+
from typing import Mapping
9+
10+
from referencing._core import Anchor, DynamicAnchor
11+
12+
ObjectSchema = Mapping[str, Any]
13+
Schema = Union[bool, ObjectSchema]
14+
AnchorType = Union[Anchor, DynamicAnchor]
15+
16+
17+
class Specification(Protocol):
18+
def subresources_of(self, resource: ObjectSchema) -> Iterable[Schema]:
19+
"""
20+
All (non-recursively nested) resources inside the given resource.
21+
"""

0 commit comments

Comments
 (0)