Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| from typing import Literal | |
| import httpx | |
| import nbformat | |
| from nbformat import NotebookNode, ValidationError | |
| from nbconvert import HTMLExporter | |
| from starlette.applications import Starlette | |
| from starlette.exceptions import HTTPException | |
| from starlette.responses import FileResponse, JSONResponse, HTMLResponse | |
| from starlette.requests import Request | |
| from starlette.routing import Route | |
| client = httpx.AsyncClient() | |
| html_exporter = HTMLExporter(template_name="lab") | |
| async def homepage(_): | |
| return FileResponse("static/index.html") | |
| async def healthz(_): | |
| return JSONResponse({"success": True}) | |
| def convert(s: str, theme: Literal["light", "dark"], debug_info: str) -> str: | |
| # Capture potential validation error: | |
| try: | |
| notebook_node: NotebookNode = nbformat.reads( | |
| s, | |
| as_version=nbformat.current_nbformat, | |
| ) | |
| except nbformat.reader.NotJSONError: | |
| print(400, f"Notebook is not JSON. {debug_info}") | |
| raise HTTPException(400, f"Notebook is not JSON.") | |
| except ValidationError as e: | |
| print( | |
| 400, | |
| f"Notebook is invalid according to nbformat: {e}. {debug_info}", | |
| ) | |
| raise HTTPException( | |
| 400, | |
| f"Notebook is invalid according to nbformat: {e}.", | |
| ) | |
| print(f"Input: nbformat v{notebook_node.nbformat}.{notebook_node.nbformat_minor}") | |
| html_exporter.theme = theme | |
| body, _ = html_exporter.from_notebook_node(notebook_node) | |
| # TODO(customize or simplify template?) | |
| # TODO(also check source code for jupyter/nbviewer) | |
| return body | |
| async def convert_from_url(req: Request): | |
| url = req.query_params.get("url") | |
| theme = "dark" if req.query_params.get("theme") == "dark" else "light" | |
| if not url: | |
| raise HTTPException(400, "Param url is missing") | |
| print("\n===", url) | |
| r = await client.get( | |
| url, | |
| follow_redirects=True, | |
| # httpx no follow redirect by default | |
| ) | |
| if r.status_code != 200: | |
| raise HTTPException( | |
| 400, f"Got an error {r.status_code} when fetching remote file" | |
| ) | |
| return HTMLResponse(content=convert(r.text, theme=theme, debug_info=f"url={url}")) | |
| async def convert_from_upload(req: Request): | |
| theme = "dark" if req.query_params.get("theme") == "dark" else "light" | |
| s = (await req.body()).decode("utf-8") | |
| return HTMLResponse( | |
| content=convert( | |
| s, theme=theme, debug_info=f"upload_from={req.headers.get('user-agent')}" | |
| ) | |
| ) | |
| app = Starlette( | |
| debug=False, | |
| routes=[ | |
| Route("/", homepage), | |
| Route("/healthz", healthz), | |
| Route("/convert", convert_from_url), | |
| Route("/upload", convert_from_upload, methods=["POST"]), | |
| ], | |
| ) | |