44from typing import TYPE_CHECKING , Any , Union
55from urllib .parse import unquote , urldefrag , urljoin
66
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
99
1010try :
1111 Mapping [str , str ]
@@ -51,6 +51,9 @@ class Anchor:
5151 name : str
5252 resource : Schema
5353
54+ def resolve (self , dynamic_scope , uri ) -> tuple [Schema , str ]:
55+ return self .resource , uri
56+
5457
5558@frozen
5659class DynamicAnchor :
@@ -59,6 +62,16 @@ class DynamicAnchor:
5962 name : str
6063 resource : Schema
6164
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+
6275
6376AnchorType = Union [Anchor , DynamicAnchor ]
6477
@@ -121,8 +134,8 @@ def resource_at(self, uri: str) -> tuple[Schema, Registry]:
121134 registry = self ._crawl ()
122135 return registry ._contents [uri ][0 ], registry
123136
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 ]
126139
127140 def _crawl (self ) -> Registry :
128141 registry = self
@@ -183,6 +196,7 @@ class Resolver:
183196
184197 _base_uri : str
185198 _registry : Registry
199+ _previous : PList [Resolver ] = plist ()
186200
187201 def lookup (self , ref : str ) -> tuple [Schema , Resolver ]:
188202 if ref .startswith ("#" ):
@@ -199,9 +213,13 @@ def lookup(self, ref: str) -> tuple[Schema, Resolver]:
199213 segment = segment .replace ("~1" , "/" ).replace ("~0" , "~" )
200214 target = target [segment ] # type: ignore # this can't be a bool
201215 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+ )
203221
204- return target , evolve (self , base_uri = uri , registry = registry )
222+ return target , self . evolve (base_uri = uri , registry = registry )
205223
206224 def with_root (self , root ) -> Resolver :
207225 maybe_relative = id_of (root )
@@ -213,7 +231,16 @@ def with_root(self, root) -> Resolver:
213231 uri = uri ,
214232 resource = root ,
215233 )
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 )
217244
218245
219246SUBRESOURCE = {"items" , "not" }
0 commit comments