6
6
import logging
7
7
import os .path
8
8
import warnings
9
+ from typing import Optional
10
+ import typing
9
11
10
12
import inflection
11
13
import jsonschema
14
+ import referencing .jsonschema
15
+ import referencing .retrieval
16
+ import referencing ._core
12
17
import six
13
- from jsonschema import Draft4Validator
18
+ from referencing import Registry , Resource
14
19
15
- from python_jsonschema_objects import classbuilder , markdown_support , util
20
+ import python_jsonschema_objects .classbuilder as classbuilder
21
+ import python_jsonschema_objects .markdown_support
22
+ import python_jsonschema_objects .util
16
23
from python_jsonschema_objects .validators import ValidationError
17
24
18
25
logger = logging .getLogger (__name__ )
19
26
27
+ __all__ = ["ObjectBuilder" , "markdown_support" , "ValidationError" ]
28
+
20
29
FILE = __file__
21
30
22
31
SUPPORTED_VERSIONS = (
23
- "http://json-schema.org/draft-03/schema# " ,
24
- "http://json-schema.org/draft-04/schema# " ,
32
+ "http://json-schema.org/draft-03/schema" ,
33
+ "http://json-schema.org/draft-04/schema" ,
25
34
)
26
35
27
36
28
37
class ObjectBuilder (object ):
29
- def __init__ (self , schema_uri , resolved = {}, resolver = None , validatorClass = None ):
30
- self .mem_resolved = resolved
31
-
38
+ def __init__ (
39
+ self ,
40
+ schema_uri : typing .Union [typing .AnyStr , typing .Mapping ],
41
+ resolved : typing .Dict [typing .AnyStr , typing .Mapping ] = {},
42
+ registry : Optional [referencing .Registry ] = None ,
43
+ resolver : Optional [referencing .typing .Retrieve ] = None ,
44
+ specification_uri : Optional [str ] = None ,
45
+ ):
32
46
if isinstance (schema_uri , six .string_types ):
33
47
uri = os .path .normpath (schema_uri )
34
48
self .basedir = os .path .dirname (uri )
@@ -41,7 +55,7 @@ def __init__(self, schema_uri, resolved={}, resolver=None, validatorClass=None):
41
55
42
56
if (
43
57
"$schema" in self .schema
44
- and self .schema ["$schema" ] not in SUPPORTED_VERSIONS
58
+ and self .schema ["$schema" ]. rstrip ( "#" ) not in SUPPORTED_VERSIONS
45
59
):
46
60
warnings .warn (
47
61
"Schema version {} not recognized. Some "
@@ -50,19 +64,91 @@ def __init__(self, schema_uri, resolved={}, resolver=None, validatorClass=None):
50
64
)
51
65
)
52
66
53
- self .resolver = resolver or jsonschema .RefResolver .from_schema (self .schema )
54
- self .resolver .handlers .update (
55
- {"file" : self .relative_file_resolver , "memory" : self .memory_resolver }
67
+ if registry is not None :
68
+ if not isinstance (registry , referencing .Registry ):
69
+ raise TypeError ("registry must be a Registry instance" )
70
+
71
+ if resolver is not None :
72
+ raise AttributeError (
73
+ "Cannot specify both registry and resolver. If you provide your own registry, pass the resolver "
74
+ "directly to that"
75
+ )
76
+ self .registry = registry
77
+ else :
78
+ if resolver is not None :
79
+
80
+ def file_and_memory_handler (uri ):
81
+ if uri .startswith ("file:" ):
82
+ return Resource .from_contents (self .relative_file_resolver (uri ))
83
+ return resolver (uri )
84
+
85
+ self .registry = Registry (retrieve = file_and_memory_handler )
86
+ else :
87
+
88
+ def file_and_memory_handler (uri ):
89
+ if uri .startswith ("file:" ):
90
+ return Resource .from_contents (self .relative_file_resolver (uri ))
91
+ raise RuntimeError (
92
+ "No remote resource resolver provided. Cannot resolve {}" .format (
93
+ uri
94
+ )
95
+ )
96
+
97
+ self .registry = Registry (retrieve = file_and_memory_handler )
98
+
99
+ if "$schema" not in self .schema :
100
+ warnings .warn (
101
+ "Schema version not specified. Defaulting to {}" .format (
102
+ specification_uri or "http://json-schema.org/draft-04/schema"
103
+ )
104
+ )
105
+ updated = {
106
+ "$schema" : specification_uri or "http://json-schema.org/draft-04/schema"
107
+ }
108
+ updated .update (self .schema )
109
+ self .schema = updated
110
+
111
+ schema = Resource .from_contents (self .schema )
112
+ if schema .id () is None :
113
+ warnings .warn ("Schema id not specified. Defaulting to 'self'" )
114
+ updated = {"$id" : "self" , "id" : "self" }
115
+ updated .update (self .schema )
116
+ self .schema = updated
117
+ schema = Resource .from_contents (self .schema )
118
+
119
+ self .registry = self .registry .with_resource ("" , schema )
120
+
121
+ if len (resolved ) > 0 :
122
+ warnings .warn (
123
+ "Use of 'memory:' URIs is deprecated. Provide a registry with properly resolved references "
124
+ "if you want to resolve items externally." ,
125
+ DeprecationWarning ,
126
+ )
127
+ for uri , contents in resolved .items ():
128
+ self .registry = self .registry .with_resource (
129
+ "memory:" + uri ,
130
+ referencing .Resource .from_contents (
131
+ contents , specification_uri or self .schema ["$schema" ]
132
+ ),
133
+ )
134
+
135
+ validatorClass = jsonschema .validators .validator_for (
136
+ {"$schema" : specification_uri or self .schema ["$schema" ]}
56
137
)
57
138
58
- validatorClass = validatorClass or Draft4Validator
59
- meta_validator = validatorClass (validatorClass .META_SCHEMA )
139
+ meta_validator = validatorClass (
140
+ validatorClass .META_SCHEMA , registry = self .registry
141
+ )
60
142
meta_validator .validate (self .schema )
61
- self .validator = validatorClass (self .schema , resolver = self .resolver )
143
+ self .validator = validatorClass (self .schema , registry = self .registry )
62
144
63
145
self ._classes = None
64
146
self ._resolved = None
65
147
148
+ @property
149
+ def resolver (self ) -> referencing ._core .Resolver :
150
+ return self .registry .resolver ()
151
+
66
152
@property
67
153
def schema (self ):
68
154
try :
@@ -85,9 +171,6 @@ def get_class(self, uri):
85
171
self ._classes = self .build_classes ()
86
172
return self ._resolved .get (uri , None )
87
173
88
- def memory_resolver (self , uri ):
89
- return self .mem_resolved [uri [7 :]]
90
-
91
174
def relative_file_resolver (self , uri ):
92
175
path = os .path .join (self .basedir , uri [8 :])
93
176
with codecs .open (path , "r" , "utf-8" ) as fin :
@@ -126,10 +209,11 @@ def build_classes(self, strict=False, named_only=False, standardize_names=True):
126
209
kw = {"strict" : strict }
127
210
builder = classbuilder .ClassBuilder (self .resolver )
128
211
for nm , defn in six .iteritems (self .schema .get ("definitions" , {})):
129
- uri = util .resolve_ref_uri (
130
- self .resolver .resolution_scope , "#/definitions/" + nm
212
+ resolved = self .resolver .lookup ("#/definitions/" + nm )
213
+ uri = python_jsonschema_objects .util .resolve_ref_uri (
214
+ self .resolver ._base_uri , "#/definitions/" + nm
131
215
)
132
- builder .construct (uri , defn , ** kw )
216
+ builder .construct (uri , resolved . contents , ** kw )
133
217
134
218
if standardize_names :
135
219
name_transform = lambda t : inflection .camelize (
@@ -152,7 +236,7 @@ def build_classes(self, strict=False, named_only=False, standardize_names=True):
152
236
elif not named_only :
153
237
classes [name_transform (uri .split ("/" )[- 1 ])] = klass
154
238
155
- return util .Namespace .from_mapping (classes )
239
+ return python_jsonschema_objects . util .Namespace .from_mapping (classes )
156
240
157
241
158
242
if __name__ == "__main__" :
0 commit comments