Skip to content

Commit c2d4a58

Browse files
committed
Add __radd__() support for HTML to add HTML() to str objects: str + HTML()
1 parent 1b658db commit c2d4a58

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

htmltools/_core.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,31 +1255,44 @@ class HTML(UserString):
12551255
<div><p>Hello</p></div>
12561256
"""
12571257

1258-
_html: str
1259-
12601258
def __init__(self, html: object) -> None:
12611259
if isinstance(html, HTML):
12621260
html = html.as_string()
1263-
self._html = str(html)
1261+
super().__init__(str(html))
12641262

12651263
def __str__(self) -> str:
12661264
return self.as_string()
12671265

1268-
# This class is a building block for other classes, therefore it should not tagifiable!
1269-
# If this method is added, HTML strings are escaped within Shiny and not kept "as is"
1266+
# DEV NOTE: 2024/09 -
1267+
# This class is a building block for other classes, therefore it should not
1268+
# tagifiable! If this method is added, HTML strings are escaped within Shiny and
1269+
# not kept "as is"
12701270
# def tagify(self) -> Tag:
12711271
# return self.as_string()
12721272

1273-
# HTML() + HTML() should return HTML()
1274-
# HTML() + str should return HTML()
1275-
# str + HTML() should return HTML() # This is not implemented and hard to catch!
1273+
# Cases:
1274+
# * `str + str` should return str # Not HTML's responsibility!
1275+
# * `str + HTML()` should return HTML() # Handled by HTML.__radd__()
1276+
# * `HTML() + str` should return HTML()
1277+
# * `HTML() + HTML()` should return HTML()
12761278
def __add__(self, other: object) -> HTML:
12771279
if isinstance(other, HTML):
12781280
# HTML strings should be concatenated without escaping
1281+
# Convert each element to strings, then concatenate them, and return HTML
1282+
# Case: `HTML() + HTML()`
12791283
return HTML(self.as_string() + other.as_string())
12801284

12811285
# Non-HTML text added to HTML should be escaped before being added
1282-
return HTML(str.__add__(self.as_string(), html_escape(str(other))))
1286+
# Convert each element to strings, then concatenate them, and return HTML
1287+
# Case: `HTML() + str`
1288+
return HTML(self.as_string() + html_escape(str(other)))
1289+
1290+
# Right side addition for when types are: `str + HTML()` or `unknown + HTML()`
1291+
def __radd__(self, other: object) -> HTML:
1292+
# Non-HTML text added to HTML should be escaped before being added
1293+
# Convert each element to strings, then concatenate them, and return HTML
1294+
# Case: `str + HTML()`
1295+
return HTML(html_escape(str(other)) + self.as_string())
12831296

12841297
def __eq__(self, x: object) -> bool:
12851298
# Set `x` first so that it can dispatch to the other object's __eq__ method as we've upgraded to `str`
@@ -1292,7 +1305,8 @@ def _repr_html_(self) -> str:
12921305
return self.as_string()
12931306

12941307
def as_string(self) -> str:
1295-
return self._html + ""
1308+
# Returns a new string
1309+
return self.data + ""
12961310

12971311

12981312
# =============================================================================

tests/test_tags.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,7 @@ def test_html_adds_str_or_html():
185185

186186
none = amp + amp
187187
first_html = HTML(amp) + amp
188-
with pytest.raises(TypeError, match='not "HTML"'):
189-
_ = amp + HTML(amp) # pyright: ignore[reportOperatorIssue]
190-
# # Can not run code as it causes an error
191-
# second_html = amp + HTML(amp)
188+
second_html = amp + HTML(amp)
192189

193190
both_html = HTML(amp) + HTML(amp)
194191

@@ -198,8 +195,8 @@ def test_html_adds_str_or_html():
198195
assert TagList(first_html).get_html_string() == f"{amp}{esc_amp}"
199196
assert isinstance(first_html, HTML)
200197

201-
# assert TagList(second_html).get_html_string() == f"{esc_amp}{amp}"
202-
# assert isinstance(second_html, HTML)
198+
assert TagList(second_html).get_html_string() == f"{esc_amp}{amp}"
199+
assert isinstance(second_html, HTML)
203200

204201
assert TagList(both_html).get_html_string() == f"{amp}{amp}"
205202
assert isinstance(both_html, HTML)

0 commit comments

Comments
 (0)