Skip to content

Commit

Permalink
Merge pull request #67 from python-openapi/fix/formats-ignore-other-t…
Browse files Browse the repository at this point in the history
…ypes

Formats raise error for other types fix
  • Loading branch information
p1c2u authored Jun 12, 2023
2 parents b60ec26 + 45aa702 commit f87958b
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 8 deletions.
39 changes: 31 additions & 8 deletions openapi_schema_validator/_format.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
import binascii
from base64 import b64decode
from base64 import b64encode
from numbers import Number
from typing import Any
from typing import Union

from jsonschema._format import FormatChecker


def is_int32(instance: Any) -> bool:
return isinstance(instance, int)
# bool inherits from int, so ensure bools aren't reported as ints
if isinstance(instance, bool):
return True
if not isinstance(instance, int):
return True
return ~(1 << 31) < instance < 1 << 31


def is_int64(instance: Any) -> bool:
return isinstance(instance, int)
# bool inherits from int, so ensure bools aren't reported as ints
if isinstance(instance, bool):
return True
if not isinstance(instance, int):
return True
return ~(1 << 63) < instance < 1 << 63


def is_float(instance: Any) -> bool:
# bool inherits from int
if isinstance(instance, int):
return True
if not isinstance(instance, Number):
return True
return isinstance(instance, float)


def is_double(instance: Any) -> bool:
# bool inherits from int
if isinstance(instance, int):
return True
if not isinstance(instance, Number):
return True
# float has double precision in Python
# It's double in CPython and Jython
return isinstance(instance, float)


def is_binary(instance: Any) -> bool:
return isinstance(instance, bytes)
if not isinstance(instance, (str, bytes)):
return True
if isinstance(instance, str):
return False
return True


def is_byte(instance: Union[str, bytes]) -> bool:
if not isinstance(instance, (str, bytes)):
return True
if isinstance(instance, str):
instance = instance.encode()
if not isinstance(instance, bytes):
return False

encoded = b64encode(b64decode(instance))
return encoded == instance


def is_password(instance: Any) -> bool:
if not isinstance(instance, (bytes, str)):
return False

# A hint to UIs to obscure input
return True


Expand Down
70 changes: 70 additions & 0 deletions tests/integration/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,53 @@ def test_required_checkers(self, format_checker):


class BaseTestOASValidatorValidate:
@pytest.mark.parametrize(
"format,value",
[
("int32", "test"),
("int32", True),
("int32", 3.12),
("int32", ["test"]),
("int64", "test"),
("int64", True),
("int64", 3.12),
("int64", ["test"]),
("float", "test"),
("float", 3),
("float", True),
("float", ["test"]),
("double", "test"),
("double", 3),
("double", True),
("double", ["test"]),
("password", 3.12),
("password", True),
("password", 3),
("password", ["test"]),
],
)
def test_formats_ignored(
self, format, value, validator_class, format_checker
):
schema = {"format": format}
validator = validator_class(schema, format_checker=format_checker)

result = validator.validate(value)

assert result is None

@pytest.mark.parametrize("format", ["float", "double"])
@pytest.mark.parametrize("value", [3, 3.14, 1.0])
def test_number_float_and_double_valid(
self, format, value, validator_class, format_checker
):
schema = {"type": "number", "format": format}
validator = validator_class(schema, format_checker=format_checker)

result = validator.validate(value)

assert result is None

@pytest.mark.parametrize("value", ["test"])
def test_string(self, validator_class, value):
schema = {"type": "string"}
Expand Down Expand Up @@ -61,6 +108,29 @@ def validator_class(self):
def format_checker(self):
return oas30_format_checker

@pytest.mark.parametrize(
"format,value",
[
("binary", True),
("binary", 3),
("binary", 3.12),
("binary", ["test"]),
("byte", True),
("byte", 3),
("byte", 3.12),
("byte", ["test"]),
],
)
def test_oas30_formats_ignored(
self, format, value, validator_class, format_checker
):
schema = {"format": format}
validator = validator_class(schema, format_checker=format_checker)

result = validator.validate(value)

assert result is None

@pytest.mark.xfail(reason="OAS 3.0 string type checker allows byte")
@pytest.mark.parametrize("value", [b"test"])
def test_string_disallow_binary(self, validator_class, value):
Expand Down

0 comments on commit f87958b

Please sign in to comment.