Skip to content

Commit 27661b2

Browse files
committed
Extend sequance and mapping check
1 parent 4ef44a7 commit 27661b2

File tree

2 files changed

+81
-26
lines changed

2 files changed

+81
-26
lines changed

jsonschema/_utils.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from collections.abc import MutableMapping
22
from urllib.parse import urlsplit
3+
import collections
34
import itertools
45
import json
56
import pkgutil
@@ -188,14 +189,32 @@ def list_equal(one, two):
188189
return True
189190

190191

192+
def is_sequence(instance):
193+
"""
194+
Checks if an instance is a sequence but not a string
195+
"""
196+
return isinstance(
197+
instance, collections.Sequence
198+
) and not isinstance(
199+
instance, str
200+
)
201+
202+
203+
def is_mapping(instance):
204+
"""
205+
Checks if an instance is a mapping
206+
"""
207+
return isinstance(instance, collections.Mapping)
208+
209+
191210
def equal(one, two):
192211
"""
193212
Check if two things are equal, but evade booleans and ints being equal.
194213
"""
195-
if isinstance(one, list) and isinstance(two, list):
214+
if is_sequence(one) and is_sequence(two):
196215
return list_equal(one, two)
197216

198-
if isinstance(one, dict) and isinstance(two, dict):
217+
if is_mapping(one) and is_mapping(two):
199218
return dict_equal(one, two)
200219

201220
return unbool(one) == unbool(two)
@@ -225,18 +244,15 @@ def uniq(container):
225244
sliced = itertools.islice(sort, 1, None)
226245

227246
for i, j in zip(sort, sliced):
228-
return not list_equal(list(i), list(j))
247+
return not list_equal(i, j)
229248

230249
except (NotImplementedError, TypeError):
231250
seen = []
232251
for e in container:
233252
e = unbool(e)
234253

235254
for i in seen:
236-
if isinstance(i, dict) and isinstance(e, dict):
237-
if dict_equal(i, e):
238-
return False
239-
elif i == e:
255+
if equal(i, e):
240256
return False
241257

242258
seen.append(e)

jsonschema/tests/test_validators.py

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,33 +1096,72 @@ def test_check_redefined_sequence(self):
10961096
"type": "array",
10971097
"uniqueItems": True
10981098
}
1099-
11001099
MyMapping = namedtuple('MyMapping', 'a, b')
1101-
11021100
Validator = validators.extend(
11031101
self.Validator,
1104-
type_checker=self.Validator.TYPE_CHECKER.redefine(
1105-
"array",
1106-
lambda checker, thing: isinstance(thing, (list, deque)),
1107-
)
1102+
type_checker=self.Validator.TYPE_CHECKER.redefine_many({
1103+
"array": lambda checker, thing: isinstance(
1104+
thing, (list, deque)
1105+
),
1106+
"object": lambda checker, thing: isinstance(
1107+
thing, (dict, MyMapping)
1108+
),
1109+
})
11081110
)
1109-
11101111
validator = Validator(schema)
1111-
validator.validate(deque(['a', None, '1', '', True]))
1112-
with self.assertRaises(exceptions.ValidationError):
1113-
validator.validate(deque(['a', 'b', 'a']))
11141112

1115-
validator.validate(deque([[False], [0]]))
1116-
with self.assertRaises(exceptions.ValidationError):
1117-
validator.validate(deque([[False], [False]]))
1113+
valid_instances = [
1114+
deque(['a', None, '1', '', True]),
1115+
deque([[False], [0]]),
1116+
[deque([False]), deque([0])],
1117+
[[deque([False])], [deque([0])]],
1118+
[[[[[deque([False])]]]], [[[[deque([0])]]]]],
1119+
[deque([deque([False])]), deque([deque([0])])],
1120+
[MyMapping('a', 0), MyMapping('a', False)],
1121+
[
1122+
MyMapping('a', [deque([0])]),
1123+
MyMapping('a', [deque([False])])
1124+
],
1125+
[
1126+
MyMapping('a', [
1127+
MyMapping('a', deque([0]))
1128+
]),
1129+
MyMapping('a', [
1130+
MyMapping('a', deque([False]))
1131+
])
1132+
],
1133+
[deque(deque(deque([False]))), deque(deque(deque([0])))],
1134+
]
11181135

1119-
validator.validate([deque([False]), deque([0])])
1120-
with self.assertRaises(exceptions.ValidationError):
1121-
validator.validate([deque([False]), deque([False])])
1136+
for instance in valid_instances:
1137+
validator.validate(instance)
1138+
1139+
invalid_instances = [
1140+
deque(['a', 'b', 'a']),
1141+
deque([[False], [False]]),
1142+
[deque([False]), deque([False])],
1143+
[[deque([False])], [deque([False])]],
1144+
[[[[[deque([False])]]]], [[[[deque([False])]]]]],
1145+
[deque([deque([False])]), deque([deque([False])])],
1146+
[MyMapping('a', False), MyMapping('a', False)],
1147+
[
1148+
MyMapping('a', [deque([False])]),
1149+
MyMapping('a', [deque([False])])
1150+
],
1151+
[
1152+
MyMapping('a', [
1153+
MyMapping('a', deque([False]))
1154+
]),
1155+
MyMapping('a', [
1156+
MyMapping('a', deque([False]))
1157+
])
1158+
],
1159+
[deque(deque(deque([False]))), deque(deque(deque([False])))],
1160+
]
11221161

1123-
validator.validate([MyMapping('a', 0), MyMapping('a', False)])
1124-
with self.assertRaises(exceptions.ValidationError):
1125-
validator.validate([MyMapping('a', False), MyMapping('a', False)])
1162+
for instance in invalid_instances:
1163+
with self.assertRaises(exceptions.ValidationError):
1164+
validator.validate(instance)
11261165

11271166

11281167
class AntiDraft6LeakMixin(object):

0 commit comments

Comments
 (0)