Skip to content

Commit 6611915

Browse files
authored
display() should escape by default (#915)
- display(some_str) escapes the string by default. This is almost always what you want - display(some_obj) calls repr(obj) and escapes the result. Again, it's a very sensible default - if you want to inject some raw HTML in the output, you can use the new HTML class: display(HTML("<p>hello</p>")).
1 parent 5b671dd commit 6611915

File tree

4 files changed

+63
-23
lines changed

4 files changed

+63
-23
lines changed

.github/workflows/dashboard.yaml

Lines changed: 0 additions & 18 deletions
This file was deleted.

pyscriptjs/src/python/pyscript.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import base64
3+
import html
34
import io
45
import time
56
from textwrap import dedent
@@ -35,7 +36,7 @@ def identity(value, meta):
3536

3637

3738
MIME_RENDERERS = {
38-
"text/plain": identity,
39+
"text/plain": html.escape,
3940
"text/html": identity,
4041
"image/png": lambda value, meta: render_image("image/png", value, meta),
4142
"image/jpeg": lambda value, meta: render_image("image/jpeg", value, meta),
@@ -45,6 +46,18 @@ def identity(value, meta):
4546
}
4647

4748

49+
class HTML:
50+
"""
51+
Wrap a string so that display() can render it as plain HTML
52+
"""
53+
54+
def __init__(self, html):
55+
self._html = html
56+
57+
def _repr_html_(self):
58+
return self._html
59+
60+
4861
def eval_formatter(obj, print_method):
4962
"""
5063
Evaluates a formatter method.
@@ -68,7 +81,7 @@ def format_mime(obj):
6881
Formats object using _repr_x_ methods.
6982
"""
7083
if isinstance(obj, str):
71-
return obj, "text/plain"
84+
return html.escape(obj), "text/plain"
7285

7386
mimebundle = eval_formatter(obj, "_repr_mimebundle_")
7487
if isinstance(mimebundle, tuple):

pyscriptjs/tests/integration/test_02_output.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import html
12
import re
23

34
import pytest
@@ -201,6 +202,30 @@ def test_display_list_dict_tuple(self):
201202
== "['A', 1, '!']\n{'B': 2, 'List': ['A', 1, '!']}\n('C', 3, '!')"
202203
)
203204

205+
def test_display_should_escape(self):
206+
self.pyscript_run(
207+
"""
208+
<py-script>
209+
display("<p>hello world</p>")
210+
</py-script>
211+
"""
212+
)
213+
out = self.page.locator("py-script > div")
214+
assert out.inner_html() == html.escape("<p>hello world</p>")
215+
assert out.inner_text() == "<p>hello world</p>"
216+
217+
def test_display_HTML(self):
218+
self.pyscript_run(
219+
"""
220+
<py-script>
221+
display(HTML("<p>hello world</p>"))
222+
</py-script>
223+
"""
224+
)
225+
out = self.page.locator("py-script > div")
226+
assert out.inner_html() == "<p>hello world</p>"
227+
assert out.inner_text() == "hello world"
228+
204229
def test_image_display(self):
205230
self.pyscript_run(
206231
"""

pyscriptjs/tests/py-unit/test_pyscript.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from unittest.mock import Mock
23

34
import pyscript
@@ -24,7 +25,26 @@ def test_element(self, monkeypatch):
2425

2526
def test_format_mime_str():
2627
obj = "just a string"
28+
out, mime = pyscript.format_mime(obj)
29+
assert out == obj
30+
assert mime == "text/plain"
2731

28-
res = pyscript.format_mime(obj)
29-
assert res[0] == obj
30-
assert res[1] == "text/plain"
32+
33+
def test_format_mime_str_escaping():
34+
obj = "<p>hello</p>"
35+
out, mime = pyscript.format_mime(obj)
36+
assert out == "&lt;p&gt;hello&lt;/p&gt;"
37+
assert mime == "text/plain"
38+
39+
40+
def test_format_mime_repr_escaping():
41+
out, mime = pyscript.format_mime(sys)
42+
assert out == "&lt;module 'sys' (built-in)&gt;"
43+
assert mime == "text/plain"
44+
45+
46+
def test_format_mime_HTML():
47+
obj = pyscript.HTML("<p>hello</p>")
48+
out, mime = pyscript.format_mime(obj)
49+
assert out == "<p>hello</p>"
50+
assert mime == "text/html"

0 commit comments

Comments
 (0)