Skip to content

Commit c5d2cf7

Browse files
committed
Allow custom_formats for known format
e.g. to focus on dates in a particular range.
1 parent bc18e21 commit c5d2cf7

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
#### 0.20.0 - 2021-06-02
4+
- Allow `custom_formats` for known string formats (#83)
5+
36
#### 0.19.2 - 2021-05-17
47
- Fix support `date` and `time` formats (#79)
58

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

99
from ._from_schema import from_schema

src/hypothesis_jsonschema/_from_schema.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import jsonschema
1313
from hypothesis import assume, provisional as prov, strategies as st
14-
from hypothesis.errors import InvalidArgument
14+
from hypothesis.errors import HypothesisWarning, InvalidArgument
1515
from hypothesis.internal.conjecture import utils as cu
1616

1717
from ._canonicalise import (
@@ -76,8 +76,11 @@ def from_schema(
7676
) -> st.SearchStrategy[JSONType]:
7777
"""Take a JSON schema and return a strategy for allowed JSON objects.
7878
79-
Schema reuse with "definitions" and "$ref" is not yet supported, but
80-
everything else in drafts 04, 05, and 07 is fully tested and working.
79+
To generate specific string formats, pass a ``custom_formats`` dict
80+
mapping the format name to a strategy for allowed strings.
81+
82+
Supports JSONSchema drafts 04, 06, and 07, with the exception of
83+
recursive references.
8184
"""
8285
try:
8386
return __from_schema(schema, custom_formats=custom_formats)
@@ -127,13 +130,18 @@ def __from_schema(
127130
for name, strat in custom_formats.items():
128131
if not isinstance(name, str):
129132
raise InvalidArgument(f"format name {name!r} must be a string")
130-
if name in STRING_FORMATS:
131-
raise InvalidArgument(f"Cannot redefine standard format {name!r}")
132133
if not isinstance(strat, st.SearchStrategy):
133134
raise InvalidArgument(
134135
f"custom_formats[{name!r}]={strat!r} must be a Hypothesis "
135136
"strategy which generates strings matching this format."
136137
)
138+
if name in STRING_FORMATS:
139+
warnings.warn(
140+
f"Overriding standard format {name!r} - was "
141+
f"{STRING_FORMATS[name]!r}, now {strat!r}",
142+
HypothesisWarning,
143+
stacklevel=2,
144+
)
137145
format_checker = jsonschema.FormatChecker()
138146
custom_formats = {
139147
name: _get_format_filter(name, format_checker, strategy)
@@ -406,7 +414,7 @@ def string_schema(
406414
min_size = schema.get("minLength", 0)
407415
max_size = schema.get("maxLength")
408416
strategy = st.text(min_size=min_size, max_size=max_size)
409-
known_formats = {**(custom_formats or {}), **STRING_FORMATS}
417+
known_formats = {**STRING_FORMATS, **(custom_formats or {})}
410418
if schema.get("format") in known_formats:
411419
# Unknown "format" specifiers should be ignored for validation.
412420
# See https://json-schema.org/latest/json-schema-validation.html#format

tests/test_from_schema.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
settings,
1919
strategies as st,
2020
)
21-
from hypothesis.errors import FailedHealthCheck, InvalidArgument
21+
from hypothesis.errors import FailedHealthCheck, HypothesisWarning, InvalidArgument
2222
from hypothesis.internal.reflection import proxies
2323

2424
from hypothesis_jsonschema._canonicalise import (
@@ -430,7 +430,6 @@ def validate_card_format(string):
430430
[
431431
{"foo": "not a strategy"},
432432
{5: st.just("name is not a string")},
433-
{"full-date": st.just("2000-01-01")}, # can't override a standard format
434433
{"card-test": st.just("not a valid card")},
435434
{"card-test": st.none()}, # Not a string
436435
],
@@ -463,6 +462,18 @@ def test_allowed_unknown_custom_format(string):
463462
assert "not registered" not in jsonschema.FormatChecker().checkers
464463

465464

465+
@given(data=st.data())
466+
def test_overriding_standard_format(data):
467+
expected = "2000-01-01"
468+
schema = {"type": "string", "format": "full-date"}
469+
custom_formats = {"full-date": st.just(expected)}
470+
with pytest.warns(
471+
HypothesisWarning, match="Overriding standard format 'full-date'"
472+
):
473+
value = data.draw(from_schema(schema, custom_formats=custom_formats))
474+
assert value == expected
475+
476+
466477
with warnings.catch_warnings():
467478
warnings.simplefilter("ignore", UserWarning)
468479

0 commit comments

Comments
 (0)