Skip to content

Commit 7fa9377

Browse files
committed
Fix #58 items merging
1 parent 718e70a commit 7fa9377

File tree

4 files changed

+61
-4
lines changed

4 files changed

+61
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
#### 0.17.3 - 2020-07-17
4+
- improved handling of overlapping `items` keywords (#58)
5+
36
#### 0.17.2 - 2020-07-16
47
- improved handling of overlapping `dependencies` keywords (#57)
58

src/hypothesis_jsonschema/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The only public API is `from_schema`; check the docstring for details.
44
"""
55

6-
__version__ = "0.17.2"
6+
__version__ = "0.17.3"
77
__all__ = ["from_schema"]
88

99
from ._from_schema import from_schema

src/hypothesis_jsonschema/_canonicalise.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
between "I'd like it to be faster" and "doesn't finish at all".
1414
"""
1515

16+
import itertools
1617
import json
1718
import math
1819
import re
@@ -298,7 +299,7 @@ def canonicalish(schema: JSONType) -> Dict[str, Any]:
298299
elif isinstance(schema.get(key), (bool, dict)):
299300
schema[key] = canonicalish(schema[key])
300301
else:
301-
assert key not in schema
302+
assert key not in schema, (key, schema[key])
302303
for key in SCHEMA_OBJECT_KEYS:
303304
if key in schema:
304305
schema[key] = {
@@ -831,8 +832,42 @@ def merged(schemas: List[Any]) -> Optional[Schema]:
831832
return None
832833
odeps[k] = m
833834
odeps.update(s.pop("dependencies"))
834-
835-
# TODO: merge `items` schemas or lists-of-schemas
835+
if "items" in out or "items" in s:
836+
oitems = out.pop("items", TRUTHY)
837+
sitems = s.pop("items", TRUTHY)
838+
if isinstance(oitems, list) and isinstance(sitems, list):
839+
out["items"] = []
840+
out["additionalItems"] = merged(
841+
[
842+
out.get("additionalItems", TRUTHY),
843+
s.get("additionalItems", TRUTHY),
844+
]
845+
)
846+
for a, b in itertools.zip_longest(oitems, sitems):
847+
if a is None:
848+
a = out.get("additionalItems", TRUTHY)
849+
elif b is None:
850+
b = s.get("additionalItems", TRUTHY)
851+
out["items"].append(merged([a, b]))
852+
elif isinstance(oitems, list):
853+
out["items"] = [merged([x, sitems]) for x in oitems]
854+
out["additionalItems"] = merged(
855+
[out.get("additionalItems", TRUTHY), sitems]
856+
)
857+
elif isinstance(sitems, list):
858+
out["items"] = [merged([x, oitems]) for x in sitems]
859+
out["additionalItems"] = merged(
860+
[s.get("additionalItems", TRUTHY), oitems]
861+
)
862+
else:
863+
out["items"] = merged([oitems, sitems])
864+
if out["items"] is None:
865+
return None
866+
if isinstance(out["items"], list) and None in out["items"]:
867+
return None
868+
if out.get("additionalItems", TRUTHY) is None:
869+
return None
870+
s.pop("additionalItems", None)
836871

837872
# This loop handles the remaining cases. Notably, we do not attempt to
838873
# merge distinct values for:

tests/test_canonicalise.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,25 @@ def test_canonicalises_to_expected(schema, expected):
401401
],
402402
None,
403403
),
404+
([{"items": {"pattern": "a"}}, {"items": {"pattern": "b"}}], None),
405+
([{"items": [{"pattern": "a"}]}, {"items": [{"pattern": "b"}]}], None,),
406+
(
407+
[
408+
{"items": [{}], "additionalItems": {"pattern": "a"}},
409+
{"items": [{}], "additionalItems": {"pattern": "b"}},
410+
],
411+
None,
412+
),
413+
(
414+
[
415+
{"items": [{}, {"type": "string"}], "additionalItems": False},
416+
{"items": [{"type": "string"}]},
417+
],
418+
{
419+
"items": [{"type": "string"}, {"type": "string"}],
420+
"additionalItems": FALSEY,
421+
},
422+
),
404423
]
405424
+ [
406425
([{lo: 0, hi: 9}, {lo: 1, hi: 10}], {lo: 1, hi: 9})

0 commit comments

Comments
 (0)