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"]), | |
], | |
) | |