whitphx / stlite

In-browser Streamlit 🎈🚀
https://edit.share.stlite.net
Apache License 2.0
1.17k stars 59 forks source link

Access to Pyodide JS Module #152

Open aghasemi opened 2 years ago

aghasemi commented 2 years ago

Hi, Running stlite on the main thread instead of the web worker makes the Pyodide js module usable, hence enabling interoeration between JavaScript and Python code. It can be as simple as an argument to the stlite.mount(..) function.

whitphx commented 2 years ago

I see, yes, probably making stlite run on the main thread instead of the worker is possible, so I would like to keep the discussion.

However, it is not much easy too. I mean, it takes some efforts because we probably need to implement 2 types of the Python code runner. See, for example in the source code of Shinylive, there are 2 separate classes; NormalPyodideProxy and WebWorkerPyodideProxy, which must take additional and maintenance efforts. So I would like to know what is the benefit if it become possible. Can you provide concrete examples about what is good if it is possible in Streamlit usage?

aghasemi commented 2 years ago

Consider the following example scenarios:

In all the above examples, all we need to do is to run one or a few lines of JS code, and get the results back into our Python script.

So how does Streamlit handle these scenarios? The only way I could find is to write a custom component to do these stuff, with a little bit of hacking. But that involves creating at least an extra HTML file with boilerplate and stuff, just to run one line JS.

So why not just use what is provided by Pyodide as a "Python" interface and have the JS interoperability in a seamless manner.?

Btw I just checked Panel's PyScript port and Pyodide's js also works there. They may have done something simpler to achieve that. Here is an example:

<html>
    <head>
      <title>Panel Example</title>
      <meta charset="iso-8859-1">
      <link rel="icon" type="image/x-icon" href="./favicon.png">
      <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
      <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
      <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
      <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.13.1/dist/panel.min.js"></script>
      <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
      <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
      <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>

      <style>
        table { width: 500px;}
        h1 {font-size: 30px}
      </style>
    </head>
    <py-env>
      - bokeh
      - numpy
      - panel==0.13.1
      - paths:
        - ./LICENSE
    </py-env>
    <body>
      <div id="simple_app"></div>
      <!--py-script src="panel.py" /-->
      <py-script>
        import js 
        print(f"Window width is {js.JSON.stringify(js.window.screen.width)}. ML5 version is {js.ml5.version}")

      </py-script>

  </body>
  </html>
whitphx commented 2 years ago

The only way I could find is to write a custom component to do these stuff,

That's true. I don't think it's a hack, but a "standard" way of Streamlit apps - Streamlit provides this versatile API to cover such a wide variety of usage.

I understand your point from kind of a practical/quick-use perspective, but my position is a bit more dogmatic; When developers need to do these things on Streamlit, they have to use the custom components, so I think if they need them on stlite, they should do the same too. There are some realistic reasons as follow;

Yet, as I wrote above, I understand your opinion is also persuasive. It's just a difference on what we put values. Let's keep this issue open. Anyway this takes some efforts and maintenance costs as written at https://github.com/whitphx/stlite/issues/152#issuecomment-1229487480 apart from my ideological statement above.

Pyodide's js also works there.

It looks like PyScript executes Pyodide in the main script, without creating a separate worker ( https://github.com/pyscript/pyscript/blob/f3157b377f843be4777124e591d1469723b5ec9f/pyscriptjs/src/components/pyconfig.ts#L65 ). As you said, it's simpler architecture allowing accessing the js module, but I wonder if there are performance problems. With this design, the Pyodide process blocks the main UI thread.

aghasemi commented 2 years ago

Actually, there are already some components that meet these needs. For example, this post introduced a custom component to access the cookie.

Indeed, I had this specific problem (accessing cookies) and the solution you mentioned above didn't work.

I wrote a simple custom component to run arbitrary JS code and get the result. It solves cokkie access issues and many other use cases (GeoLocation, Sharing API, Clipboard copying,...). It also works with STLite.

I recently put it on PyPI. Hope it helps someone else too. Feedback largely appreciated: https://github.com/aghasemi/streamlit_js_eval