1
- #-----------------------------------------------------------------------------
1
+ # -----------------------------------------------------------------------------
2
2
# Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors.
3
3
# All rights reserved.
4
4
#
5
5
# The full license is in the file LICENSE.txt, distributed with this software.
6
- #-----------------------------------------------------------------------------
6
+ # -----------------------------------------------------------------------------
7
7
8
- #-----------------------------------------------------------------------------
8
+ # -----------------------------------------------------------------------------
9
9
# Boilerplate
10
- #-----------------------------------------------------------------------------
10
+ # -----------------------------------------------------------------------------
11
11
from __future__ import annotations
12
12
13
13
import logging # isort:skip
14
14
log = logging .getLogger (__name__ )
15
15
16
- #-----------------------------------------------------------------------------
16
+ # -----------------------------------------------------------------------------
17
17
# Imports
18
- #-----------------------------------------------------------------------------
18
+ # -----------------------------------------------------------------------------
19
19
20
20
# Standard library imports
21
21
import asyncio
56
56
get_token_payload ,
57
57
)
58
58
59
- #-----------------------------------------------------------------------------
59
+ # -----------------------------------------------------------------------------
60
60
# Globals and constants
61
- #-----------------------------------------------------------------------------
61
+ # -----------------------------------------------------------------------------
62
62
63
63
__all__ = (
64
64
'DocConsumer' ,
65
65
'AutoloadJsConsumer' ,
66
66
'WSConsumer' ,
67
67
)
68
68
69
- #-----------------------------------------------------------------------------
69
+ # -----------------------------------------------------------------------------
70
70
# General API
71
- #-----------------------------------------------------------------------------
71
+ # -----------------------------------------------------------------------------
72
+
72
73
73
74
class ConsumerHelper (AsyncConsumer ):
74
75
@@ -95,26 +96,33 @@ def resources(self, absolute_url: str | None = None) -> Resources:
95
96
return Resources (mode = "server" , root_url = root_url , path_versioner = StaticHandler .append_version )
96
97
return Resources (mode = mode )
97
98
99
+
98
100
class SessionConsumer (AsyncHttpConsumer , ConsumerHelper ):
99
101
100
- application_context : ApplicationContext
102
+ _application_context : ApplicationContext
101
103
102
- def __init__ (self , scope : Dict [str , Any ]) -> None :
103
- super ().__init__ (scope )
104
+ def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
105
+ super ().__init__ (* args , ** kwargs )
106
+ self ._application_context = kwargs .get ('app_context' )
104
107
105
- kwargs = self .scope ["url_route" ]["kwargs" ]
106
- self .application_context = kwargs ["app_context" ]
108
+ @property
109
+ def application_context (self ) -> ApplicationContext :
110
+ # backwards compatibility
111
+ if self ._application_context is None :
112
+ self ._application_context = self .scope ["url_route" ]["kwargs" ]["app_context" ]
107
113
108
114
# XXX: accessing asyncio's IOLoop directly doesn't work
109
- if self .application_context .io_loop is None :
110
- self .application_context ._loop = IOLoop .current ()
115
+ if self ._application_context .io_loop is None :
116
+ self ._application_context ._loop = IOLoop .current ()
117
+ return self ._application_context
111
118
112
119
async def _get_session (self ) -> ServerSession :
113
120
session_id = self .arguments .get ('bokeh-session-id' ,
114
121
generate_session_id (secret_key = None , signed = False ))
115
- payload = {'headers' : {k .decode ('utf-8' ): v .decode ('utf-8' )
116
- for k , v in self .request .headers },
117
- 'cookies' : dict (self .request .cookies )}
122
+ payload = dict (
123
+ headers = {k .decode ('utf-8' ): v .decode ('utf-8' ) for k , v in self .request .headers },
124
+ cookies = dict (self .request .cookies ),
125
+ )
118
126
token = generate_jwt_token (session_id ,
119
127
secret_key = None ,
120
128
signed = False ,
@@ -123,6 +131,7 @@ async def _get_session(self) -> ServerSession:
123
131
session = await self .application_context .create_session_if_needed (session_id , self .request , token )
124
132
return session
125
133
134
+
126
135
class AutoloadJsConsumer (SessionConsumer ):
127
136
128
137
async def handle (self , body : bytes ) -> None :
@@ -143,6 +152,8 @@ async def handle(self, body: bytes) -> None:
143
152
144
153
resources_param = self .get_argument ("resources" , "default" )
145
154
resources = self .resources (server_url ) if resources_param != "none" else None
155
+
156
+ resources = self .resources (server_url )
146
157
bundle = bundle_for_objs_and_resources (None , resources )
147
158
148
159
render_items = [RenderItem (token = session .token , elementid = element_id , use_for_title = False )]
@@ -157,34 +168,42 @@ async def handle(self, body: bytes) -> None:
157
168
]
158
169
await self .send_response (200 , js .encode (), headers = headers )
159
170
171
+
160
172
class DocConsumer (SessionConsumer ):
161
173
162
174
async def handle (self , body : bytes ) -> None :
163
175
session = await self ._get_session ()
164
- page = server_html_page_for_session (session ,
165
- resources = self .resources (),
166
- title = session .document .title ,
167
- template = session .document .template ,
168
- template_variables = session .document .template_variables )
176
+ page = server_html_page_for_session (
177
+ session ,
178
+ resources = self .resources (),
179
+ title = session .document .title ,
180
+ template = session .document .template ,
181
+ template_variables = session .document .template_variables
182
+ )
169
183
await self .send_response (200 , page .encode (), headers = [(b"Content-Type" , b"text/html" )])
170
184
185
+
171
186
class WSConsumer (AsyncWebsocketConsumer , ConsumerHelper ):
172
187
173
188
_clients : Set [ServerConnection ]
174
189
175
- application_context : ApplicationContext
190
+ _application_context : ApplicationContext | None
176
191
177
- def __init__ (self , scope : Dict [str , Any ]) -> None :
178
- super ().__init__ (scope )
192
+ def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
193
+ super ().__init__ (* args , ** kwargs )
194
+ self ._application_context = kwargs .get ('app_context' )
195
+ self ._clients = set ()
196
+ self .lock = locks .Lock ()
179
197
180
- kwargs = self .scope ['url_route' ]["kwargs" ]
181
- self .application_context = kwargs ["app_context" ]
198
+ @property
199
+ def application_context (self ) -> ApplicationContext :
200
+ # backward compatiblity
201
+ if self ._application_context is None :
202
+ self ._application_context = self .scope ["url_route" ]["kwargs" ]["app_context" ]
182
203
183
- if self .application_context .io_loop is None :
204
+ if self ._application_context .io_loop is None :
184
205
raise RuntimeError ("io_loop should already been set" )
185
-
186
- self ._clients = set ()
187
- self .lock = locks .Lock ()
206
+ return self ._application_context
188
207
189
208
async def connect (self ):
190
209
log .info ('WebSocket connection opened' )
@@ -259,6 +278,7 @@ async def _async_open(self, token: str) -> None:
259
278
log .info ("ServerConnection created" )
260
279
261
280
except Exception as e :
281
+ breakpoint ()
262
282
log .error ("Could not create new server session, reason: %s" , e )
263
283
self .close ()
264
284
raise e
@@ -285,33 +305,29 @@ async def _send_bokeh_message(self, message: Message) -> int:
285
305
sent += len (header ) + len (payload )
286
306
except Exception : # Tornado 4.x may raise StreamClosedError
287
307
# on_close() is / will be called anyway
288
- log .warn ("Failed sending message as connection was closed" )
308
+ log .warning ("Failed sending message as connection was closed" )
289
309
return sent
290
310
311
+ async def send_message (self , message : Message ) -> int :
312
+ return await self ._send_bokeh_message (message )
313
+
291
314
def _new_connection (self ,
292
315
protocol : Protocol ,
293
316
socket : AsyncConsumer ,
294
317
application_context : ApplicationContext ,
295
318
session : ServerSession ) -> ServerConnection :
296
- connection = AsyncServerConnection (protocol , socket , application_context , session )
319
+ connection = ServerConnection (protocol , socket , application_context , session )
297
320
self ._clients .add (connection )
298
321
return connection
299
322
300
- #-----------------------------------------------------------------------------
323
+ # -----------------------------------------------------------------------------
301
324
# Dev API
302
- #-----------------------------------------------------------------------------
325
+ # -----------------------------------------------------------------------------
303
326
304
- #-----------------------------------------------------------------------------
327
+ # -----------------------------------------------------------------------------
305
328
# Private API
306
- #-----------------------------------------------------------------------------
307
-
308
- # TODO: remove this when coroutines are dropped
309
- class AsyncServerConnection (ServerConnection ):
329
+ # -----------------------------------------------------------------------------
310
330
311
- async def send_patch_document (self , event ):
312
- """ Sends a PATCH-DOC message, returning a Future that's completed when it's written out. """
313
- msg = self .protocol .create ('PATCH-DOC' , [event ])
314
- await self ._socket ._send_bokeh_message (msg )
315
331
316
332
class AttrDict (dict ):
317
333
""" Provide a dict subclass that supports access by named attributes.
@@ -321,6 +337,6 @@ class AttrDict(dict):
321
337
def __getattr__ (self , key ):
322
338
return self [key ]
323
339
324
- #-----------------------------------------------------------------------------
340
+ # -----------------------------------------------------------------------------
325
341
# Code
326
- #-----------------------------------------------------------------------------
342
+ # -----------------------------------------------------------------------------
0 commit comments