marimo icon indicating copy to clipboard operation
marimo copied to clipboard

FastAPI migration

Open mscolnick opened this issue 1 year ago • 4 comments

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 mpl interactive to not use tornado
  • [ ] Update marimo env to check new packages, remove tornado

mscolnick avatar Jan 18 '24 18:01 mscolnick

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

vercel[bot] avatar Jan 18 '24 18:01 vercel[bot]

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.

akshayka avatar Jan 18 '24 22:01 akshayka

~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__'

akshayka avatar Jan 18 '24 22:01 akshayka

@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

akshayka avatar Jan 20 '24 01:01 akshayka

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:

akshayka avatar Jan 22 '24 18:01 akshayka

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'

akshayka avatar Jan 22 '24 23:01 akshayka

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%.

Files Patch % Lines
marimo/_plugins/stateless/mpl/_mpl.py 18.91% 90 Missing :warning:
marimo/_server/api/lifespans.py 40.81% 57 Missing and 1 partial :warning:
marimo/_utils/log_formatter.py 33.87% 39 Missing and 2 partials :warning:
marimo/_server/api/endpoints/files.py 70.68% 25 Missing and 9 partials :warning:
marimo/_server/api/endpoints/ws.py 80.00% 18 Missing and 8 partials :warning:
marimo/_server/start.py 0.00% 20 Missing :warning:
marimo/_server/api/endpoints/assets.py 73.84% 17 Missing :warning:
marimo/_server/api/endpoints/config.py 54.05% 17 Missing :warning:
marimo/_server/api/deps.py 75.38% 14 Missing and 2 partials :warning:
marimo/_server/utils.py 26.31% 13 Missing and 1 partial :warning:
... and 14 more
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.

codecov[bot] avatar Jan 23 '24 01:01 codecov[bot]