6
6
import numbers
7
7
8
8
from six import add_metaclass
9
+ from rfc3986 import uri_reference
9
10
10
11
from jsonschema import _utils , _validators , _types
11
12
from jsonschema .compat import (
12
- Sequence , urljoin , urlsplit , urldefrag , unquote , urlopen ,
13
+ Sequence , unquote , urlopen ,
13
14
str_types , int_types , iteritems , lru_cache ,
14
15
)
15
16
from jsonschema .exceptions import (
@@ -108,10 +109,31 @@ def DEFAULT_TYPES(self):
108
109
return self ._DEFAULT_TYPES
109
110
110
111
112
+ def _as_uri (uri_or_str ):
113
+ """Return URIReference parse result of input string,
114
+ or pass through URIReference argument
115
+ """
116
+ if isinstance (uri_or_str , basestring ):
117
+ return uri_reference (uri_or_str )
118
+ return uri_or_str
119
+
120
+
121
+ def _join_uri (base , ref ):
122
+ """Join absolute base URI with relative URI reference"""
123
+ return _as_uri (ref ).resolve_with (base , strict = True )
124
+
125
+
126
+ def _load_uri_from_schema (schema , key ):
127
+ """Return URIReference object from URI given by key in schema.
128
+ Return URIReference.fromstring('') if key not found
129
+ """
130
+ return uri_reference (schema .get (key , "" ))
131
+
132
+
111
133
def _id_of (schema ):
112
134
if schema is True or schema is False :
113
135
return u""
114
- return schema . get ( u"$id" , u" " )
136
+ return _load_uri_from_schema ( schema , "$id " )
115
137
116
138
117
139
def create (
@@ -256,7 +278,7 @@ def iter_errors(self, instance, _schema=None):
256
278
return
257
279
258
280
scope = id_of (_schema )
259
- if scope :
281
+ if scope . unsplit () :
260
282
self .resolver .push_scope (scope )
261
283
try :
262
284
ref = _schema .get (u"$ref" )
@@ -283,7 +305,7 @@ def iter_errors(self, instance, _schema=None):
283
305
error .schema_path .appendleft (k )
284
306
yield error
285
307
finally :
286
- if scope :
308
+ if scope . unsplit () :
287
309
self .resolver .pop_scope ()
288
310
289
311
def descend (self , instance , schema , path = None , schema_path = None ):
@@ -416,7 +438,7 @@ def extend(validator, validators=(), version=None, type_checker=None):
416
438
},
417
439
type_checker = _types .draft3_type_checker ,
418
440
version = "draft3" ,
419
- id_of = lambda schema : schema . get ( u"id" , " " ),
441
+ id_of = lambda schema : _load_uri_from_schema ( schema , u"id " ),
420
442
)
421
443
422
444
Draft4Validator = create (
@@ -451,7 +473,7 @@ def extend(validator, validators=(), version=None, type_checker=None):
451
473
},
452
474
type_checker = _types .draft4_type_checker ,
453
475
version = "draft4" ,
454
- id_of = lambda schema : schema . get ( u"id" , " " ),
476
+ id_of = lambda schema : _load_uri_from_schema ( schema , u"id " ),
455
477
)
456
478
457
479
@@ -542,6 +564,10 @@ class RefResolver(object):
542
564
543
565
"""
544
566
567
+ DEFAULT_BASE_URI = uri_reference (
568
+ "urn:uuid:00000000-0000-0000-0000-000000000000"
569
+ )
570
+
545
571
def __init__ (
546
572
self ,
547
573
base_uri ,
@@ -553,10 +579,21 @@ def __init__(
553
579
remote_cache = None ,
554
580
):
555
581
if urljoin_cache is None :
556
- urljoin_cache = lru_cache (1024 )(urljoin )
582
+ urljoin_cache = lru_cache (1024 )(_join_uri )
557
583
if remote_cache is None :
558
584
remote_cache = lru_cache (1024 )(self .resolve_from_url )
559
585
586
+ if isinstance (base_uri , basestring ):
587
+ base_uri = uri_reference (base_uri )
588
+
589
+ if not base_uri .unsplit ():
590
+ base_uri = self .DEFAULT_BASE_URI
591
+
592
+ if not base_uri .is_absolute ():
593
+ if base_uri .fragment :
594
+ raise ValueError ("Base URI must not have non-empty fragment" )
595
+ base_uri = base_uri .copy_with (fragment = None )
596
+
560
597
self .referrer = referrer
561
598
self .cache_remote = cache_remote
562
599
self .handlers = dict (handlers )
@@ -566,7 +603,8 @@ def __init__(
566
603
(id , validator .META_SCHEMA )
567
604
for id , validator in iteritems (meta_schemas )
568
605
)
569
- self .store .update (store )
606
+
607
+ self .store .update ({_as_uri (k ): v for k , v in dict (store ).items ()})
570
608
self .store [base_uri ] = referrer
571
609
572
610
self ._urljoin_cache = urljoin_cache
@@ -599,7 +637,7 @@ def from_schema(
599
637
600
638
def push_scope (self , scope ):
601
639
self ._scopes_stack .append (
602
- self ._urljoin_cache (self .resolution_scope , scope ),
640
+ self ._urljoin_cache (self .base_uri , scope )
603
641
)
604
642
605
643
def pop_scope (self ):
@@ -618,8 +656,7 @@ def resolution_scope(self):
618
656
619
657
@property
620
658
def base_uri (self ):
621
- uri , _ = urldefrag (self .resolution_scope )
622
- return uri
659
+ return self .resolution_scope .copy_with (fragment = None )
623
660
624
661
@contextlib .contextmanager
625
662
def in_scope (self , scope ):
@@ -651,11 +688,17 @@ def resolving(self, ref):
651
688
self .pop_scope ()
652
689
653
690
def resolve (self , ref ):
654
- url = self ._urljoin_cache (self .resolution_scope , ref )
691
+ assert self .base_uri
692
+ url = self ._urljoin_cache (self .base_uri , ref )
655
693
return url , self ._remote_cache (url )
656
694
657
695
def resolve_from_url (self , url ):
658
- url , fragment = urldefrag (url )
696
+ if url .fragment :
697
+ fragment = url .fragment
698
+ url = url .copy_with (fragment = None )
699
+ else :
700
+ fragment = ''
701
+
659
702
try :
660
703
document = self .store [url ]
661
704
except KeyError :
@@ -722,7 +765,7 @@ def resolve_remote(self, uri):
722
765
723
766
Arguments:
724
767
725
- uri (str ):
768
+ uri (URIReference ):
726
769
727
770
The URI to resolve
728
771
@@ -738,8 +781,7 @@ def resolve_remote(self, uri):
738
781
except ImportError :
739
782
requests = None
740
783
741
- scheme = urlsplit (uri ).scheme
742
-
784
+ scheme = uri .scheme
743
785
if scheme in self .handlers :
744
786
result = self .handlers [scheme ](uri )
745
787
elif (
@@ -750,12 +792,12 @@ def resolve_remote(self, uri):
750
792
# Requests has support for detecting the correct encoding of
751
793
# json over http
752
794
if callable (requests .Response .json ):
753
- result = requests .get (uri ).json ()
795
+ result = requests .get (uri . unsplit () ).json ()
754
796
else :
755
- result = requests .get (uri ).json
797
+ result = requests .get (uri . unsplit () ).json
756
798
else :
757
799
# Otherwise, pass off to urllib and assume utf-8
758
- result = json .loads (urlopen (uri ).read ().decode ("utf-8" ))
800
+ result = json .loads (urlopen (uri . unsplit () ).read ().decode ("utf-8" ))
759
801
760
802
if self .cache_remote :
761
803
self .store [uri ] = result
@@ -845,4 +887,4 @@ def validator_for(schema, default=_LATEST_VERSION):
845
887
"""
846
888
if schema is True or schema is False :
847
889
return default
848
- return meta_schemas .get (schema . get ( u"$ schema" , u"" ), default )
890
+ return meta_schemas .get (_load_uri_from_schema ( schema , u"$schema " ), default )
0 commit comments