Skip to content

Commit

Permalink
Implement Element.__html__.
Browse files Browse the repository at this point in the history
This makes Element compatible with markupsafes escape() and Django's
django.utils.html.conditional_escape.
  • Loading branch information
pelme committed Oct 23, 2024
1 parent a053e81 commit 0e554fd
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 3 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## next
- Implement `Element.__html__`. This avoids double escaping when passed
to `markupsafe.escape` and Django's `django.utils.html.conditional_escape`.
- Raise errors directly on invalid children. This avoids cryptic stack traces.
[PR #56](https://github.com/pelme/htpy/pull/56).
- Raise TypeError rather than ValueError when invalid types are passed as
Expand Down
2 changes: 2 additions & 0 deletions htpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ def __init__(self, name: str, attrs_str: str = "", children: Node = None) -> Non
def __str__(self) -> _Markup:
return _Markup("".join(self))

__html__ = __str__

@t.overload
def __call__(
self: BaseElementSelf, id_class: str, attrs: dict[str, Attribute], **kwargs: Attribute
Expand Down
7 changes: 6 additions & 1 deletion tests/test_django.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.forms.utils import ErrorList
from django.template import Context, Template, TemplateDoesNotExist
from django.template.loader import render_to_string
from django.utils.html import escape
from django.utils.html import conditional_escape, escape
from django.utils.safestring import SafeString

from htpy import Element, Node, div, li, ul
Expand Down Expand Up @@ -68,3 +68,8 @@ def test_system_checks_works(self) -> None:
# Django 5.1 requires template backends to implement a check() method.
# This test ensures that it does not crash.
management.call_command("check")


def test_conditional_escape() -> None:
result = conditional_escape(div["test"]) # type: ignore[arg-type]
assert result == "<div>test</div>"
17 changes: 15 additions & 2 deletions tests/test_element.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import markupsafe
import pytest
from markupsafe import Markup
from typing_extensions import assert_type

import htpy
Expand Down Expand Up @@ -29,7 +29,7 @@ def test_void_element_repr() -> None:
def test_markup_str() -> None:
result = str(div(id="a"))
assert isinstance(result, str)
assert isinstance(result, Markup)
assert isinstance(result, markupsafe.Markup)
assert result == '<div id="a"></div>'


Expand All @@ -42,3 +42,16 @@ def test_element_type() -> None:

assert_type(div()["a"], Element)
assert isinstance(div()["a"], Element)


def test_html_protocol() -> None:
element = div["test"]
result = element.__html__()
assert result == "<div>test</div>"
assert isinstance(result, markupsafe.Markup)


def test_markupsafe_escape() -> None:
result = markupsafe.escape(div["test"])
assert result == "<div>test</div>"
assert isinstance(result, markupsafe.Markup)

0 comments on commit 0e554fd

Please sign in to comment.