Skip to content

Commit a75fb16

Browse files
committed
fix: not respecting allow_x00 and codec arguments for values in some schemas
Signed-off-by: Dmitry Dygalo <[email protected]>
1 parent 34e6f53 commit a75fb16

File tree

3 files changed

+59
-11
lines changed

3 files changed

+59
-11
lines changed

CHANGELOG.md

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

3+
- Fix not respecting `allow_x00` and `codec` arguments for values in some schemas
4+
35
#### 0.23.0 - 2023-09-24
46
- Add new `allow_x00=` and `codec=` arguments to `from_schema()`, so that you can
57
control generated strings more precisely.

src/hypothesis_jsonschema/_from_schema.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,16 @@ def from_js_regex(pattern: str, alphabet: CharStrategy) -> st.SearchStrategy[str
7979

8080

8181
def merged_as_strategies(
82-
schemas: List[Schema], custom_formats: Optional[Dict[str, st.SearchStrategy[str]]]
82+
schemas: List[Schema],
83+
*,
84+
alphabet: CharStrategy,
85+
custom_formats: Optional[Dict[str, st.SearchStrategy[str]]],
8386
) -> st.SearchStrategy[JSONType]:
8487
assert schemas, "internal error: must pass at least one schema to merge"
8588
if len(schemas) == 1:
86-
return from_schema(schemas[0], custom_formats=custom_formats)
89+
return __from_schema(
90+
schemas[0], alphabet=alphabet, custom_formats=custom_formats
91+
)
8792
# Try to merge combinations of strategies.
8893
strats = []
8994
combined: Set[str] = set()
@@ -96,7 +101,9 @@ def merged_as_strategies(
96101
s = merged([inputs[g] for g in group])
97102
if s is not None and s != FALSEY:
98103
strats.append(
99-
from_schema(s, custom_formats=custom_formats).filter(
104+
__from_schema(
105+
s, alphabet=alphabet, custom_formats=custom_formats
106+
).filter(
100107
lambda obj, validators=tuple(
101108
make_validator(s).is_valid for s in schemas
102109
): all(v(obj) for v in validators)
@@ -165,7 +172,7 @@ def __from_schema(
165172
schema: Union[bool, Schema],
166173
*,
167174
alphabet: CharStrategy,
168-
custom_formats: Optional[Dict[str, st.SearchStrategy[str]]] = None,
175+
custom_formats: Optional[Dict[str, st.SearchStrategy[str]]],
169176
) -> st.SearchStrategy[JSONType]:
170177
try:
171178
schema = resolve_all_refs(schema)
@@ -217,27 +224,36 @@ def __from_schema(
217224
not_ = schema.pop("not")
218225
assert isinstance(not_, dict)
219226
validator = make_validator(not_).is_valid
220-
return from_schema(schema, custom_formats=custom_formats).filter(
221-
lambda v: not validator(v)
222-
)
227+
return __from_schema(
228+
schema, alphabet=alphabet, custom_formats=custom_formats
229+
).filter(lambda v: not validator(v))
223230
if "anyOf" in schema:
224231
tmp = schema.copy()
225232
ao = tmp.pop("anyOf")
226233
assert isinstance(ao, list)
227-
return st.one_of([merged_as_strategies([tmp, s], custom_formats) for s in ao])
234+
return st.one_of(
235+
[
236+
merged_as_strategies(
237+
[tmp, s], alphabet=alphabet, custom_formats=custom_formats
238+
)
239+
for s in ao
240+
]
241+
)
228242
if "allOf" in schema:
229243
tmp = schema.copy()
230244
ao = tmp.pop("allOf")
231245
assert isinstance(ao, list)
232-
return merged_as_strategies([tmp, *ao], custom_formats)
246+
return merged_as_strategies(
247+
[tmp, *ao], alphabet=alphabet, custom_formats=custom_formats
248+
)
233249
if "oneOf" in schema:
234250
tmp = schema.copy()
235251
oo = tmp.pop("oneOf")
236252
assert isinstance(oo, list)
237253
schemas = [merged([tmp, s]) for s in oo]
238254
return st.one_of(
239255
[
240-
from_schema(s, custom_formats=custom_formats)
256+
__from_schema(s, alphabet=alphabet, custom_formats=custom_formats)
241257
for s in schemas
242258
if s is not None
243259
]
@@ -692,7 +708,13 @@ def from_object_schema(draw: Any) -> Any:
692708
pattern_schemas.insert(0, properties[key])
693709

694710
if pattern_schemas:
695-
out[key] = draw(merged_as_strategies(pattern_schemas, custom_formats))
711+
out[key] = draw(
712+
merged_as_strategies(
713+
pattern_schemas,
714+
alphabet=alphabet,
715+
custom_formats=custom_formats,
716+
)
717+
)
696718
else:
697719
out[key] = draw(
698720
__from_schema(

tests/test_from_schema.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,3 +576,27 @@ def test_errors_on_unencodable_property_name(data):
576576
data.draw(from_schema(non_ascii_schema, codec=None))
577577
with pytest.raises(InvalidArgument, match=r"'é' cannot be encoded as 'ascii'"):
578578
data.draw(from_schema(non_ascii_schema, codec="ascii"))
579+
580+
581+
@settings(deadline=None)
582+
@given(data=st.data())
583+
def test_no_null_bytes(data):
584+
schema = {
585+
"type": "object",
586+
"properties": {
587+
"p1": {"type": "string"},
588+
"p2": {
589+
"type": "object",
590+
"properties": {"pp1": {"type": "string"}},
591+
"required": ["pp1"],
592+
"additionalProperties": False,
593+
},
594+
"p3": {"type": "array", "items": {"type": "string"}},
595+
},
596+
"required": ["p1", "p2", "p3"],
597+
"additionalProperties": False,
598+
}
599+
example = data.draw(from_schema(schema, allow_x00=False))
600+
assert "\x00" not in example["p1"]
601+
assert "\x00" not in example["p2"]["pp1"]
602+
assert all("\x00" not in item for item in example["p3"])

0 commit comments

Comments
 (0)