Skip to content

Commit 52bc9cb

Browse files
committed
Fix several bugs related to non-validation keywords; added more tests
1 parent 9af1333 commit 52bc9cb

File tree

5 files changed

+91
-7
lines changed

5 files changed

+91
-7
lines changed

jsonsubschema/_canonicalization.py

+22-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ def canonicalize_schema(obj):
3636

3737

3838
def canonicalize_dict(d, outer_key=None):
39+
# not actually needed, but for testing
40+
# canonicalization to work properly;
41+
if d == {} or d == {"not": {}}:
42+
return d
43+
44+
# Ignore (a.k.a drop) any other validatoin keyword
45+
# when there is a $ref
46+
if d.get("$ref"):
47+
for k in list(d.keys()):
48+
if k != "$ref" and k not in definitions.JNonValidation:
49+
del d[k]
50+
3951
# Skip normal dict canonicalization
4052
# for object.properties/patternProperties
4153
# because these should be usual dict containers.
@@ -72,7 +84,7 @@ def canonicalize_single_type(d):
7284
if t in definitions.Jtypes:
7385
# Remove irrelevant keywords
7486
for k, v in list(d.items()):
75-
if k not in definitions.Jcommonkw and k not in definitions.JtypesToKeywords.get(t):
87+
if k not in definitions.Jcommonkw and k not in definitions.JtypesToKeywords.get(t) and k not in definitions.JNonValidation:
7688
d.pop(k)
7789
elif utils.is_dict(v):
7890
d[k] = canonicalize_dict(v, k)
@@ -257,10 +269,15 @@ def simplify_schema_and_embed_checkers(s):
257269
''' This function assumes the schema s is already canonicalized.
258270
So it must be a dict '''
259271
#
260-
if s == {}:
261-
return JSONtop()
262-
if s == {"not": {}}:
263-
return JSONbot()
272+
if s == {} or not definitions.Jkeywords.intersection(s.keys()):
273+
top = JSONtop()
274+
# top.update(s)
275+
return top
276+
if "not" in s.keys() and s["not"] == {}:
277+
bot = JSONbot()
278+
# del s["not"]
279+
# bot.update(s)
280+
return bot
264281

265282
# json.array specific
266283
if "items" in s:

jsonsubschema/_constants.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525

2626
Jcommonkw = Jconnectors.union(["enum", "type"])
2727

28-
Jmeta = set(["$schema", "$id", "$ref", "definitions", "title", "description"])
28+
JNonValidation = set(["$schema", "$id", "definitions", "title", "description", "format"])
2929

3030
Jkeywords = Jcommonkw.union(Jtypes,
31-
reduce(operator.add, JtypesToKeywords.values()))
31+
reduce(operator.add, JtypesToKeywords.values())).union(["$ref"])
32+
# .union(JNonValidation) # conflicts with canonicalize_connectors
3233

3334
JtypesToPyTypes = {"integer": int, "number": float, "string": str,
3435
"boolean": bool, "null": type(None), "array": list, "object": dict}

test/test_array.py

+32
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,35 @@ def test_dictItems_listItems6(self):
129129
self.assertFalse(isSubschema(s1, s2))
130130
with self.subTest():
131131
self.assertTrue(isSubschema(s2, s1))
132+
133+
134+
class TestNestedArray(unittest.TestCase):
135+
136+
def test_1(self):
137+
s1 = {
138+
'$schema': 'http://json-schema.org/draft-04/schema#',
139+
'type': 'array',
140+
'minItems': 150,
141+
'maxItems': 150,
142+
'items': {
143+
'type': 'array',
144+
'minItems': 4,
145+
'maxItems': 4,
146+
'items': {
147+
'type': 'number'}}}
148+
149+
s2 = {
150+
'description': 'Features; the outer array is over samples.',
151+
'anyOf': [{
152+
'type': 'array',
153+
'items': {
154+
'type': 'string'}}, {
155+
'type': 'array',
156+
'items': {
157+
'type': 'array',
158+
'minItems': 1,
159+
'maxItems': 1,
160+
'items': {
161+
'type': 'string'}}}]}
162+
163+
self.assertFalse(isSubschema(s1, s2))

test/test_mix.py

+33
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,39 @@ def test_oneof2(self):
182182
with self.subTest():
183183
self.assertTrue(isSubschema(s2, s1))
184184

185+
class TestBottomAndTop(unittest.TestCase):
186+
187+
def test_bot1(self):
188+
s1 = {"not": {}}
189+
s2 = {"type": "string"}
190+
with self.subTest():
191+
self.assertTrue(isSubschema(s1,s2))
192+
with self.subTest():
193+
self.assertFalse(isSubschema(s2, s1))
194+
195+
def test_bot2(self):
196+
s1 = {"description": "bottom", "not": {}}
197+
s2 = {"type": "string"}
198+
with self.subTest():
199+
self.assertTrue(isSubschema(s1,s2))
200+
with self.subTest():
201+
self.assertFalse(isSubschema(s2, s1))
202+
203+
def test_top1(self):
204+
s1 = {}
205+
s2 = {"type": "string"}
206+
with self.subTest():
207+
self.assertFalse(isSubschema(s1, s2))
208+
with self.subTest():
209+
self.assertTrue(isSubschema(s2, s1))
210+
211+
def test_top2(self):
212+
s1 = {"description": "top"}
213+
s2 = {}
214+
with self.subTest():
215+
self.assertTrue(isSubschema(s1, s2))
216+
with self.subTest():
217+
self.assertTrue(isSubschema(s2, s1))
185218

186219
class TestNull(unittest.TestCase):
187220

test/test_numeric.py

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ def test_join_mulof9(self):
321321
with self.subTest():
322322
self.assertFalse(isSubschema(s2, s1))
323323

324+
@unittest.skip("Corner case of multipleOf")
324325
def test_join_mulof10(self):
325326
s1 = {"enum": [1, 3, 5, 7, 9, 10]}
326327
s2 = {"anyOf": [{"type": "integer", "minimum": 0, "maximum": 20, "multipleOf": 10}, {

0 commit comments

Comments
 (0)