bokeh / bokeh-fastapi

BSD 3-Clause "New" or "Revised" License
9 stars 2 forks source link

running the app from uvicorns Python API does not work #3

Closed pmeier closed 4 months ago

pmeier commented 6 months ago

uvicorn test:app in the terminal is equivalent to python -c 'import uvicorn; uvicorn.run("test:app")'. This works fine.

Screencast from 2024-04-25 20-51-18.webm

However, for some reason, python -c 'import uvicorn, test; uvicorn.run(test.app)' does not. The server starts correctly and the WS is also connected. But for some reason no change is ever pushed back to the client.

Screencast from 2024-04-25 20-59-07.webm

pmeier commented 6 months ago

It seems to be some import side effect. Importing the test module before running the server somehow breaks the functionality: python -c 'import uvicorn, test; uvicorn.run("test:app")'

pmeier commented 6 months ago

It seems to be caused by the fact that we instantiate app in the test module. If we wrap into a make_app function, we can do python -c 'import uvicorn, test; uvicorn.run(test.make_app, factory=True)'. However, python -c 'import uvicorn, test; uvicorn.run(test.make_app())' still doesn't work.

My guess is that creating the app triggers some side effect that is only allowed to happen after the uvicorn server is started.

pmeier commented 6 months ago

Digging deeper, uvicorn.run internally uses asyncio.run. The app has to be created within this call rather than before to work correctly. The config is loaded at this point, which is why loading the app by string or creating it from the factory works.

pmeier commented 6 months ago

@philippjfr before I dig any deeper here, do you know of any side effects that the panel code could have that could cause a later execution of it by asyncio.run to somehow impede WS functionality?

philippjfr commented 6 months ago

before I dig any deeper here, do you know of any side effects that the panel code could have that could cause a later execution of it by asyncio.run to somehow impede WS functionality?

I do not but will dig in a little more.

pmeier commented 5 months ago

Here is another pointer: async functions are not called when passing an app instance to uvicorn.run

diff --git a/test.py b/test.py
index 8efa324..8d31df6 100644
--- a/test.py
+++ b/test.py
@@ -20,6 +20,9 @@ def dispatch_fastapi(conn, events=None, msg=None):

 extra_socket_handlers[WSHandler] = dispatch_fastapi

+async def hello():
+    return pn.pane.Markdown("Hello!")
+

 def panel_app(doc):
     doc.on_event('document_ready', partial(pn.state._schedule_on_load, doc))
@@ -28,6 +31,7 @@ def panel_app(doc):
         slider = pn.widgets.IntSlider(start=0, end=10, value=5)
         out = pn.Column(
             pn.pane.Markdown('### ' + pn.rx('*') * slider),
+            hello,
             pn.state.session_info,
             pn.state.curdoc
         )

python -c 'import uvicorn; uvicorn.run("test:app")'

image

python -c 'import uvicorn, test; uvicorn.run(test.app)'

image

pmeier commented 5 months ago

The function call is correctly scheduled here, but for some reason is never called.