nz-nz commited on
Commit
4d74f1c
·
verified ·
1 Parent(s): 1eec565

Fix Space RUNTIME_ERROR: publish demo=app.blocks for SpacesReloader, block_thread to stay alive

Browse files
Files changed (1) hide show
  1. server.py +25 -15
server.py CHANGED
@@ -326,25 +326,35 @@ async def index():
326
  return FileResponse(FRONTEND / "index.html")
327
 
328
 
329
- # HF runs `python server.py` (README app_file) and proxies port 7860. With
330
- # `gradio.Server` the launch happens at MODULE TOP LEVEL (not under
331
- # `if __name__ == "__main__"`) — that's how a gradio-SDK Space finds and runs the
332
- # app (and how gradio's own reference `Server` example deploys). Two things matter:
 
 
 
333
  # * ssr_mode=False — gradio 6 defaults to SSR, which needs a Node front-proxy on
334
- # 7860 and pushes the Python app to 7861; on a Space (no Node) that fails the
335
- # 7860 health check. Disabling SSR binds the Python server directly on 7860.
336
- # * we deliberately do NOT mount the legacy app.py Blocks here. A second
337
- # launchable named `demo` makes the Space's gradio reloader auto-launch IT on
338
- # 7860, colliding with this server. The standalone Gradio UI is still
339
- # available locally via `python app.py`.
340
- # The Space also sets GRADIO_HOT_RELOAD=false (a Space variable): with hot-reload
341
- # on, gradio's reloader makes launch() non-blocking and the process exits right
342
- # after binding 7860 (→ RUNTIME_ERROR). Disabling it lets launch() block and keeps
343
- # the one long-lived server alive. (Must be a Space variable, not set in-process —
344
- # HF sets the env in the parent before this module is imported.)
345
  app.launch(
346
  server_name="0.0.0.0",
347
  server_port=int(os.getenv("PORT", "7860")),
348
  ssr_mode=False,
 
349
  show_error=True,
350
  )
 
 
 
 
 
 
 
 
326
  return FileResponse(FRONTEND / "index.html")
327
 
328
 
329
+ # HF runs `python server.py` (README app_file) and proxies port 7860. Launch
330
+ # happens at MODULE TOP LEVEL (gradio.Server, like gradio's reference example).
331
+ #
332
+ # Running on a Space is fiddly because gradio *always* routes through its
333
+ # SpacesReloader there (regardless of GRADIO_HOT_RELOAD). `launch()` starts the
334
+ # uvicorn server AND a daemon "watch" thread (spaces.reloading) whose `postrun`
335
+ # does `getattr(__main__, "demo")` and compares it to the live Blocks. So:
336
  # * ssr_mode=False — gradio 6 defaults to SSR, which needs a Node front-proxy on
337
+ # 7860 and pushes Python to 7861; on a Space (no Node) that fails the 7860
338
+ # health check. Disabling SSR binds the Python server directly on 7860.
339
+ # * prevent_thread_lock=True return from launch() so we can publish `demo`
340
+ # before holding the thread ourselves.
341
+ # * `demo = app.blocks` the watcher requires a module global named `demo` that
342
+ # *is* the running Blocks. gradio.Server only populates `.blocks` during
343
+ # launch, so we must assign it AFTER. If it's missing (or isn't the live
344
+ # Blocks), postrun raises / tries to swap a non-Blocks and the Space dies with
345
+ # RUNTIME_ERROR. We do NOT mount the legacy app.py Blocks (a second `demo`
346
+ # would collide); the standalone Gradio UI is still available via `python app.py`.
 
347
  app.launch(
348
  server_name="0.0.0.0",
349
  server_port=int(os.getenv("PORT", "7860")),
350
  ssr_mode=False,
351
+ prevent_thread_lock=True,
352
  show_error=True,
353
  )
354
+
355
+ # Expose the live Blocks as the module global the SpacesReloader's postrun expects.
356
+ demo = app.blocks
357
+
358
+ # launch() returned (prevent_thread_lock); the server + watch threads are daemons,
359
+ # so hold the main thread open to keep them — and the Space — alive.
360
+ app.blocks.block_thread()