marimo
marimo copied to clipboard
FastAPI migration
EDIT(akshay):
Checklist
- [ ] Test wake from sleep reconnection in edit mode (see this comment)
- [ ] Test on Windows (@akshayka)
- [ ] Lower bounds for additional packages (uvicorn, websockets)
- [ ] Strategy for supporting multiple package versions for uvicorn, fastapi — they aren't stable yet
- [ ] Test multiple Pydantic versions in CI: 0.1.74, >2.
- [ ] Pin fastapi to 0.95.0 in our CI tests
- [ ] Redo the server logger to not use tornado
- [ ] Reimplement
mplinteractive to not use tornado - [ ] Update
marimo envto check new packages, remove tornado
The latest updates on your projects. Learn more about Vercel for Git ↗︎
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| marimo-docs | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Jan 24, 2024 6:41pm |
| marimo-storybook | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Jan 24, 2024 6:41pm |
Issues we've found:
- Verbose errors b/c of in-progress coroutines
- Shutdown fails -- uvicorn (I believe) still running, port stays open. True for UI shutdown and shutdown from terminal (Ctrl-C).
- Sometimes see "another browser tab is already connected", even though there shouldn't be one; maybe racing with an open tab for an old (killed) notebook?
- Delete handler not working (see traceback in subsequent comment)
Things to check:
- Does TTL in run mode still work?
- Does reconnecting to the kernel after waking from sleep still work?
Things to consider:
- CSRF protection dropped -- Is this okay?
- What lower bounds should we use for the new dependencies?
- Pydantic seems very finicky based on Python version; we will have to code a bit more defensively.
~Issue with delete handler:~ Fixed
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
result = await app( # type: ignore[func-returns-value]
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/middleware/cors.py", line 91, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/middleware/cors.py", line 146, in simple_response
await self.app(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/routing.py", line 762, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/routing.py", line 782, in app
await route.handle(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/routing.py", line 297, in handle
await self.app(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/routing.py", line 72, in app
response = await func(request)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/fastapi/routing.py", line 299, in app
raise e
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/fastapi/routing.py", line 294, in app
raw_response = await run_endpoint_function(
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/fastapi/routing.py", line 193, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/anyio/to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 2134, in run_sync_in_worker_thread
return await future
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 851, in run
result = context.run(func, *args)
File "/home/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/editing.py", line 49, in delete_cell
session.control_queue.put(requests.DeleteRequest(cell_id=request.cell_id))
File "<string>", line 3, in __init__
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/pydantic/main.py", line 804, in __setattr__
self.__pydantic_fields_set__.add(name)
File "/home/akshay/envs/tmpenv/lib/python3.10/site-packages/pydantic/main.py", line 758, in __getattr__
return super().__getattribute__(item) # Raises AttributeError if appropriate
AttributeError: 'DeleteRequest' object has no attribute '__pydantic_fields_set__'
@mscolnick
I tested whether the kernel reconnects in edit mode after waking from sleep. It looks like the kernel reconnects, but many unfriendly errors are printed to the console, which we should try to eliminate. Here are some examples:
Task was destroyed but it is pending! task: <Task pending name='Task-39' coro=<WebsocketHandler.start.<locals>.listen_for_messages() done, defined at /Users/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/ws.py:200> wait_for=<Future cancelle
d> cb=[_gather.<locals>._done_callback() at /opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/tasks.py:767]>
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/ws.py", line 215, in listen_for_disconnect
await self.websocket.receive_text()
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/websockets.py", line 113, in receive_text
self._raise_on_disconnect(message)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/websockets.py", line 105, in _raise_on_disconnect
raise WebSocketDisconnect(message["code"], message.get("reason"))
starlette.websockets.WebSocketDisconnect: (1006, None)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 254, in run_asgi
result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/middleware/errors.py", line 151, in __call__
await self.app(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/middleware/cors.py", line 75, in __call__
await self.app(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/routing.py", line 762, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/routing.py", line 782, in app
await route.handle(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/routing.py", line 373, in handle
await self.app(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/routing.py", line 96, in app
await wrap_app_handling_exceptions(app, session)(scope, receive, send)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/starlette/routing.py", line 94, in app
await func(session)
File "/Users/akshay/envs/marimo/lib/python3.9/site-packages/fastapi/routing.py", line 360, in app
await dependant.call(**values)
File "/Users/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/ws.py", line 39, in websocket_endpoint
await WebsocketHandler(
File "/Users/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/ws.py", line 256, in start
await asyncio.gather(
File "/Users/akshay/src/marimo-team/marimo/marimo/_server2/api/endpoints/ws.py", line 246, in listen_for_disconnect
self.message_queue.task_done()
File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/queues.py", line 209, in task_done
raise ValueError('task_done() called too many times')
ValueError: task_done() called too many times
Refresh in edit mode appears to shutdown or otherwise break the server (?). Confirmed that this bug existed in the FastAPI implementation too; not introduced by the switch to Starlette.
print_shutdown()is called, printing 'Thanks for using marimo' to the console- the notebook frontend fails to connect to a new kernel ("Kernel not found")
- have to force quit (ctrl-c)
- marimo process still running afterward
[D 240122 10:48:17 ws:218] Websocket disconnected for session 96fffabc-af13-4efb-9566-679455b9abf6
[D 240122 10:48:17 ws:262] Websocket terminated with CancelledError
Thanks for using marimo! 🌊🍃
^CException ignored in atexit callback: <function _exit_function at 0x7fb64914caf0>
Traceback (most recent call last):
File "/usr/lib/python3.10/multiprocessing/util.py", line 357, in _exit_function
p.join()
File "/usr/lib/python3.10/multiprocessing/process.py", line 149, in join
res = self._popen.wait(timeout)
File "/usr/lib/python3.10/multiprocessing/popen_fork.py", line 43, in wait
return self.poll(os.WNOHANG if timeout == 0.0 else 0)
File "/usr/lib/python3.10/multiprocessing/popen_fork.py", line 27, in poll
pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt:
Another issue with refreshing the page in edit mode, this time macOS Python 3.10, 3.11
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
result = await app( # type: ignore[func-returns-value]
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/middleware/cors.py", line 83, in __call__
await self.app(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 758, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 778, in app
await route.handle(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 487, in handle
await self.app(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 758, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 778, in app
await route.handle(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 299, in handle
await self.app(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 79, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
await response(scope, receive, send)
File "/Users/akshay/.pyenv/versions/3.10.13/envs/mo3.10/lib/python3.10/site-packages/starlette/responses.py", line 340, in __call__
elif "http.response.pathsend" in scope["extensions"]:
KeyError: 'extensions'
Codecov Report
Attention: 411 lines in your changes are missing coverage. Please review.
Comparison is base (
37c0143) 63.61% compared to head (c3b205b) 69.07%.
Additional details and impacted files
@@ Coverage Diff @@
## main #606 +/- ##
==========================================
+ Coverage 63.61% 69.07% +5.46%
==========================================
Files 140 139 -1
Lines 6662 6976 +314
Branches 1321 1410 +89
==========================================
+ Hits 4238 4819 +581
+ Misses 2240 1907 -333
- Partials 184 250 +66
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.