Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create_asgi_app to programatically run marimo apps #1002

Merged
merged 4 commits into from
Mar 27, 2024

Conversation

mscolnick
Copy link
Contributor

@mscolnick mscolnick commented Mar 26, 2024

This exposes a new function from the marimo module: create_asgi_app. Which is a builder to add many marimo apps:

Closes #904 #395

Regarding the API:

  • i chose a builder instead of seperate create_asgi_app so 1) we can share configs, 2) we can initialize shared singletons between the apps
  • i called the top-level root instead of filename so we can latter support dirs or code

It would look something like:

from typing import Annotated, Callable, Coroutine
from fastapi.responses import HTMLResponse, RedirectResponse
import marimo
from fastapi import FastAPI, Form, Request, Response


# Create a marimo asgi app
server = (
    marimo.create_asgi_app()
    .with_app(path="", root="./pages/index.py")
    .with_app(path="/dashboard", root="./pages/dashboard.py")
    .with_app(path="/sales", root="./pages/sales.py")
)

# Create a FastAPI app
app = FastAPI()

app.add_middleware(auth_middleware)
app.add_route("/login", my_login_route, methods=["POST"])

app.mount("/", server.build())

# Run the server
if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="localhost", port=8000)

Copy link

vercel bot commented Mar 26, 2024

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 Mar 27, 2024 9:19pm
marimo-storybook ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 27, 2024 9:19pm

akshayka
akshayka previously approved these changes Mar 27, 2024
Copy link
Contributor

@akshayka akshayka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! I can definitely see this unlocking more use cases. Two small comments, o.w. looks good.

Comment on lines 72 to 104
# class StripBaseURLMiddleware:
# def __init__(self, app: ASGIApp, base_url: str) -> None:
# self.app = app
# self.base_url = base_url

async def __call__(
self, scope: Scope, receive: Receive, send: Send
) -> None:
# If not HTTP, skip
if scope["type"] != "http":
return await self.app(scope, receive, send)
# async def __call__(
# self, scope: Scope, receive: Receive, send: Send
# ) -> None:
# # If not HTTP, skip
# if scope["type"] != "http":
# return await self.app(scope, receive, send)

# If base URL is empty, skip
if self.base_url == "" or self.base_url == "/":
return await self.app(scope, receive, send)
# # If base URL is empty, skip
# if self.base_url == "" or self.base_url == "/":
# return await self.app(scope, receive, send)

request = Request(scope)
# request = Request(scope)

# If under common infra routes, allow
if request.url.path in ALLOWED_BASE_URLS:
return await self.app(scope, receive, send)
# # If under common infra routes, allow
# if request.url.path in ALLOWED_BASE_URLS:
# return await self.app(scope, receive, send)

# If not under base URL or under infra routes, return 404
# Otherwise, this may hide real issues
if not request.url.path.startswith(self.base_url):
response = JSONResponse({"error": "Not found"}, status_code=404)
return await response(scope, receive, send)
# # If not under base URL or under infra routes, return 404
# # Otherwise, this may hide real issues
# if not request.url.path.startswith(self.base_url):
# response = JSONResponse({"error": "Not found"}, status_code=404)
# return await response(scope, receive, send)

# Strip base URL
scope["path"] = scope["path"][len(self.base_url) :]
if not scope["path"].startswith("/"):
scope["path"] = "/" + scope["path"]
return await self.app(scope, receive, send)
# # Strip base URL
# scope["path"] = scope["path"][len(self.base_url) :]
# if not scope["path"].startswith("/"):
# scope["path"] = "/" + scope["path"]
# return await self.app(scope, receive, send)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can all this dead code be deleted?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asgi.py -> test_asgi.py, to make sure pytest runs it.

@mscolnick mscolnick merged commit 784c48b into main Mar 27, 2024
29 checks passed
@mscolnick mscolnick deleted the ms/create_asgi_app branch March 27, 2024 21:22
@github-actions github-actions bot locked and limited conversation to collaborators Mar 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Embed Marimo in FastAPI/Starlette Server
2 participants