posit-dev / py-shiny

Shiny for Python
https://shiny.posit.co/py/
MIT License
1.1k stars 62 forks source link

HTML dependencies in dynamic UI don't work with Pyodide #55

Closed wch closed 1 year ago

wch commented 2 years ago

Examples: https://pyshiny.netlify.app/examples/#dynamically-inserting-ui https://pyshiny.netlify.app/examples/#ui-output (select the slider)

wch commented 1 year ago

Interestingly, the apps work in Firefox, but not Chrome or Safari on my Mac. I think it's because it's trying to fetch the JS files with a synchronous XMLHttpRequest, and those requests are not getting intercepted by the Service Worker on Chrome or Safari. This sync XHR + Service Worker test page passes on Firefox bur fails on Chrome and Safari: https://w3c-test.org/service-workers/service-worker/fetch-request-xhr-sync.https.html

More references: https://wpt.fyi/results/service-workers/service-worker/fetch-request-xhr-sync.https.html?label=experimental&label=master&aligned https://stackoverflow.com/a/54319282/412655


On Chrome, it manages to fetch the .css files, but not the .js files:

image

It appears that it's trying to fetch the .js files with synchronous xhr. The request headers are:

GET /examples/app_appxzgxiqswz3ymwz80m/lib/ionrangeslider-2.3.1/js/ion.rangeSlider.min.js HTTP/1.1
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/examples/app_appxzgxiqswz3ymwz80m/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
X-Requested-With: XMLHttpRequest
sec-ch-ua: ".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"

I think this is probably due to how jQuery handles the injection of script tags -- we'll probably have to work around jQuery's <script> loading method somehow.

wch commented 1 year ago

Potential fix here: https://github.com/rstudio/shiny/pull/3666

Test app that @jcheng5 made:

from shiny import *
import random
import asyncio

app_ui = ui.page_fluid(
    ui.input_action_button("shuffle", "Shuffle"),
    ui.output_ui("inputs1"),
    ui.output_ui("inputs2"),
    ui.output_text("out"),
)

def server(input, output, session):
    @reactive.Calc
    def rand():
        req(input.shuffle())
        return random.randint(0, 1000)

    @output
    @render.ui
    def inputs1():
        input.x.freeze()
        return ui.input_slider("x", "x", min=1, max=1001, value=rand())

    @output
    @render.ui
    def inputs2():
        input.y.freeze()
        return ui.input_numeric("y", "y", min=1, max=1001, value=rand())

    @output
    @render.text
    async def out():
        print("======Out called======")

        # For the purposes of this repro, it's important that input.y is depended
        # on first, because there's a window of time where y is unfrozen but x is not.
        input.y()
        input.x()

        if input.x() != input.y():
            raise Exception(f"Inconsistent inputs: {input.x()} {input.y()}")
        if input.x() != rand():
            raise Exception(f"Input doesn't match calc: {input.x()} {rand()}")
        await asyncio.sleep(0.5)
        return f"Hello {input.x()} {input.y()}"

app = App(app_ui, server, debug=True)
wch commented 1 year ago

Closed by 6a5f45558fe69f9b6dc89b5fc7451136220926db.

The updated version of shiny.js is from https://github.com/rstudio/shiny/pull/3666, commit 796e81c3. It's not merged yet, but hopefully we'll settle on a solution for this; then we can update shiny.js again.