diff --git a/.github/workflows/dashboard.yaml b/.github/workflows/dashboard.yaml deleted file mode 100644 index ee47b479..00000000 --- a/.github/workflows/dashboard.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Push issue to Github Project dashboard - -on: - issues: - types: - - opened - pull_request_target: - types: - - opened - -jobs: - add_to_project: - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@v0.0.3 - with: - project-url: https://github.com/orgs/pyscript/projects/4/ - github-token: ${{ secrets.PROJECT_TOKEN }} diff --git a/pyscriptjs/src/python/pyscript.py b/pyscriptjs/src/python/pyscript.py index f8999b4e..816082a4 100644 --- a/pyscriptjs/src/python/pyscript.py +++ b/pyscriptjs/src/python/pyscript.py @@ -1,5 +1,6 @@ import asyncio import base64 +import html import io import time from textwrap import dedent @@ -35,7 +36,7 @@ def identity(value, meta): MIME_RENDERERS = { - "text/plain": identity, + "text/plain": html.escape, "text/html": identity, "image/png": lambda value, meta: render_image("image/png", value, meta), "image/jpeg": lambda value, meta: render_image("image/jpeg", value, meta), @@ -45,6 +46,18 @@ MIME_RENDERERS = { } +class HTML: + """ + Wrap a string so that display() can render it as plain HTML + """ + + def __init__(self, html): + self._html = html + + def _repr_html_(self): + return self._html + + def eval_formatter(obj, print_method): """ Evaluates a formatter method. @@ -68,7 +81,7 @@ def format_mime(obj): Formats object using _repr_x_ methods. """ if isinstance(obj, str): - return obj, "text/plain" + return html.escape(obj), "text/plain" mimebundle = eval_formatter(obj, "_repr_mimebundle_") if isinstance(mimebundle, tuple): diff --git a/pyscriptjs/tests/integration/test_02_output.py b/pyscriptjs/tests/integration/test_02_output.py index ef7372b3..0605677d 100644 --- a/pyscriptjs/tests/integration/test_02_output.py +++ b/pyscriptjs/tests/integration/test_02_output.py @@ -1,3 +1,4 @@ +import html import re import pytest @@ -201,6 +202,30 @@ class TestOutput(PyScriptTest): == "['A', 1, '!']\n{'B': 2, 'List': ['A', 1, '!']}\n('C', 3, '!')" ) + def test_display_should_escape(self): + self.pyscript_run( + """ + + display("

hello world

") +
+ """ + ) + out = self.page.locator("py-script > div") + assert out.inner_html() == html.escape("

hello world

") + assert out.inner_text() == "

hello world

" + + def test_display_HTML(self): + self.pyscript_run( + """ + + display(HTML("

hello world

")) +
+ """ + ) + out = self.page.locator("py-script > div") + assert out.inner_html() == "

hello world

" + assert out.inner_text() == "hello world" + def test_image_display(self): self.pyscript_run( """ diff --git a/pyscriptjs/tests/py-unit/test_pyscript.py b/pyscriptjs/tests/py-unit/test_pyscript.py index fec923b5..a7b7c09d 100644 --- a/pyscriptjs/tests/py-unit/test_pyscript.py +++ b/pyscriptjs/tests/py-unit/test_pyscript.py @@ -1,3 +1,4 @@ +import sys from unittest.mock import Mock import pyscript @@ -24,7 +25,26 @@ class TestElement: def test_format_mime_str(): obj = "just a string" + out, mime = pyscript.format_mime(obj) + assert out == obj + assert mime == "text/plain" - res = pyscript.format_mime(obj) - assert res[0] == obj - assert res[1] == "text/plain" + +def test_format_mime_str_escaping(): + obj = "

hello

" + out, mime = pyscript.format_mime(obj) + assert out == "<p>hello</p>" + assert mime == "text/plain" + + +def test_format_mime_repr_escaping(): + out, mime = pyscript.format_mime(sys) + assert out == "<module 'sys' (built-in)>" + assert mime == "text/plain" + + +def test_format_mime_HTML(): + obj = pyscript.HTML("

hello

") + out, mime = pyscript.format_mime(obj) + assert out == "

hello

" + assert mime == "text/html"