diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e813b80..79cb494 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.x" + python-version-file: pyproject.toml cache: "pip" cache-dependency-path: pyproject.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e28c5f0..f484a39 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version-file: pyproject.toml cache: "pip" cache-dependency-path: pyproject.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4409172..ab23c37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: ">= 3.8" + python-version-file: pyproject.toml cache: "pip" cache-dependency-path: pyproject.toml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d267175..87b9046 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,10 +11,9 @@ jobs: strategy: matrix: python: - - "3.8" - - "3.9" - "3.10" - "3.11" + - "3.12" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/src/rfc8785/__init__.py b/src/rfc8785/__init__.py index bf1e46b..3783a39 100644 --- a/src/rfc8785/__init__.py +++ b/src/rfc8785/__init__.py @@ -4,11 +4,12 @@ __version__ = "0.0.1" -from ._impl import CanonicalizationError, IntegerDomainError, dump, dumps +from ._impl import CanonicalizationError, FloatDomainError, IntegerDomainError, dump, dumps __all__ = [ "CanonicalizationError", "IntegerDomainError", + "FloatDomainError", "dump", "dumps", ] diff --git a/src/rfc8785/_impl.py b/src/rfc8785/_impl.py index 32cbd4c..0a99791 100644 --- a/src/rfc8785/_impl.py +++ b/src/rfc8785/_impl.py @@ -49,6 +49,9 @@ class IntegerDomainError(CanonicalizationError): """ def __init__(self, n: int) -> None: + """ + Initialize an `IntegerDomainError`. + """ super().__init__(f"{n} exceeds safe integer domain for JSON floats") @@ -59,6 +62,10 @@ class FloatDomainError(CanonicalizationError): """ def __init__(self, f: float) -> None: + """ + Initialize an `FloatDomainError`. + """ + super().__init__(f"{f} is not representable in JCS") diff --git a/test/test_impl.py b/test/test_impl.py index e74b522..2e93323 100644 --- a/test/test_impl.py +++ b/test/test_impl.py @@ -72,3 +72,26 @@ def test_es6_float_stringification_full(es6_test_file): actual = sink.getvalue().decode() assert actual == expected + + +def test_integer_domain(): + impl.dumps(impl._INT_MAX) + with pytest.raises(impl.IntegerDomainError): + impl.dumps(impl._INT_MAX + 1) + + impl.dumps(impl._INT_MIN) + with pytest.raises(impl.IntegerDomainError): + impl.dumps(impl._INT_MIN - 1) + + +def test_string_invalid_utf8(): + # escaped surrogate is fine + impl.dumps("\\udead") + with pytest.raises(impl.CanonicalizationError): + impl.dumps("\udead") + + +def test_dumps_invalid_type(): + with pytest.raises(impl.CanonicalizationError): + # set is not serializable + impl.dumps({1, 2, 3}) diff --git a/test/test_init.py b/test/test_init.py index 1e391d9..44ebb63 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -24,3 +24,11 @@ def test_roundtrip(vector, name): actual_deserialized = json.loads(actual_output) assert actual_deserialized == py_input + + +def test_exception_hierarchy(): + assert issubclass(rfc8785.CanonicalizationError, ValueError) + assert issubclass(rfc8785.IntegerDomainError, rfc8785.CanonicalizationError) + assert issubclass(rfc8785.FloatDomainError, rfc8785.CanonicalizationError) + assert not issubclass(rfc8785.IntegerDomainError, rfc8785.FloatDomainError) + assert not issubclass(rfc8785.FloatDomainError, rfc8785.IntegerDomainError)