anyxml 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyxml-0.1.0/PKG-INFO +28 -0
- anyxml-0.1.0/README.md +19 -0
- anyxml-0.1.0/pyproject.toml +14 -0
- anyxml-0.1.0/src/anyxml/__init__.py +57 -0
- anyxml-0.1.0/src/anyxml/py.typed +0 -0
anyxml-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: anyxml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A minimal reimplementation of `ET.tostring` that supports unescaped 'raw' text content.
|
|
5
|
+
Author: BHznJNs
|
|
6
|
+
Author-email: BHznJNs <bhznjns@outlook.com>
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# AnyXml
|
|
11
|
+
|
|
12
|
+
A minimal reimplementation of `ET.tostring` that supports unescaped "raw" text content.
|
|
13
|
+
|
|
14
|
+
The standard library's `ET.tostring` always XML-escapes the `text`/`tail` content of elements (e.g. `<`, `&`), which makes it impossible to inject pre-built, already-valid XML fragments (or other raw strings) during serialization.
|
|
15
|
+
This class provides :meth:`tostring`, which behaves similarly to `ET.tostring` but additionally supports:
|
|
16
|
+
|
|
17
|
+
- Strings wrapped in :class:`RawText` are emitted verbatim during serialization, without any escaping; regular `str` text is still escaped according to XML rules.
|
|
18
|
+
- Comment nodes (`ET.Comment`) and processing instruction nodes (`ET.ProcessingInstruction`) are rendered as `<!--...-->` and `<?...?>` respectively.
|
|
19
|
+
- Elements with no text and no children are rendered as self-closing tags (`<tag/>`).
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
>>> el = ET.Element("p")
|
|
25
|
+
>>> el.text = AnyXml.RawText("<b>bold</b>")
|
|
26
|
+
>>> AnyXml.tostring(el)
|
|
27
|
+
'<p><b>bold</b></p>'
|
|
28
|
+
```
|
anyxml-0.1.0/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# AnyXml
|
|
2
|
+
|
|
3
|
+
A minimal reimplementation of `ET.tostring` that supports unescaped "raw" text content.
|
|
4
|
+
|
|
5
|
+
The standard library's `ET.tostring` always XML-escapes the `text`/`tail` content of elements (e.g. `<`, `&`), which makes it impossible to inject pre-built, already-valid XML fragments (or other raw strings) during serialization.
|
|
6
|
+
This class provides :meth:`tostring`, which behaves similarly to `ET.tostring` but additionally supports:
|
|
7
|
+
|
|
8
|
+
- Strings wrapped in :class:`RawText` are emitted verbatim during serialization, without any escaping; regular `str` text is still escaped according to XML rules.
|
|
9
|
+
- Comment nodes (`ET.Comment`) and processing instruction nodes (`ET.ProcessingInstruction`) are rendered as `<!--...-->` and `<?...?>` respectively.
|
|
10
|
+
- Elements with no text and no children are rendered as self-closing tags (`<tag/>`).
|
|
11
|
+
|
|
12
|
+
## Example
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
>>> el = ET.Element("p")
|
|
16
|
+
>>> el.text = AnyXml.RawText("<b>bold</b>")
|
|
17
|
+
>>> AnyXml.tostring(el)
|
|
18
|
+
'<p><b>bold</b></p>'
|
|
19
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "anyxml"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A minimal reimplementation of `ET.tostring` that supports unescaped 'raw' text content."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "BHznJNs", email = "bhznjns@outlook.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["uv_build>=0.9.28,<0.10.0"]
|
|
14
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import xml.etree.ElementTree as ET
|
|
2
|
+
from xml.sax.saxutils import escape, quoteattr
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AnyXml:
|
|
6
|
+
"""A minimal reimplementation of `ET.tostring` that supports unescaped "raw" text content.
|
|
7
|
+
|
|
8
|
+
The standard library's `ET.tostring` always XML-escapes the `text`/`tail` content of elements (e.g. `<`, `&`), which makes it impossible to inject pre-built, already-valid XML fragments (or other raw strings) during serialization.
|
|
9
|
+
This class provides :meth:`tostring`, which behaves similarly to `ET.tostring` but additionally supports:
|
|
10
|
+
|
|
11
|
+
- Strings wrapped in :class:`RawText` are emitted verbatim during serialization, without any escaping; regular `str` text is still escaped according to XML rules.
|
|
12
|
+
- Comment nodes (`ET.Comment`) and processing instruction nodes (`ET.ProcessingInstruction`) are rendered as `<!--...-->` and `<?...?>` respectively.
|
|
13
|
+
- Elements with no text and no children are rendered as self-closing tags (`<tag/>`).
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
>>> el = ET.Element("p")
|
|
17
|
+
>>> el.text = AnyXml.RawText("<b>bold</b>")
|
|
18
|
+
>>> AnyXml.tostring(el)
|
|
19
|
+
'<p><b>bold</b></p>'
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
class RawText(str): pass
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def _text(s: str) -> str:
|
|
26
|
+
if isinstance(s, AnyXml.RawText): return s
|
|
27
|
+
return escape(s)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _comment(el: ET.Element) -> str:
|
|
31
|
+
assert el.tag is ET.Comment
|
|
32
|
+
return f"<!--{el.text or ''}-->"
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def _processing_instruction(el: ET.Element) -> str:
|
|
36
|
+
assert el.tag is ET.ProcessingInstruction
|
|
37
|
+
return f"<?{el.text or ''}?>"
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def tostring(el: ET.Element) -> str:
|
|
41
|
+
if el.tag is ET.Comment: return AnyXml._comment(el)
|
|
42
|
+
if el.tag is ET.ProcessingInstruction: return AnyXml._processing_instruction(el)
|
|
43
|
+
|
|
44
|
+
attrs = "".join(f" {k}={quoteattr(str(v))}" for k, v in el.attrib.items())
|
|
45
|
+
|
|
46
|
+
children_xml = []
|
|
47
|
+
for child in el:
|
|
48
|
+
children_xml.append(AnyXml.tostring(child))
|
|
49
|
+
if child.tail:
|
|
50
|
+
children_xml.append(AnyXml._text(child.tail))
|
|
51
|
+
|
|
52
|
+
if el.text is None and len(children_xml) == 0:
|
|
53
|
+
return f"<{el.tag}{attrs}/>"
|
|
54
|
+
|
|
55
|
+
inner_text = AnyXml._text(el.text) if el.text is not None else ""
|
|
56
|
+
body = inner_text + "".join(children_xml)
|
|
57
|
+
return f"<{el.tag}{attrs}>{body}</{el.tag}>"
|
|
File without changes
|