Skip to content

Commit

Permalink
Gen empty lists if we must
Browse files Browse the repository at this point in the history
  • Loading branch information
Zac-HD committed Jan 6, 2021
1 parent 175213f commit 851d4c7
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

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

#### 0.18.2 - 2020-11-22
Expand Down
2 changes: 1 addition & 1 deletion src/hypothesis_jsonschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The only public API is `from_schema`; check the docstring for details.
"""

__version__ = "0.18.2"
__version__ = "0.19.0"
__all__ = ["from_schema"]

from ._from_schema import from_schema
16 changes: 15 additions & 1 deletion src/hypothesis_jsonschema/_from_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,14 @@ def array_schema(
if contains_additional is not None:
additional_items_strat |= _from_schema_(contains_additional)

# Hypothesis raises InvalidArgument for empty elements and non-None
# max_size, because the user has asked for a possibility which will
# never happen... but we can work around that here.
if additional_items_strat.is_empty:
if min_size >= 1:
return st.nothing()
max_size = 0

if unique:

@st.composite # type: ignore
Expand All @@ -477,6 +485,8 @@ def not_seen(elem: JSONType) -> bool:
for strat in items_strats:
elems.append(draw(strat.filter(not_seen)))
seen.add(encode_canonical_json(elems[-1]))
if max_size == 0:
return elems
extra_items = st.lists(
additional_items_strat.filter(not_seen),
min_size=min_size,
Expand All @@ -487,6 +497,8 @@ def not_seen(elem: JSONType) -> bool:
return elems + more_elems

strat = compose_lists_with_filter()
elif max_size == 0:
strat = st.tuples(*items_strats).map(list)
else:
strat = st.builds(
operator.add,
Expand All @@ -504,7 +516,9 @@ def not_seen(elem: JSONType) -> bool:
# heterogeneous) and hope it works out anyway.
contains_strat = contains_strat.filter(make_validator(items).is_valid)
items_strat |= contains_strat

elif items_strat.is_empty and min_size == 0 and max_size is not None:
# As above, work around a Hypothesis check for unsatisfiable max_size.
return st.builds(list)
strat = st.lists(
items_strat,
min_size=min_size,
Expand Down
21 changes: 21 additions & 0 deletions tests/corpus-reported.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,27 @@
"format": "Unknown formats should be treated as annotations and pass validation",
"type": "string"
},
"unique array with list-items and required additionalItems": {
"additionalItems": {
"const": 2
},
"items": [
{
"const": 1
}
],
"minItems": 2,
"type": "array",
"uniqueItems": true
},
"unique array with list-items without additionalItems": {
"items": [
{}
],
"maxItems": 1,
"type": "array",
"uniqueItems": true
},
"unmergable allOf schema can still be resolved": {
"allOf": [
{
Expand Down
37 changes: 37 additions & 0 deletions tests/test_from_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import re
import warnings
from pathlib import Path

import jsonschema
Expand Down Expand Up @@ -388,6 +389,19 @@ def test_impossible_multiplier(type_):
assert strategy.is_empty


def test_unsatisfiable_array_returns_nothing():
schema = {
"type": "array",
"items": [],
"additionalItems": INVALID_REGEX_SCHEMA,
"minItems": 1,
}
with pytest.warns(UserWarning):
strategy = from_schema(schema)
strategy.validate()
assert strategy.is_empty


ALLOF_CONTAINS = {
"type": "array",
"items": {"type": "string"},
Expand Down Expand Up @@ -444,3 +458,26 @@ def test_allowed_custom_format(num):
def test_allowed_unknown_custom_format(string):
assert string == "hello world"
assert "not registered" not in jsonschema.FormatChecker().checkers


with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)

@given(
from_schema({"type": "array", "items": INVALID_REGEX_SCHEMA, "maxItems": 10})
)
def test_can_generate_empty_list_with_max_size_and_no_allowed_items(val):
assert val == []

@given(
from_schema(
{
"type": "array",
"items": [{"const": 1}, {"const": 2}, {"const": 3}],
"additionalItems": INVALID_REGEX_SCHEMA,
"maxItems": 10,
}
)
)
def test_can_generate_list_with_max_size_and_no_allowed_additional_items(val):
assert val == [1, 2, 3]

0 comments on commit 851d4c7

Please sign in to comment.