Skip to content

Commit d646e75

Browse files
committed
Merge branch 'release/0.0.22'
2 parents 59407d3 + 95f1f9a commit d646e75

File tree

3 files changed

+100
-9
lines changed

3 files changed

+100
-9
lines changed

python_jsonschema_objects/classbuilder.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,14 @@ def _construct(self, uri, clsdata, parent=(ProtocolBase,)):
464464
return self.resolved[uri]
465465

466466
elif clsdata.get('type') == 'array' and 'items' in clsdata:
467-
self.resolved[uri] = self._build_object(
467+
clsdata_copy = {}
468+
clsdata_copy.update(clsdata)
469+
self.resolved[uri] = validators.ArrayValidator.create(
468470
uri,
469-
clsdata,
470-
parent)
471+
item_constraint=clsdata_copy.pop('items'),
472+
classbuilder=self,
473+
**clsdata_copy)
474+
return self.resolved[uri]
471475

472476
elif (clsdata.get('type', None) == 'object' or
473477
clsdata.get('properties', None) is not None or
@@ -700,12 +704,12 @@ def setprop(self, val):
700704
type_checks.append(typ)
701705
continue
702706
typ = ProtocolBase.__SCHEMA_TYPES__[typ['type']]
703-
if typ == None:
704-
typ = type(None)
707+
if typ is None:
708+
typ = type(None)
705709
if isinstance(typ, (list, tuple)):
706-
type_checks.extend(typ)
710+
type_checks.extend(typ)
707711
else:
708-
type_checks.append(typ)
712+
type_checks.append(typ)
709713

710714
for typ in type_checks:
711715
if isinstance(val, typ):
@@ -734,6 +738,17 @@ def setprop(self, val):
734738
val.validate()
735739
ok = True
736740
break
741+
elif util.safe_issubclass(typ, validators.ArrayValidator):
742+
try:
743+
val = typ(val)
744+
except Exception as e:
745+
errors.append(
746+
"Failed to coerce to '{0}': {1}".format(typ, e))
747+
pass
748+
else:
749+
val.validate()
750+
ok = True
751+
break
737752

738753
if not ok:
739754
errstr = "\n".join(errors)
@@ -754,6 +769,12 @@ def setprop(self, val):
754769
val = info['type'](**util.coerce_for_expansion(val))
755770

756771
val.validate()
772+
elif info['type'] is None:
773+
# This is the null value
774+
if val is not None:
775+
raise validators.ValidationError(
776+
"None is only valid value for null")
777+
757778
else:
758779
raise TypeError("Unknown object type: '{0}'".format(info['type']))
759780

python_jsonschema_objects/validators.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import six
22
from python_jsonschema_objects import util
33
import collections
4+
import logging
5+
logger = logging.getLogger(__name__)
46

57

68
class ValidationError(Exception):
@@ -156,7 +158,13 @@ def check_type(param, value, type_data):
156158
class ArrayValidator(object):
157159

158160
def __init__(self, ary):
159-
self.data = ary
161+
if isinstance(ary, (list, tuple, collections.Sequence)):
162+
self.data = ary
163+
elif isinstance(ary, ArrayValidator):
164+
self.data = ary.data
165+
else:
166+
raise TypeError("Invalid value given to array validator: {0}"
167+
.format(ary))
160168

161169
def validate(self):
162170
converted = self.validate_items()
@@ -261,6 +269,7 @@ def create(name, item_constraint=None, **addl_constraints):
261269
constraints permitted by JSON Schema v4.
262270
"""
263271
from python_jsonschema_objects import classbuilder
272+
klassbuilder = addl_constraints.pop("classbuilder", None)
264273
props = {}
265274

266275
if item_constraint is not None:
@@ -283,7 +292,29 @@ def create(name, item_constraint=None, **addl_constraints):
283292
if not any([isdict, isklass]):
284293
raise TypeError("Item constraint is not a schema")
285294

286-
if isdict and item_constraint['type'] == 'array':
295+
if isdict and '$ref' in item_constraint:
296+
if klassbuilder is None:
297+
raise TypeError("Cannot resolve {0} without classbuilder"
298+
.format(item_constraint['$ref']))
299+
300+
uri = item_constraint['$ref']
301+
if uri in klassbuilder.resolved:
302+
logger.debug(util.lazy_format(
303+
"Using previously resolved object for {0}", uri))
304+
else:
305+
logger.debug(util.lazy_format("Resolving object for {0}", uri))
306+
307+
with klassbuilder.resolver.resolving(uri) as resolved:
308+
# Set incase there is a circular reference in schema definition
309+
klassbuilder.resolved[uri] = None
310+
klassbuilder.resolved[uri] = klassbuilder.construct(
311+
uri,
312+
resolved,
313+
(classbuilder.ProtocolBase,))
314+
315+
item_constraint = klassbuilder.resolved[uri]
316+
317+
elif isdict and item_constraint['type'] == 'array':
287318
item_constraint = ArrayValidator.create(name + "#sub",
288319
item_constraint=item_constraint[
289320
'items'],

test/test_pytest.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import pkg_resources
66
import python_jsonschema_objects as pjs
77

8+
import logging
9+
logging.basicConfig(level=logging.DEBUG)
10+
811

912
def test_regression_9():
1013
schema = {
@@ -20,6 +23,42 @@ def test_regression_9():
2023
builder = pjs.ObjectBuilder(schema)
2124
builder.build_classes()
2225

26+
def test_array_regressions():
27+
schema = {
28+
"$schema": "http://json-schema.org/schema#",
29+
"id": "test",
30+
"type": "object",
31+
"properties": {
32+
"name": {"type": "string"},
33+
"email_aliases": {
34+
"type": "object",
35+
"additionalProperties": {
36+
"type": "array",
37+
"items": {"$ref": "#/definitions/foo"}
38+
}
39+
}
40+
},
41+
"definitions": {
42+
"foo": {
43+
"type": "string"
44+
}
45+
}
46+
}
47+
builder = pjs.ObjectBuilder(schema)
48+
49+
ns = builder.build_classes()
50+
51+
x = ns.Test.from_json('''{"email_aliases": {
52+
"Freddie": ["james", "bond"]
53+
}}''')
54+
x.validate()
55+
56+
y = ns.Test(email_aliases={
57+
"Freddie": ["james", "bond"]
58+
})
59+
y.validate()
60+
61+
2362
def test_arrays_can_have_reffed_items_of_mixed_type():
2463
schema = {
2564
"$schema": "http://json-schema.org/schema#",

0 commit comments

Comments
 (0)