Skip to content

Commit fee5fae

Browse files
committed
Gen empty lists if we must
1 parent 175213f commit fee5fae

File tree

5 files changed

+68
-2
lines changed

5 files changed

+68
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
22

3+
#### 0.19.0 - 2021-01-06
4+
- Generate empty lists when `maxItems > 0` but no elements are allowed (#75)
35
- Correct handling of regex patterns which are invalid in Python (#75)
46

57
#### 0.18.2 - 2020-11-22

src/hypothesis_jsonschema/__init__.py

+1-1
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.18.2"
6+
__version__ = "0.19.0"
77
__all__ = ["from_schema"]
88

99
from ._from_schema import from_schema

src/hypothesis_jsonschema/_from_schema.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,14 @@ def array_schema(
464464
if contains_additional is not None:
465465
additional_items_strat |= _from_schema_(contains_additional)
466466

467+
# Hypothesis raises InvalidArgument for empty elements and non-None
468+
# max_size, because the user has asked for a possibility which will
469+
# never happen... but we can work around that here.
470+
if additional_items_strat.is_empty:
471+
if min_size >= 1:
472+
return st.nothing()
473+
max_size = 0
474+
467475
if unique:
468476

469477
@st.composite # type: ignore
@@ -477,6 +485,8 @@ def not_seen(elem: JSONType) -> bool:
477485
for strat in items_strats:
478486
elems.append(draw(strat.filter(not_seen)))
479487
seen.add(encode_canonical_json(elems[-1]))
488+
if max_size == 0:
489+
return elems
480490
extra_items = st.lists(
481491
additional_items_strat.filter(not_seen),
482492
min_size=min_size,
@@ -487,6 +497,8 @@ def not_seen(elem: JSONType) -> bool:
487497
return elems + more_elems
488498

489499
strat = compose_lists_with_filter()
500+
elif max_size == 0:
501+
strat = st.tuples(*items_strats).map(list)
490502
else:
491503
strat = st.builds(
492504
operator.add,
@@ -504,7 +516,9 @@ def not_seen(elem: JSONType) -> bool:
504516
# heterogeneous) and hope it works out anyway.
505517
contains_strat = contains_strat.filter(make_validator(items).is_valid)
506518
items_strat |= contains_strat
507-
519+
elif items_strat.is_empty and min_size == 0 and max_size is not None:
520+
# As above, work around a Hypothesis check for unsatisfiable max_size.
521+
return st.builds(list)
508522
strat = st.lists(
509523
items_strat,
510524
min_size=min_size,

tests/corpus-reported.json

+13
Original file line numberDiff line numberDiff line change
@@ -326,5 +326,18 @@
326326
"type": "string"
327327
},
328328
"type": "array"
329+
},
330+
"unique array with list-items and required additionalItems": {
331+
"type": "array",
332+
"items": [
333+
{
334+
"const": 1
335+
}
336+
],
337+
"additionalItems": {
338+
"const": 2
339+
},
340+
"minItems": 2,
341+
"uniqueItems": true
329342
}
330343
}

tests/test_from_schema.py

+37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import json
44
import re
5+
import warnings
56
from pathlib import Path
67

78
import jsonschema
@@ -388,6 +389,19 @@ def test_impossible_multiplier(type_):
388389
assert strategy.is_empty
389390

390391

392+
def test_unsatisfiable_array_returns_nothing():
393+
schema = {
394+
"type": "array",
395+
"items": [],
396+
"additionalItems": INVALID_REGEX_SCHEMA,
397+
"minItems": 1,
398+
}
399+
with pytest.warns(UserWarning):
400+
strategy = from_schema(schema)
401+
strategy.validate()
402+
assert strategy.is_empty
403+
404+
391405
ALLOF_CONTAINS = {
392406
"type": "array",
393407
"items": {"type": "string"},
@@ -444,3 +458,26 @@ def test_allowed_custom_format(num):
444458
def test_allowed_unknown_custom_format(string):
445459
assert string == "hello world"
446460
assert "not registered" not in jsonschema.FormatChecker().checkers
461+
462+
463+
with warnings.catch_warnings():
464+
warnings.simplefilter("ignore", UserWarning)
465+
466+
@given(
467+
from_schema({"type": "array", "items": INVALID_REGEX_SCHEMA, "maxItems": 10})
468+
)
469+
def test_can_generate_empty_list_with_max_size_and_no_allowed_items(val):
470+
assert val == []
471+
472+
@given(
473+
from_schema(
474+
{
475+
"type": "array",
476+
"items": [{"const": 1}, {"const": 2}, {"const": 3}],
477+
"additionalItems": INVALID_REGEX_SCHEMA,
478+
"maxItems": 10,
479+
}
480+
)
481+
)
482+
def test_can_generate_list_with_max_size_and_no_allowed_additional_items(val):
483+
assert val == [1, 2, 3]

0 commit comments

Comments
 (0)