Skip to content

Commit 73092cd

Browse files
authored
feat(inner content): allow passing content as a function, as a custom parser (#13)
* feat(inner content): allow passing content as a function, as a custom parser * fix(render): add raw content helper function * bump version to 0.4.0 * docs: about the new raw content feature
1 parent 2535d4d commit 73092cd

6 files changed

Lines changed: 44 additions & 8 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ The HTML equivalent is:
106106
<script async src="js/script.js"></script>
107107
```
108108

109+
### Adding unescaped content
110+
This is useful when rendering HTML entities like `&copy;`.
111+
112+
``` python
113+
from python_hiccup.html import raw
114+
115+
data = ["div", raw("&copy; this should <strong>not</strong> be escaped!")]
116+
```
117+
118+
The HTML output:
119+
120+
``` html
121+
<div>&copy; this should <strong>not</strong> be escaped!</div>
122+
```
123+
109124
## Resources
110125
- [PyScript and python-hiccup example](https://pyscript.com/@davidvujic/pyscript-jokes-with-a-hiccup/latest?files=main.py) - PyScript Jokes with a Hiccup
111126
- [Hiccup](https://github.com/weavejester/hiccup) - the original implementation, for Clojure.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "python-hiccup"
3-
version = "0.3.4"
3+
version = "0.4.0"
44
description = "Python Hiccup is a library for representing HTML using plain Python data structures"
55
authors = [{name = "David Vujic"}]
66
readme = "README.md"

src/python_hiccup/html/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Render data into HTML."""
22

3-
from python_hiccup.html.core import render
3+
from python_hiccup.html.core import raw, render
44

5-
__all__ = ["render"]
5+
__all__ = ["raw", "render"]

src/python_hiccup/html/core.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import html
44
import operator
5-
from collections.abc import Mapping, Sequence
5+
from collections.abc import Callable, Mapping, Sequence
66
from functools import reduce
77

88
from python_hiccup.transform import CONTENT_TAG, transform
@@ -60,12 +60,16 @@ def _is_content(element: str) -> bool:
6060
return element == CONTENT_TAG
6161

6262

63+
def _is_raw(content: str | Callable) -> bool:
64+
return callable(content) and content.__name__ == "raw_content"
65+
66+
6367
def _to_html(tag: Mapping, parent: str = "") -> list:
6468
element = next(iter(tag.keys()))
6569
child = next(iter(tag.values()))
6670

6771
if _is_content(element):
68-
return [_escape(str(child), parent)]
72+
return child() if _is_raw(child) else [_escape(str(child), parent)]
6973

7074
attributes = reduce(_to_attributes, tag.get("attributes", []), "")
7175
bool_attributes = reduce(_to_bool_attributes, tag.get("boolean_attributes", []), "")
@@ -96,3 +100,12 @@ def render(data: Sequence) -> str:
96100
transformed_html: list = reduce(operator.iadd, matrix, [])
97101

98102
return "".join(transformed_html)
103+
104+
105+
def raw(content: str | float) -> Callable:
106+
"""Content that should not be escaped when rendering HTML elements."""
107+
108+
def raw_content() -> str | float:
109+
return content
110+
111+
return raw_content

src/python_hiccup/transform/core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""Transform a sequence of tag data into groups."""
22

33
from collections import defaultdict
4-
from collections.abc import Mapping, Sequence
4+
from collections.abc import Callable, Mapping, Sequence
55
from collections.abc import Set as AbstractSet
66
from functools import reduce
77

8-
Item = str | AbstractSet | Mapping | Sequence
8+
Item = str | AbstractSet | Mapping | Sequence | Callable
99

1010
ATTRIBUTES = "attributes"
1111
BOOLEAN_ATTRIBUTES = "boolean_attributes"

test/test_render_html.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Unit tests for the python_hiccup.html.render function."""
22

3-
from python_hiccup.html import render
3+
from python_hiccup.html import raw, render
44

55

66
def test_returns_a_string() -> None:
@@ -142,3 +142,11 @@ def test_order_of_items() -> None:
142142
data = ["h1", "some ", ["span.pys", "<py>"]]
143143

144144
assert render(data) == '<h1>some <span class="pys">&lt;py&gt;</span></h1>'
145+
146+
147+
def test_content_as_function() -> None:
148+
"""Allow defining content as a callable function, as a custom parser."""
149+
content = "&copy; this <strong>should</strong> not be escaped!"
150+
151+
assert render(["div", raw("&copy;")]) == "<div>&copy;</div>"
152+
assert render(["div", raw(content)]) == f"<div>{content}</div>"

0 commit comments

Comments
 (0)