8
8
#############################################################################
9
9
10
10
11
+ import asyncio
11
12
import os
12
13
from typing import Dict
13
14
@@ -33,8 +34,7 @@ def initialize(self, **kwargs):
33
34
# we want to avoid starting multiple kernels due to template mistakes
34
35
self .kernel_started = False
35
36
36
- @tornado .web .authenticated
37
- async def get (self , path = None ):
37
+ async def get_generator (self , path = None ):
38
38
# if the handler got a notebook_path argument, always serve that
39
39
notebook_path = self .notebook_path or path
40
40
@@ -101,22 +101,18 @@ async def get(self, path=None):
101
101
QueryStringSocketHandler .send_updates ({'kernel_id' : kernel_id , 'payload' : self .request .query })
102
102
# Send rendered cell to frontend
103
103
if len (rendered_cache ) > 0 :
104
- self .write ('' .join (rendered_cache ))
105
- self .flush ()
104
+ yield '' .join (rendered_cache )
106
105
107
106
# Wait for current running cell finish and send this cell to
108
107
# frontend.
109
108
rendered , rendering = await render_task
110
109
if len (rendered ) > len (rendered_cache ):
111
110
html_snippet = '' .join (rendered [len (rendered_cache ):])
112
- self .write (html_snippet )
113
- self .flush ()
111
+ yield html_snippet
114
112
115
113
# Continue render cell from generator.
116
114
async for html_snippet , _ in rendering :
117
- self .write (html_snippet )
118
- self .flush ()
119
- self .flush ()
115
+ yield html_snippet
120
116
121
117
else :
122
118
# All kernels are used or pre-heated kernel is disabled, start a normal kernel.
@@ -139,8 +135,7 @@ def time_out():
139
135
can be used in a template to give feedback to a user
140
136
"""
141
137
142
- self .write ('<script>voila_heartbeat()</script>\n ' )
143
- self .flush ()
138
+ return '<script>voila_heartbeat()</script>\n '
144
139
145
140
kernel_env [ENV_VARIABLE .VOILA_PREHEAT ] = 'False'
146
141
kernel_env [ENV_VARIABLE .VOILA_BASE_URL ] = self .base_url
@@ -154,13 +149,34 @@ def time_out():
154
149
)
155
150
)
156
151
kernel_future = self .kernel_manager .get_kernel (kernel_id )
157
- async for html_snippet , _ in gen .generate_content_generator (
158
- kernel_id , kernel_future , time_out
159
- ):
160
- self .write (html_snippet )
161
- self .flush ()
162
- # we may not want to consider not flusing after each snippet, but add an explicit flush function to the jinja context
163
- # yield # give control back to tornado's IO loop, so it can handle static files or other requests
152
+ queue = asyncio .Queue ()
153
+
154
+ async def put_html ():
155
+ async for html_snippet , _ in gen .generate_content_generator (kernel_id , kernel_future ):
156
+ await queue .put (html_snippet )
157
+
158
+ await queue .put (None )
159
+
160
+ asyncio .ensure_future (put_html ())
161
+
162
+ # If not done within the timeout, we send a heartbeat
163
+ # this is fundamentally to avoid browser/proxy read-timeouts, but
164
+ # can be used in a template to give feedback to a user
165
+ while True :
166
+ try :
167
+ html_snippet = await asyncio .wait_for (queue .get (), self .voila_configuration .http_keep_alive_timeout )
168
+ except asyncio .TimeoutError :
169
+ yield time_out ()
170
+ else :
171
+ if html_snippet is None :
172
+ break
173
+ yield html_snippet
174
+
175
+ @tornado .web .authenticated
176
+ async def get (self , path = None ):
177
+ gen = self .get_generator (path = path )
178
+ async for html in gen :
179
+ self .write (html )
164
180
self .flush ()
165
181
166
182
def redirect_to_file (self , path ):
0 commit comments