Skip to content

Commit 6d97478

Browse files
committed
Improved canonicalization and added many more tests
1 parent 7c87131 commit 6d97478

File tree

7 files changed

+168
-119
lines changed

7 files changed

+168
-119
lines changed

.coveragerc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[run]
22
branch = True
33
source = jsonsubschema
4-
omit = */jsonsubschema/cli.py,*/jsonsubschema/dircmp.py
4+
omit = */jsonsubschema/cli.py,*/jsonsubschema/exp_*, */jsonsubschema/test_*

jsonsubschema/_canonicalization.py

+44-27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import copy
77
import jsonschema
88
import numbers
9+
import numpy
910
import sys
1011

1112
import jsonsubschema._constants as definitions
@@ -16,14 +17,15 @@
1617
JSONtop,
1718
JSONbot
1819
)
20+
from jsonsubschema.exceptions import UnexpectedCanonicalization
1921

2022
TOP = {}
2123
BOT = {"not": {}}
2224

2325

2426
def canonicalize_schema(obj):
2527
# First, make sure the given json is a valid json schema.
26-
utils.validate_schema(obj)
28+
utils.validate_schema(obj) # should throw jsonschema.SchemaError on unknown types
2729

2830
# Second, canonicalize the schema.
2931
if utils.is_dict(obj):
@@ -41,24 +43,33 @@ def canonicalize_dict(d, outer_key=None):
4143
if d == {} or d == {"not": {}}:
4244
return d
4345

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]
46+
# Ignore (drop) any other validatoin keyword when there is a $ref
47+
# Currently, jsonref handles this case properly,
48+
# We might need to handle it again on out own when
49+
# we handle recursive $ref independently from jsonref.
50+
# if d.get("$ref"):
51+
# for k in list(d.keys()):
52+
# if k != "$ref" and k not in definitions.JNonValidation:
53+
# del d[k]
5054

5155
# Skip normal dict canonicalization
52-
# for object.properties/patternProperties
56+
# for object.properties;
57+
# patternProperties;
58+
# dependencies
5359
# because these should be usual dict containers.
5460
if outer_key in ["properties", "patternProperties"]:
5561
for k, v in d.items():
5662
d[k] = canonicalize_dict(v)
5763
return d
64+
if outer_key == "dependencies":
65+
for k, v in d.items():
66+
if utils.is_dict(v):
67+
d[k] = canonicalize_dict(v)
68+
return d
5869

5970
# here, start dict canonicalization
6071
if not definitions.Jkeywords.intersection(d.keys()):
61-
return TOP
72+
return d
6273

6374
t = d.get("type")
6475
has_connectors = definitions.Jconnectors.intersection(d.keys())
@@ -91,10 +102,10 @@ def canonicalize_single_type(d):
91102
elif utils.is_list(v):
92103
if k == "enum":
93104
v = utils.get_typed_enum_vals(v, t)
94-
if not v:
95-
return BOT
96-
else:
97-
d[k] = v
105+
# if not v:
106+
# return BOT
107+
# else:
108+
d[k] = v
98109
elif k == "required":
99110
d[k] = sorted(set(v))
100111
else:
@@ -104,13 +115,14 @@ def canonicalize_single_type(d):
104115
return rewrite_enum(d)
105116
else:
106117
return d
107-
108-
else:
109-
# TODO: or just return?
110-
print("Unknown schema type {} at:".format(t))
111-
print(d)
112-
print("Exiting...")
113-
sys.exit(1)
118+
119+
# jsonschema validation in the begining prevents
120+
# reaching this case. So we don't need this.
121+
# else:
122+
# print("Unknown schema type {} at:".format(t))
123+
# print(d)
124+
# print("Exiting...")
125+
# sys.exit(1)
114126

115127

116128
def canonicalize_list_of_types(d):
@@ -122,13 +134,18 @@ def canonicalize_list_of_types(d):
122134
s_i["type"] = t_i
123135
s_i = canonicalize_single_type(s_i)
124136
anyofs.append(s_i)
125-
else:
126-
# TODO: or just return?
127-
print("Unknown schema type {} at: {}".format(t_i, t))
128-
print(d)
129-
print("Exiting...")
130-
sys.exit(1)
131-
137+
138+
# jsonschema validation in the begining prevents
139+
# reaching this case. So we don't need this.
140+
# else:
141+
# print("Unknown schema type {} at: {}".format(t_i, t))
142+
# print(d)
143+
# print("Exiting...")
144+
# sys.exit(1)
145+
146+
# if len(anyofs) == 1:
147+
# return anyofs[0]
148+
# elif len(anyofs) > 1:
132149
return {"anyOf": anyofs}
133150

134151

jsonsubschema/_checkers.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,8 @@ def _isObjectSubtype(s1, s2):
12661266
# Check properties range
12671267
is_sub_interval = s1.interval in s2.interval
12681268
if not is_sub_interval:
1269+
print_db(s1.interval, s1)
1270+
print_db(s2.interval, s2)
12691271
print_db("__00__")
12701272
return False
12711273
#
@@ -1318,7 +1320,8 @@ def get_schema_for_key(k, s):
13181320
if lhs_:
13191321
if rhs_:
13201322
if not lhs_.isSubtype(rhs_):
1321-
print_db("__03__")
1323+
print_db(k, "LHS", lhs_, "RHS", rhs_)
1324+
print_db("!!__03__")
13221325
return False
13231326
else:
13241327
print_db("__04__")
@@ -1330,15 +1333,26 @@ def get_schema_for_key(k, s):
13301333
for k_ in s1.patternProperties.keys():
13311334
if utils.regex_matches_string(k_, k):
13321335
extra_keys_on_rhs.remove(k)
1333-
if extra_keys_on_rhs:
1334-
if not s1.additionalProperties:
1335-
print_db("__05__")
1336+
# if extra_keys_on_rhs:
1337+
# if not s1.additionalProperties:
1338+
# print_db("?__05__")
1339+
# return False
1340+
# else:
1341+
for k in extra_keys_on_rhs:
1342+
if is_bot(s1.additionalProperties):
1343+
continue
1344+
elif is_top(s1.additionalProperties):
1345+
print_db("__06__")
13361346
return False
1337-
else:
1338-
for k in extra_keys_on_rhs:
1339-
if not s1.additionalProperties.isSubtype(s2.properties[k]):
1340-
print_db("__06__")
1341-
return False
1347+
# for s in get_schema_for_key(k, s1):
1348+
# if not is_bot(s):
1349+
# continue
1350+
# elif is_bot(s2.):
1351+
# return False
1352+
# print("-->", s)
1353+
# if is_top(s) and not is_top(s2.properties[k]) or not s.isSubtype(s2.properties[k]):
1354+
# print_db("__06__")
1355+
# return False
13421356

13431357
extra_patterns_on_rhs = set(s2.patternProperties.keys()).difference(
13441358
s1.patternProperties.keys())

0 commit comments

Comments
 (0)