66import logging
77import os .path
88import warnings
9+ from typing import Optional
10+ import typing
911
1012import inflection
1113import jsonschema
14+ import referencing .jsonschema
15+ import referencing .retrieval
16+ import referencing ._core
1217import six
13- from jsonschema import Draft4Validator
18+ from referencing import Registry , Resource
1419
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
1623from python_jsonschema_objects .validators import ValidationError
1724
1825logger = logging .getLogger (__name__ )
1926
27+ __all__ = ["ObjectBuilder" , "markdown_support" , "ValidationError" ]
28+
2029FILE = __file__
2130
2231SUPPORTED_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" ,
2534)
2635
2736
2837class 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+ ):
3246 if isinstance (schema_uri , six .string_types ):
3347 uri = os .path .normpath (schema_uri )
3448 self .basedir = os .path .dirname (uri )
@@ -41,7 +55,7 @@ def __init__(self, schema_uri, resolved={}, resolver=None, validatorClass=None):
4155
4256 if (
4357 "$schema" in self .schema
44- and self .schema ["$schema" ] not in SUPPORTED_VERSIONS
58+ and self .schema ["$schema" ]. rstrip ( "#" ) not in SUPPORTED_VERSIONS
4559 ):
4660 warnings .warn (
4761 "Schema version {} not recognized. Some "
@@ -50,19 +64,91 @@ def __init__(self, schema_uri, resolved={}, resolver=None, validatorClass=None):
5064 )
5165 )
5266
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" ]}
56137 )
57138
58- validatorClass = validatorClass or Draft4Validator
59- meta_validator = validatorClass (validatorClass .META_SCHEMA )
139+ meta_validator = validatorClass (
140+ validatorClass .META_SCHEMA , registry = self .registry
141+ )
60142 meta_validator .validate (self .schema )
61- self .validator = validatorClass (self .schema , resolver = self .resolver )
143+ self .validator = validatorClass (self .schema , registry = self .registry )
62144
63145 self ._classes = None
64146 self ._resolved = None
65147
148+ @property
149+ def resolver (self ) -> referencing ._core .Resolver :
150+ return self .registry .resolver ()
151+
66152 @property
67153 def schema (self ):
68154 try :
@@ -85,9 +171,6 @@ def get_class(self, uri):
85171 self ._classes = self .build_classes ()
86172 return self ._resolved .get (uri , None )
87173
88- def memory_resolver (self , uri ):
89- return self .mem_resolved [uri [7 :]]
90-
91174 def relative_file_resolver (self , uri ):
92175 path = os .path .join (self .basedir , uri [8 :])
93176 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):
126209 kw = {"strict" : strict }
127210 builder = classbuilder .ClassBuilder (self .resolver )
128211 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
131215 )
132- builder .construct (uri , defn , ** kw )
216+ builder .construct (uri , resolved . contents , ** kw )
133217
134218 if standardize_names :
135219 name_transform = lambda t : inflection .camelize (
@@ -152,7 +236,7 @@ def build_classes(self, strict=False, named_only=False, standardize_names=True):
152236 elif not named_only :
153237 classes [name_transform (uri .split ("/" )[- 1 ])] = klass
154238
155- return util .Namespace .from_mapping (classes )
239+ return python_jsonschema_objects . util .Namespace .from_mapping (classes )
156240
157241
158242if __name__ == "__main__" :
0 commit comments