11
11
12
12
from jsonschema import _utils , _validators
13
13
from jsonschema .compat import (
14
- Sequence , urljoin , urlsplit , urldefrag , unquote , urlopen ,
14
+ Sequence , urljoin , urlsplit , urldefrag , unquote , urlopen , DefragResult ,
15
+
15
16
str_types , int_types , iteritems ,
16
17
)
17
18
from jsonschema .exceptions import ErrorTree # Backwards compatibility # noqa
@@ -79,7 +80,10 @@ def iter_errors(self, instance, _schema=None):
79
80
if _schema is None :
80
81
_schema = self .schema
81
82
82
- with self .resolver .in_scope (_schema .get (u"id" , u"" )):
83
+ scope = _schema .get (u"id" )
84
+ if scope :
85
+ self .resolver .push_scope (scope )
86
+ try :
83
87
ref = _schema .get (u"$ref" )
84
88
if ref is not None :
85
89
validators = [(u"$ref" , ref )]
@@ -103,6 +107,9 @@ def iter_errors(self, instance, _schema=None):
103
107
if k != u"$ref" :
104
108
error .schema_path .appendleft (k )
105
109
yield error
110
+ finally :
111
+ if scope :
112
+ self .resolver .pop_scope ()
106
113
107
114
def descend (self , instance , schema , path = None , schema_path = None ):
108
115
for error in self .iter_errors (instance , schema ):
@@ -222,7 +229,7 @@ class RefResolver(object):
222
229
223
230
:argument str base_uri: URI of the referring document
224
231
:argument referrer: the actual referring document
225
- :argument dict store: a mapping from URIs to documents to cache
232
+ :argument dict store: a mapping from URIs (without fragments!) to documents to cache
226
233
:argument bool cache_remote: whether remote refs should be cached after
227
234
first resolution
228
235
:argument dict handlers: a mapping from URI schemes to functions that
@@ -233,19 +240,21 @@ class RefResolver(object):
233
240
def __init__ (
234
241
self , base_uri , referrer , store = (), cache_remote = True , handlers = (),
235
242
):
243
+ base_uri = urldefrag (base_uri )
236
244
self .base_uri = base_uri
237
245
self .resolution_scope = base_uri
238
246
# This attribute is not used, it is for backwards compatibility
239
247
self .referrer = referrer
240
248
self .cache_remote = cache_remote
241
249
self .handlers = dict (handlers )
242
250
251
+ self .scopes_stack = []
243
252
self .store = _utils .URIDict (
244
253
(id , validator .META_SCHEMA )
245
254
for id , validator in iteritems (meta_schemas )
246
255
)
247
256
self .store .update (store )
248
- self .store [base_uri ] = referrer
257
+ self .store [base_uri . url ] = referrer
249
258
250
259
@classmethod
251
260
def from_schema (cls , schema , * args , ** kwargs ):
@@ -259,14 +268,19 @@ def from_schema(cls, schema, *args, **kwargs):
259
268
260
269
return cls (schema .get (u"id" , u"" ), schema , * args , ** kwargs )
261
270
262
- @contextlib .contextmanager
263
- def in_scope (self , scope ):
271
+ def push_scope (self , scope , is_defragged = False ):
264
272
old_scope = self .resolution_scope
265
- self .resolution_scope = urljoin (old_scope , scope )
266
- try :
267
- yield
268
- finally :
269
- self .resolution_scope = old_scope
273
+ self .scopes_stack .append (old_scope )
274
+ if not is_defragged :
275
+ scope = urldefrag (scope )
276
+ self .resolution_scope = DefragResult (
277
+ urljoin (old_scope .url , scope .url , allow_fragments = False )
278
+ if scope .url else old_scope .url ,
279
+ scope .fragment
280
+ )
281
+
282
+ def pop_scope (self ):
283
+ self .resolution_scope = self .scopes_stack .pop ()
270
284
271
285
@contextlib .contextmanager
272
286
def resolving (self , ref ):
@@ -278,24 +292,26 @@ def resolving(self, ref):
278
292
279
293
"""
280
294
281
- full_uri = urljoin (self .resolution_scope , ref )
282
- uri , fragment = urldefrag (full_uri )
283
- if not uri :
284
- uri = self .base_uri
295
+ ref = urldefrag (ref )
285
296
286
- if uri in self .store :
287
- document = self .store [uri ]
288
- else :
297
+ url = urljoin (self .resolution_scope .url , ref .url , allow_fragments = False ) \
298
+ if ref .url else self .resolution_scope .url
299
+
300
+ try :
301
+ document = self .store [url ]
302
+ except KeyError :
289
303
try :
290
- document = self .resolve_remote (uri )
304
+ document = self .resolve_remote (url )
291
305
except Exception as exc :
292
306
raise RefResolutionError (exc )
293
307
308
+ uri = DefragResult (url , ref .fragment )
294
309
old_base_uri , self .base_uri = self .base_uri , uri
310
+ self .push_scope (uri , is_defragged = True )
295
311
try :
296
- with self .in_scope (uri ):
297
- yield self .resolve_fragment (document , fragment )
312
+ yield self .resolve_fragment (document , ref .fragment )
298
313
finally :
314
+ self .pop_scope ()
299
315
self .base_uri = old_base_uri
300
316
301
317
def resolve_fragment (self , document , fragment ):
0 commit comments