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,27 @@ 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
+
58
+ def custom_loader (uri , ** kwargs ):
59
+ scheme = urlparse .urlsplit (uri ).scheme
60
+ if scheme == "urn" :
61
+ return registry .contents (uri .split ("#" )[0 ])
62
+ else :
63
+ return jsonref .jsonloader (uri , ** kwargs )
64
+ return custom_loader
65
+
66
+
41
67
class JSONSchemaDirective (Directive ):
42
68
has_content = True
43
69
required_arguments = 1
@@ -49,6 +75,7 @@ class JSONSchemaDirective(Directive):
49
75
'addtargets' : directives .flag ,
50
76
'externallinks' : directives .unchanged ,
51
77
'allowexternalrefs' : directives .flag ,
78
+ 'allowurnrefs' : directives .flag ,
52
79
}
53
80
# Add a rollup option here
54
81
@@ -87,11 +114,18 @@ def run(self):
87
114
self .arguments [0 ])
88
115
env .note_dependency (relpath )
89
116
90
- schema = JSONSchema .loadfromfile (abspath , allow_external_refs = ('allowexternalrefs' in self .options ))
117
+ schema = JSONSchema .loadfromfile (
118
+ abspath ,
119
+ allow_external_refs = ('allowexternalrefs' in self .options ),
120
+ loader = (build_custom_schema_loader (abspath .rsplit ("/" , 1 )[0 ])
121
+ if 'allowurnrefs' in self .options else None )
122
+ )
91
123
else :
92
124
schema = JSONSchema .loadfromfile (
93
125
'' .join (self .content ),
94
- allow_external_refs = ('allowexternalrefs' in self .options )
126
+ allow_external_refs = ('allowexternalrefs' in self .options ),
127
+ loader = (build_custom_schema_loader (abspath .rsplit ("/" , 1 )[0 ])
128
+ if 'allowurnrefs' in self .options else None )
95
129
)
96
130
except ValueError as exc :
97
131
raise self .error ('Failed to parse JSON Schema: %s' % exc )
@@ -242,10 +276,12 @@ def simplify(obj):
242
276
243
277
class JSONSchema (object ):
244
278
@classmethod
245
- def load (cls , reader , allow_external_refs = False , base_uri = "" ):
279
+ def load (cls , reader , allow_external_refs = False , base_uri = "" , loader = None ):
246
280
args = {}
247
281
if not allow_external_refs :
248
282
args ['loader' ] = custom_jsonref_jsonloader
283
+ if loader :
284
+ args ['loader' ] = loader
249
285
obj = jsonref .load (reader , object_pairs_hook = OrderedDict , base_uri = base_uri , ** args )
250
286
return cls .instantiate (None , obj )
251
287
@@ -255,12 +291,14 @@ def loads(cls, string):
255
291
return cls .instantiate (None , obj )
256
292
257
293
@classmethod
258
- def loadfromfile (cls , filename , allow_external_refs = False ):
294
+ def loadfromfile (cls , filename , allow_external_refs = False , loader = None ):
259
295
with io .open (filename , 'rt' , encoding = 'utf-8' ) as reader :
260
296
args = {}
261
297
if allow_external_refs :
262
298
args ['allow_external_refs' ] = True
263
299
args ['base_uri' ] = Path (os .path .realpath (filename )).as_uri ()
300
+ if loader :
301
+ args ['loader' ] = loader
264
302
return cls .load (reader , ** args )
265
303
266
304
@classmethod
0 commit comments