Skip to content

Commit

Permalink
Test types
Browse files Browse the repository at this point in the history
  • Loading branch information
nineteendo committed Feb 3, 2025
1 parent 0a0ea66 commit a87b95b
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 30 deletions.
13 changes: 1 addition & 12 deletions src/jsonyx/_speedups.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,18 +437,7 @@ scanstring_unicode(PyScannerObject *s, PyObject *pyfilename, PyObject *pystr, Py
c = d;
}

if (c == '"') {
// Fast path for simple case.
if (writer.buffer == NULL) {
PyObject *ret = PyUnicode_Substring(pystr, end, next);
if (ret == NULL) {
goto bail;
}
*next_end_ptr = next + 1;;
return ret;
}
}
else if (c != '\\') {
if (c != '"' && c != '\\') {
raise_errmsg("Unterminated string", pyfilename, pystr, begin, next);
goto bail;
}
Expand Down
80 changes: 62 additions & 18 deletions src/jsonyx/test/test_loads.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Copyright (C) 2024 Nice Zombies
"""JSON loads tests."""
# TODO(Nice Zombies): test types
from __future__ import annotations

__all__: list[str] = []

from collections import UserString
from dataclasses import dataclass
from decimal import Decimal
from math import isnan
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

import pytest

Expand All @@ -24,6 +25,21 @@
from types import ModuleType


# pylint: disable-next=R0903
@dataclass
class _MyNumber:
value: Any

def __bool__(self) -> bool:
return bool(self.value)

def __float__(self) -> float:
return float(self.value)

def __int__(self) -> int:
return int(self.value)


@pytest.mark.parametrize(
"string", ["\ud800", "\ud800$", "\udf48"], # noqa: PT014
)
Expand Down Expand Up @@ -51,44 +67,51 @@ def test_utf8_bom(json: ModuleType) -> None:
check_syntax_err(exc_info, "Unexpected UTF-8 BOM", 1, 2)


def test_null(json: ModuleType) -> None:
"""Test null."""
assert json.loads("null") is None


@pytest.mark.parametrize(("s", "expected"), [
("true", True),
("false", False),
("null", None),
])
def test_literal_names(
json: ModuleType, s: str, expected: bool | None, # noqa: FBT001
@pytest.mark.parametrize("bool_type", [_MyNumber, bool])
def test_bool(
json: ModuleType, s: str, expected: bool, bool_type: type, # noqa: FBT001
) -> None:
"""Test literal names."""
assert json.loads(s) is expected
"""Test bool."""
obj: object = json.loads(s, types={"bool": bool_type})
assert isinstance(obj, bool_type)
assert obj == bool_type(expected)


@pytest.mark.parametrize("s", ["NaN", "Infinity", "-Infinity"])
@pytest.mark.parametrize("use_decimal", [True, False])
def test_nan_and_infinity(
json: ModuleType, s: str, use_decimal: bool, # noqa: FBT001
) -> None:
@pytest.mark.parametrize("float_type", [_MyNumber, Decimal, float])
def test_nan_and_infinity(json: ModuleType, s: str, float_type: type) -> None:
"""Test NaN and infinity."""
obj: object = json.loads(
s, allow=NAN_AND_INFINITY, use_decimal=use_decimal,
s, allow=NAN_AND_INFINITY, types={"float": float_type},
use_decimal=float_type is Decimal,
)
expected_type: type[Decimal | float] = Decimal if use_decimal else float
expected: Decimal | float = expected_type(s)
assert isinstance(obj, expected_type)
expected: Any = Decimal(s) if float_type is Decimal else float(s)
expected = float_type(expected)
assert isinstance(obj, float_type)
if isnan(expected):
assert isnan(obj) # type: ignore[arg-type]
else:
assert obj == expected


@pytest.mark.parametrize("s", ["NaN", "Infinity", "-Infinity"])
@pytest.mark.parametrize("use_decimal", [True, False])
@pytest.mark.parametrize("float_type", [_MyNumber, Decimal, float])
def test_nan_and_infinity_not_allowed(
json: ModuleType, s: str, use_decimal: bool, # noqa: FBT001
json: ModuleType, s: str, float_type: type,
) -> None:
"""Test NaN and infinity when not allowed."""
types: dict[str, type] = {"float": float_type}
with pytest.raises(json.JSONSyntaxError) as exc_info:
json.loads(s, use_decimal=use_decimal)
json.loads(s, types=types, use_decimal=float_type is Decimal)

check_syntax_err(exc_info, f"{s} is not allowed", 1, len(s) + 1)

Expand All @@ -115,6 +138,13 @@ def test_too_big_int(json: ModuleType, big_num: str) -> None:
check_syntax_err(exc_info, "Number is too big", 1, len(big_num) + 1)


def test_int_type(json: ModuleType) -> None:
"""Test int_type."""
obj: object = json.loads("0", types={"int": _MyNumber})
assert isinstance(obj, _MyNumber)
assert obj == _MyNumber(0)


@pytest.mark.parametrize("s", [
# Sign
"-1.0",
Expand Down Expand Up @@ -184,6 +214,13 @@ def test_invalid_number(json: ModuleType, s: str) -> None:
json.loads(s)


def test_float_type(json: ModuleType) -> None:
"""Test int_type."""
obj: object = json.loads("0.0", types={"float": _MyNumber})
assert isinstance(obj, _MyNumber)
assert obj == _MyNumber(0.0)


@pytest.mark.parametrize(("s", "expected"), [
# Empty string
("", ""),
Expand Down Expand Up @@ -300,6 +337,13 @@ def test_invalid_unicode_escape(
check_syntax_err(exc_info, "Expecting 4 hex digits", colno, end_colno)


def test_str_type(json: ModuleType) -> None:
"""Test str_type."""
obj: object = json.loads('""', types={"str": UserString})
assert isinstance(obj, UserString)
assert not obj


@pytest.mark.parametrize(("s", "expected"), [
# Empty array
("[]", []),
Expand Down

0 comments on commit a87b95b

Please sign in to comment.