4
4
from typing import TYPE_CHECKING , Any , Union
5
5
from urllib .parse import unquote , urldefrag , urljoin
6
6
7
- from pyrsistent import m , s
8
- from pyrsistent .typing import PMap , PSet
7
+ from pyrsistent import m , plist , s
8
+ from pyrsistent .typing import PList , PMap , PSet
9
9
10
10
try :
11
11
Mapping [str , str ]
@@ -51,6 +51,9 @@ class Anchor:
51
51
name : str
52
52
resource : Schema
53
53
54
+ def resolve (self , dynamic_scope , uri ) -> tuple [Schema , str ]:
55
+ return self .resource , uri
56
+
54
57
55
58
@frozen
56
59
class DynamicAnchor :
@@ -59,6 +62,16 @@ class DynamicAnchor:
59
62
name : str
60
63
resource : Schema
61
64
65
+ def resolve (self , dynamic_scope , uri ) -> tuple [Schema , str ]:
66
+ last = self .resource
67
+ for resource , anchors in dynamic_scope :
68
+ anchor = anchors .get (self .name )
69
+ if isinstance (anchor , DynamicAnchor ):
70
+ last = anchor .resource
71
+ elif "$ref" not in resource :
72
+ break
73
+ return last , id_of (last ) or "" # FIXME: consider when this can be None
74
+
62
75
63
76
AnchorType = Union [Anchor , DynamicAnchor ]
64
77
@@ -121,8 +134,8 @@ def resource_at(self, uri: str) -> tuple[Schema, Registry]:
121
134
registry = self ._crawl ()
122
135
return registry ._contents [uri ][0 ], registry
123
136
124
- def anchor_at (self , uri , name ) -> AnchorType :
125
- return self ._contents [uri ][1 ][ name ]
137
+ def anchors_at (self , uri ) -> PMap [ str , AnchorType ] :
138
+ return self ._contents [uri ][1 ]
126
139
127
140
def _crawl (self ) -> Registry :
128
141
registry = self
@@ -183,6 +196,7 @@ class Resolver:
183
196
184
197
_base_uri : str
185
198
_registry : Registry
199
+ _previous : PList [Resolver ] = plist ()
186
200
187
201
def lookup (self , ref : str ) -> tuple [Schema , Resolver ]:
188
202
if ref .startswith ("#" ):
@@ -199,9 +213,13 @@ def lookup(self, ref: str) -> tuple[Schema, Resolver]:
199
213
segment = segment .replace ("~1" , "/" ).replace ("~0" , "~" )
200
214
target = target [segment ] # type: ignore # this can't be a bool
201
215
elif fragment :
202
- target = registry .anchor_at (uri = uri , name = fragment ).resource
216
+ anchor = registry .anchors_at (uri = uri )[fragment ]
217
+ target , uri = anchor .resolve (
218
+ dynamic_scope = self .dynamic_scope (),
219
+ uri = uri ,
220
+ )
203
221
204
- return target , evolve (self , base_uri = uri , registry = registry )
222
+ return target , self . evolve (base_uri = uri , registry = registry )
205
223
206
224
def with_root (self , root ) -> Resolver :
207
225
maybe_relative = id_of (root )
@@ -213,7 +231,16 @@ def with_root(self, root) -> Resolver:
213
231
uri = uri ,
214
232
resource = root ,
215
233
)
216
- return evolve (self , base_uri = uri , registry = registry )
234
+ return self .evolve (base_uri = uri , registry = registry )
235
+
236
+ def evolve (self , ** kwargs ):
237
+ previous = self ._previous .cons (self ._base_uri )
238
+ return evolve (self , previous = previous , ** kwargs )
239
+
240
+ def dynamic_scope (self ):
241
+ for uri in self ._previous :
242
+ resource , _ = self ._registry .resource_at (uri )
243
+ yield resource , self ._registry .anchors_at (uri )
217
244
218
245
219
246
SUBRESOURCE = {"items" , "not" }
0 commit comments