13
13
from docutils import nodes
14
14
from docutils .parsers .rst import directives , Directive
15
15
from pathlib import Path
16
+ from urllib import parse as urlparse
17
+ from referencing import Registry , Resource
18
+ from referencing .jsonschema import DRAFT202012
19
+ from jscc .schema import is_json_schema
20
+ from jscc .testing .filesystem import walk_json_data
16
21
17
22
import json
18
23
from collections import OrderedDict
@@ -38,6 +43,26 @@ def custom_jsonref_jsonloader(uri, **kwargs):
38
43
return {}
39
44
40
45
46
+ def build_custom_schema_loader (schema_path ):
47
+ """
48
+ Buildss a callable which handles a URN if provided (e.g. resolves part
49
+ before hash in 'urn:components#/$defs/UnspecifiedRecord' to components.json
50
+ in schema_path dircetory), else calls default JSON loader.
51
+ """
52
+ schemas = []
53
+ for _ , _ , _ , data in walk_json_data (top = schema_path ):
54
+ if is_json_schema (data ):
55
+ schemas .append ((data .get ("$id" ), Resource (contents = data , specification = DRAFT202012 )))
56
+ registry = Registry ().with_resources (schemas )
57
+ def custom_loader (uri , ** kwargs ):
58
+ scheme = urlparse .urlsplit (uri ).scheme
59
+ if scheme == "urn" :
60
+ return registry .contents (uri .split ("#" )[0 ])
61
+ else :
62
+ return jsonref .jsonloader (uri , ** kwargs )
63
+ return custom_loader
64
+
65
+
41
66
class JSONSchemaDirective (Directive ):
42
67
has_content = True
43
68
required_arguments = 1
@@ -49,6 +74,7 @@ class JSONSchemaDirective(Directive):
49
74
'addtargets' : directives .flag ,
50
75
'externallinks' : directives .unchanged ,
51
76
'allowexternalrefs' : directives .flag ,
77
+ 'allowurnrefs' : directives .flag ,
52
78
}
53
79
# Add a rollup option here
54
80
@@ -87,11 +113,18 @@ def run(self):
87
113
self .arguments [0 ])
88
114
env .note_dependency (relpath )
89
115
90
- schema = JSONSchema .loadfromfile (abspath , allow_external_refs = ('allowexternalrefs' in self .options ))
116
+ schema = JSONSchema .loadfromfile (
117
+ abspath ,
118
+ allow_external_refs = ('allowexternalrefs' in self .options ),
119
+ loader = (build_custom_schema_loader (abspath .rsplit ("/" , 1 )[0 ])
120
+ if 'allowurnrefs' in self .options else None )
121
+ )
91
122
else :
92
123
schema = JSONSchema .loadfromfile (
93
124
'' .join (self .content ),
94
- allow_external_refs = ('allowexternalrefs' in self .options )
125
+ allow_external_refs = ('allowexternalrefs' in self .options ),
126
+ loader = (build_custom_schema_loader (abspath .rsplit ("/" , 1 )[0 ])
127
+ if 'allowurnrefs' in self .options else None )
95
128
)
96
129
except ValueError as exc :
97
130
raise self .error ('Failed to parse JSON Schema: %s' % exc )
@@ -242,10 +275,12 @@ def simplify(obj):
242
275
243
276
class JSONSchema (object ):
244
277
@classmethod
245
- def load (cls , reader , allow_external_refs = False , base_uri = "" ):
278
+ def load (cls , reader , allow_external_refs = False , base_uri = "" , loader = None ):
246
279
args = {}
247
280
if not allow_external_refs :
248
281
args ['loader' ] = custom_jsonref_jsonloader
282
+ if loader :
283
+ args ['loader' ] = loader
249
284
obj = jsonref .load (reader , object_pairs_hook = OrderedDict , base_uri = base_uri , ** args )
250
285
return cls .instantiate (None , obj )
251
286
@@ -255,12 +290,14 @@ def loads(cls, string):
255
290
return cls .instantiate (None , obj )
256
291
257
292
@classmethod
258
- def loadfromfile (cls , filename , allow_external_refs = False ):
293
+ def loadfromfile (cls , filename , allow_external_refs = False , loader = None ):
259
294
with io .open (filename , 'rt' , encoding = 'utf-8' ) as reader :
260
295
args = {}
261
296
if allow_external_refs :
262
297
args ['allow_external_refs' ] = True
263
298
args ['base_uri' ] = Path (os .path .realpath (filename )).as_uri ()
299
+ if loader :
300
+ args ['loader' ] = loader
264
301
return cls .load (reader , ** args )
265
302
266
303
@classmethod
0 commit comments