comicbox.schemas.json_schemas

[docs] module comicbox.schemas.json_schemas

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
"""Json Schema."""

from abc import ABC
from collections.abc import Mapping
from datetime import date, datetime
from types import MappingProxyType
from typing import Any

import simplejson as json
from typing_extensions import override

from comicbox.fields.time_fields import DateField, DateTimeField
from comicbox.schemas.base import BaseRenderModule, BaseSchema, BaseSubSchema


def datetime_handler(value):
    """Convert datetimes to strings for json.dumps."""
    if isinstance(value, date):
        value = DateField()._serialize(value, "", None)  # noqa: SLF001
    elif isinstance(value, datetime):
        value = DateTimeField()._serialize(value, "", None)  # noqa: SLF001
    return value


class JsonRenderModule(BaseRenderModule):
    """JSON Render module with custom formatting and Decimal support."""

    COMPACT_SEPARATORS = (",", ":")
    NORMAL_DUMPS_ARGS = MappingProxyType({"indent": 2})
    COMPACT_DUMPS_ARGS = MappingProxyType({"separators": COMPACT_SEPARATORS})

    @override
    @classmethod
    def dumps(
        cls,
        obj: Mapping,
        *args: Any,
        compact: bool = False,
        sort_keys: bool = False,
        **kwargs: Any,
    ) -> str:
        """Dump dict to JSON string with formatting."""
        extra_kwargs = cls.COMPACT_DUMPS_ARGS if compact else cls.NORMAL_DUMPS_ARGS
        return json.dumps(
            dict(obj),
            *args,
            sort_keys=sort_keys,
            iterable_as_array=True,
            use_decimal=True,
            default=datetime_handler,
            **extra_kwargs,
            **kwargs,
        )

    @override
    @classmethod
    def loads(
        cls,
        s: str | bytes | bytearray,
        *args: Any,
        **kwargs: Any,
    ) -> Any:
        """Load JSON string to dict."""
        if cleaned_s := cls.clean_string(s):
            return json.loads(
                cleaned_s,
                *args,
                use_decimal=True,
                **kwargs,
            )
        return None


class JsonSubSchema(BaseSubSchema, ABC):
    """Json Sub Schema."""


class JsonSchema(BaseSchema, ABC):
    """Json Schema."""

    class Meta(BaseSchema.Meta):
        """Schema Options."""

        render_module = JsonRenderModule