Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"format" supports all types by default #102

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

v0.12.0 (in development)
------------------------
Features:

* "format" applies to all types


v0.11.1 (in development)
------------------------
Features:
Expand Down
10 changes: 4 additions & 6 deletions examples/format_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@


# register an 'ipv4' format validator
@format_validator('ipv4')
@format_validator('ipv4', instance_types=('string',))
def validate_ipv4(value: str) -> None:
if isinstance(value, str):
ipaddress.IPv4Address(value) # raises ValueError for an invalid IPv4 address
ipaddress.IPv4Address(value) # raises ValueError for an invalid IPv4 address


# register an 'ipv6' format validator
@format_validator('ipv6')
@format_validator('ipv6', instance_types=('string',))
def validate_ipv6(value: str) -> None:
if isinstance(value, str):
ipaddress.IPv6Address(value) # raises ValueError for an invalid IPv6 address
ipaddress.IPv6Address(value) # raises ValueError for an invalid IPv6 address


# initialize the catalog, with JSON Schema 2020-12 vocabulary support
Expand Down
7 changes: 3 additions & 4 deletions jschon/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from jschon.vocabulary.format import format_validator


@format_validator('json-pointer')
@format_validator('json-pointer', instance_types=('string',))
def validate_json_pointer(value: str) -> None:
if isinstance(value, str):
if not JSONPointer._json_pointer_re.fullmatch(value):
raise ValueError
if not JSONPointer._json_pointer_re.fullmatch(value):
raise ValueError
6 changes: 4 additions & 2 deletions jschon/vocabulary/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, parentschema: JSONSchema, value: str):
if parentschema.catalog.is_format_enabled(value):
self.validator, self.validates_types = _format_validators[value]
else:
self.validator = None
self.validator, self.validates_types = None, set()

def evaluate(self, instance: JSON, result: Result) -> None:
result.annotate(self.json.value)
Expand Down Expand Up @@ -47,7 +47,9 @@ def evaluate(self, instance: JSON, result: Result) -> None:
def format_validator(
format_attr: str,
*,
instance_types: Tuple[str, ...] = ('string',)
instance_types: Tuple[str, ...] = (
"null", "boolean", "number", "string", "array", "object",
)
):
"""A decorator for a format validation function.

Expand Down
35 changes: 29 additions & 6 deletions tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,28 @@ def setup_validators(catalog):
"ipv4",
"ipv6",
"json-pointer",
"uint8",
)
yield
catalog._enabled_formats.clear()


@format_validator('ipv4')
@format_validator('ipv4', instance_types=('string',))
def ipv4_validator(value):
if isinstance(value, str):
ipaddress.IPv4Address(value)
ipaddress.IPv4Address(value)


@format_validator('ipv6')
@format_validator('ipv6', instance_types=('string',))
def ipv6_validator(value):
if isinstance(value, str):
ipaddress.IPv6Address(value)
ipaddress.IPv6Address(value)


@format_validator('uint8', instance_types=('number',))
def uint8_validator(value):
if value % 1:
raise ValueError(f'{value} is not an integer (uint8)')
if value < 0 or value > 255:
raise ValueError(f'{value} is out of range for uint8')


def evaluate(format_attr, instval, assert_=True):
Expand Down Expand Up @@ -88,6 +95,22 @@ def test_jsonpointer_invalid(instval):
assert result is False


@pytest.mark.parametrize('instval', (3, "1", "what"))
def test_uint8_valid(instval):
result = evaluate(
"uint8",
instval,
isinstance(instval, (int, float))
)
assert result is True


@pytest.mark.parametrize('instval', (3.001, -1, 256))
def test_uint8_invalid(instval):
result = evaluate("uint8", instval)
assert result is False


@given(instval=hs.uuids() | hs.text())
def test_uuid(instval):
# we've not registered a "uuid" validator, so the test should always pass
Expand Down