Skip to content

Commit edcee7c

Browse files
committed
Support progressive HTML rendering
1 parent cdd874b commit edcee7c

File tree

2 files changed

+13
-26
lines changed

2 files changed

+13
-26
lines changed

fps_plugins/voila/fps_voila/routes.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from mimetypes import guess_type
1010
from fastapi import APIRouter, Depends
11-
from fastapi.responses import RedirectResponse, HTMLResponse, Response
11+
from fastapi.responses import RedirectResponse, StreamingResponse, Response
1212
from fastapi.staticfiles import StaticFiles
1313
from fps.hooks import register_router # type: ignore
1414

@@ -23,15 +23,6 @@ class FPSVoilaHandler(_VoilaHandler):
2323
def redirect(self, url):
2424
return RedirectResponse(url)
2525

26-
def write(self, html):
27-
self.html += [html]
28-
29-
def flush(self):
30-
pass
31-
32-
def return_html(self):
33-
return HTMLResponse("".join(self.html))
34-
3526
def get_argument(self, name, default):
3627
if self.fps_arguments[name] is None:
3728
return default
@@ -85,7 +76,7 @@ async def get_root(voila_template: Optional[str] = None, voila_theme: Optional[s
8576
fps_voila_handler.fps_arguments["voila-template"] = voila_template
8677
fps_voila_handler.fps_arguments["voila-theme"] = voila_theme
8778
path = "" #voila_config.notebook_path or "/"
88-
return await _get(fps_voila_handler, path)
79+
return StreamingResponse(_get(fps_voila_handler, path))
8980

9081
@router.get("/voila/static/{path}")
9182
def get_file1(path):

voila/handler.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,21 +97,18 @@ async def _get(self, path=None):
9797
QueryStringSocketHandler.send_updates({'kernel_id': kernel_id, 'payload': self.request.query})
9898
# Send rendered cell to frontend
9999
if len(rendered_cache) > 0:
100-
self.write(''.join(rendered_cache))
101-
self.flush()
100+
yield ''.join(rendered_cache)
102101

103102
# Wait for current running cell finish and send this cell to
104103
# frontend.
105104
rendered, rendering = await render_task
106105
if len(rendered) > len(rendered_cache):
107106
html_snippet = ''.join(rendered[len(rendered_cache):])
108-
self.write(html_snippet)
109-
self.flush()
107+
yield html_snippet
110108

111109
# Continue render cell from generator.
112110
async for html_snippet, _ in rendering:
113-
self.write(html_snippet)
114-
self.flush()
111+
yield html_snippet
115112
self.flush()
116113

117114
else:
@@ -136,8 +133,7 @@ def time_out():
136133
can be used in a template to give feedback to a user
137134
"""
138135

139-
self.write('<script>voila_heartbeat()</script>\n')
140-
self.flush()
136+
yield '<script>voila_heartbeat()</script>\n'
141137

142138
kernel_env[ENV_VARIABLE.VOILA_PREHEAT] = 'False'
143139
kernel_env[ENV_VARIABLE.VOILA_BASE_URL] = self.base_url
@@ -154,14 +150,9 @@ def time_out():
154150
async for html_snippet, _ in gen.generate_content_generator(
155151
kernel_id, kernel_future, time_out
156152
):
157-
self.write(html_snippet)
158-
self.flush()
153+
yield html_snippet
159154
# we may not want to consider not flusing after each snippet, but add an explicit flush function to the jinja context
160155
# yield # give control back to tornado's IO loop, so it can handle static files or other requests
161-
self.flush()
162-
163-
if self.is_fps:
164-
return self.return_html()
165156

166157
class _VoilaHandler:
167158
is_fps = False
@@ -203,4 +194,9 @@ def should_use_rendered_notebook(
203194
class VoilaHandler(_VoilaHandler, JupyterHandler):
204195
@tornado.web.authenticated
205196
async def get(self, path=None):
206-
return await _get(self, path=path)
197+
it = _get(self, path=path)
198+
async for html in it:
199+
self.write(html)
200+
self.flush()
201+
202+
return it

0 commit comments

Comments
 (0)