PyScript: Can't find a pure Python 3 wheel for 'orm==0.2.0.dev1'

I have a bokeh app that I am trying to convert into a standalone webassembly app via:

panel convert demo.py --to pyodide-worker

which successfully produces the relevant demo.html and demo.js files. I then serve this up using:

python -m http.server

And navigate to http://localhost:8000/demo.html, where I see the following console log error:

[bokeh] setting log level to: 'info'
bokeh-2.4.3.min.js:165 [bokeh] document idle at 154 ms
demo.js:12 Loading pyodide!
pyodide.asm.js:10 Loading distutils
pyodide.asm.js:10 Loaded distutils
pyodide.asm.js:10 Python initialization complete
demo.js:16 Loaded!
pyodide.asm.js:10 distutils already loaded from default channel
pyodide.asm.js:10 Loading micropip, pyparsing, packaging
pyodide.asm.js:10 Loaded pyparsing, packaging, micropip
pyodide.asm.js:10 Loading jinja2, markupsafe, numpy, pyyaml, pillow, typing-extensions
pyodide.asm.js:10 Loaded typing-extensions, markupsafe, jinja2, pyyaml, pillow, numpy
pyodide.asm.js:10 packaging already loaded from default channel
pyodide.asm.js:10 distutils already loaded from default channel
pyodide.asm.js:10 pyparsing already loaded from default channel
pyodide.asm.js:10 Loading bleach, webencodings, six, tqdm, setuptools, certifi
pyodide.asm.js:10 Loaded webencodings, six, bleach, tqdm, setuptools, certifi
pyodide.asm.js:10 Uncaught (in promise) PythonError: Traceback (most recent call last):
  File "/lib/python3.10/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.10/asyncio/tasks.py", line 234, in __step
    result = coro.throw(exc)
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 506, in eval_code_async
    await CodeRunner(
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 359, in run_async
    await coroutine
  File "<exec>", line 3, in <module>
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 548, in install
    await transaction.gather_requirements(requirements)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 305, in gather_requirements
    await gather(*requirement_promises)
  File "/lib/python3.10/asyncio/futures.py", line 284, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.10/asyncio/tasks.py", line 304, in __wakeup
    future.result()
  File "/lib/python3.10/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.10/asyncio/tasks.py", line 234, in __step
    result = coro.throw(exc)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 312, in add_requirement
    return await self.add_requirement_inner(Requirement(req))
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 420, in add_requirement_inner
    await self.add_wheel(wheel, req.extras)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 435, in add_wheel
    await self.gather_requirements(wheel.requires(extras))
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 305, in gather_requirements
    await gather(*requirement_promises)
  File "/lib/python3.10/asyncio/futures.py", line 284, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.10/asyncio/tasks.py", line 304, in __wakeup
    future.result()
  File "/lib/python3.10/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.10/asyncio/tasks.py", line 232, in __step
    result = coro.send(None)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 309, in add_requirement
    return await self.add_requirement_inner(req)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 407, in add_requirement_inner
    wheel = find_wheel(metadata, req)
  File "/lib/python3.10/site-packages/micropip/_micropip.py", line 275, in find_wheel
    raise ValueError(
ValueError: Can't find a pure Python 3 wheel for 'orm==0.2.0.dev1'.
See: https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel
You can use `micropip.install(..., keep_going=True)`to get a list of all packages with missing wheels.

    at new_error (pyodide.asm.js:10:218123)
    at pyodide.asm.wasm:0xdef7c
    at pyodide.asm.wasm:0xe37ae
    at method_call_trampoline (pyodide.asm.js:10:218037)
    at pyodide.asm.wasm:0x126317
    at pyodide.asm.wasm:0x1f6f2e
    at pyodide.asm.wasm:0x161a32
    at pyodide.asm.wasm:0x126827
    at pyodide.asm.wasm:0x126921
    at pyodide.asm.wasm:0x1269c4
    at pyodide.asm.wasm:0x1e0697
    at pyodide.asm.wasm:0x1da6a5
    at pyodide.asm.wasm:0x126a07
    at pyodide.asm.wasm:0x1e248c
    at pyodide.asm.wasm:0x1e00d9
    at pyodide.asm.wasm:0x1da6a5
    at pyodide.asm.wasm:0x126a07
    at pyodide.asm.wasm:0xe347a
    at Module.callPyObjectKwargs (pyodide.asm.js:10:119064)
    at Module.callPyObject (pyodide.asm.js:10:119442)
    at wrapper (pyodide.asm.js:10:183746)
new_error @ pyodide.asm.js:10
$wrap_exception @ pyodide.asm.wasm:0xdef7c
$FutureDoneCallback_call @ pyodide.asm.wasm:0xe37ae
method_call_trampoline @ pyodide.asm.js:10
$_PyObject_MakeTpCall @ pyodide.asm.wasm:0x126317
$func3192 @ pyodide.asm.wasm:0x1f6f2e
$func1855 @ pyodide.asm.wasm:0x161a32
$PyVectorcall_Call @ pyodide.asm.wasm:0x126827
$_PyObject_Call @ pyodide.asm.wasm:0x126921
$PyObject_Call @ pyodide.asm.wasm:0x1269c4
$_PyEval_EvalFrameDefault @ pyodide.asm.wasm:0x1e0697
$func3015 @ pyodide.asm.wasm:0x1da6a5
$_PyFunction_Vectorcall @ pyodide.asm.wasm:0x126a07
$func3027 @ pyodide.asm.wasm:0x1e248c
$_PyEval_EvalFrameDefault @ pyodide.asm.wasm:0x1e00d9
$func3015 @ pyodide.asm.wasm:0x1da6a5
$_PyFunction_Vectorcall @ pyodide.asm.wasm:0x126a07
$_pyproxy_apply @ pyodide.asm.wasm:0xe347a
Module.callPyObjectKwargs @ pyodide.asm.js:10
Module.callPyObject @ pyodide.asm.js:10
wrapper @ pyodide.asm.js:10

Pyodide seems to be looking for the orm package but I’m not certain where that requirement is coming from. This app depends on numpy, pandas, and bokeh and, for reference, a properly running (pure bokeh) instance can be found here.

What do I need to do in order to get this app running as a standalone webassembly/pyscript app?

So this is probably not documented well but the issue is that right now your entire app has to be defined in one module. In your case demo.py imports dashboard.py but since we don’t resolve local files currently it assumes you mean the dashboard library on PyPI.

1 Like

Also I’m not sure we properly support pure bokeh apps right now so you may have to change curdoc().add_root(layout) to pn.state.curdoc.add_root(layout).

1 Like

I’ll try to figure out how to support both.

1 Like

Also had to add URLs for raw.csv and matrix_profile.csv


    def get_df_from_file(self):
        raw_df = pd.read_csv('https://raw.githubusercontent.com/seanlaw/stumpy-live-demo/master/raw.csv')

        mp_df = pd.read_csv('https://raw.githubusercontent.com/seanlaw/stumpy-live-demo/master/matrix_profile.csv')
        ...

but once I did that it seems to have worked:

1 Like

Actually Play doesn’t work because bokeh’s periodic callback calls into tornado. You’ll have to replace it with pn.state.add_periodic_callback. Will post the full app in a minute.

1 Like

Here’s the dashboard.py that you can convert and the converted HTML and JS files.

dashboard.html (890.4 KB)
dashboard.js (16.8 KB)
dashboard.py (14.5 KB)

and here it is live:

http://assets.holoviz.org/demos/stumpy_demo.html

3 Likes

@philippjfr Thank you so much for you assistance (and all of your and your team’s work on PyScript)! I was working on it while you were updating this discussion and I landed on something similar in the new app.py file (now in the same repo), which combines what you described above (including the need to add/remove callbacks through Panel).

Honestly, this feels like magic!

2 Likes

Nice, glad you got it working!

3 Likes

I added a theme to the app

Source: awesome-panel/examples
Live App: Stumpy Timeseries Analysis Dashboard

stumpy-dashboard

5 Likes

It looks fantastic. Thanks, @Marc!

1 Like