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
98
99
100
101
102
103
104
105
106 | """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.formats.base.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,
}
)
# xmltodict-style key for the XML `Image` attribute (`@` is xmltodict's
# attribute prefix). Used by ComicInfo's <Page Image="…"> elements and
# referenced by the YAML render module for flow-style rendering.
IMAGE_ATTRIBUTE = "@Image"
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
|