comicbox.schemas.xml_schemas

[docs] module comicbox.schemas.xml_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
85
86
87
88
89
90
91
92
93
94
95
96
97
"""XML Metadata parser superclass."""

from abc import ABC
from types import MappingProxyType
from typing import Any

import xmltodict
from marshmallow.fields import Constant
from typing_extensions import override

from comicbox.schemas.base import BaseRenderModule, BaseSchema, BaseSubSchema

XML_UNPARSE_ARGS = MappingProxyType(
    # used by tests
    {
        # Capitalize UTF-8 to be standard.
        "encoding": "UTF-8",
        "pretty": True,
        "short_empty_elements": True,
    }
)


class XmlRenderModule(BaseRenderModule):
    """Marshmallow Render Module imitates json module."""

    @override
    @classmethod
    def dumps(cls, obj: dict, *args: Any, **kwargs: Any) -> str:
        """Dump dict to XML string."""
        return xmltodict.unparse(  # ty: ignore[no-matching-overload]
            obj,
            *args,
            **XML_UNPARSE_ARGS,
            **kwargs,
        )

    @override
    @classmethod
    def loads(
        cls,
        s: str | bytes | bytearray,
        *args: Any,
        **kwargs: Any,
    ) -> Any:
        """Load XML string into a dict."""
        if cleaned_s := cls.clean_string(s):
            return xmltodict.parse(cleaned_s, *args, **kwargs)
        return None


class XmlSubSchema(BaseSubSchema, ABC):
    """XML Rendered Sub Schema."""

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

        render_module = XmlRenderModule


def create_xml_headers(
    ns: str, ns_uri: str, xsd_uri: str
) -> MappingProxyType[str, Constant]:
    """Create Namespace and Schema Location XML Attributes."""
    return MappingProxyType(
        {
            f"@xmlns:{ns}": Constant(ns_uri),
            "@xsi:schemaLocation": Constant(f"{ns_uri} {xsd_uri}"),
        }
    )


class XmlSubHeadSchema(XmlSubSchema, ABC):
    """XML Head Sub Schema customizations."""

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

        NS = ""
        NS_URI = ""
        XSD_URI = ""

        include = MappingProxyType(
            {
                "@xmlns:xsd": Constant("http://www.w3.org/2001/XMLSchema"),
                "@xmlns:xsi": Constant("http://www.w3.org/2001/XMLSchema-instance"),
            }
        )


class XmlSchema(BaseSchema, ABC):
    """Xml Rendered Schema."""

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

        render_module = XmlRenderModule