1- #-----------------------------------------------------------------------------
1+ # -----------------------------------------------------------------------------
22# Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors.
33# All rights reserved.
44#
55# The full license is in the file LICENSE.txt, distributed with this software.
6- #-----------------------------------------------------------------------------
6+ # -----------------------------------------------------------------------------
77
8- #-----------------------------------------------------------------------------
8+ # -----------------------------------------------------------------------------
99# Boilerplate
10- #-----------------------------------------------------------------------------
10+ # -----------------------------------------------------------------------------
1111from __future__ import annotations
1212
1313import logging # isort:skip
1414log = logging .getLogger (__name__ )
1515
16- #-----------------------------------------------------------------------------
16+ # -----------------------------------------------------------------------------
1717# Imports
18- #-----------------------------------------------------------------------------
18+ # -----------------------------------------------------------------------------
1919
2020# Standard library imports
2121import asyncio
5656 get_token_payload ,
5757)
5858
59- #-----------------------------------------------------------------------------
59+ # -----------------------------------------------------------------------------
6060# Globals and constants
61- #-----------------------------------------------------------------------------
61+ # -----------------------------------------------------------------------------
6262
6363__all__ = (
6464 'DocConsumer' ,
6565 'AutoloadJsConsumer' ,
6666 'WSConsumer' ,
6767)
6868
69- #-----------------------------------------------------------------------------
69+ # -----------------------------------------------------------------------------
7070# General API
71- #-----------------------------------------------------------------------------
71+ # -----------------------------------------------------------------------------
72+
7273
7374class ConsumerHelper (AsyncConsumer ):
7475
@@ -95,26 +96,33 @@ def resources(self, absolute_url: str | None = None) -> Resources:
9596 return Resources (mode = "server" , root_url = root_url , path_versioner = StaticHandler .append_version )
9697 return Resources (mode = mode )
9798
99+
98100class SessionConsumer (AsyncHttpConsumer , ConsumerHelper ):
99101
100- application_context : ApplicationContext
102+ _application_context : ApplicationContext
101103
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' )
104107
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" ]
107113
108114 # 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
111118
112119 async def _get_session (self ) -> ServerSession :
113120 session_id = self .arguments .get ('bokeh-session-id' ,
114121 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+ )
118126 token = generate_jwt_token (session_id ,
119127 secret_key = None ,
120128 signed = False ,
@@ -123,6 +131,7 @@ async def _get_session(self) -> ServerSession:
123131 session = await self .application_context .create_session_if_needed (session_id , self .request , token )
124132 return session
125133
134+
126135class AutoloadJsConsumer (SessionConsumer ):
127136
128137 async def handle (self , body : bytes ) -> None :
@@ -143,6 +152,8 @@ async def handle(self, body: bytes) -> None:
143152
144153 resources_param = self .get_argument ("resources" , "default" )
145154 resources = self .resources (server_url ) if resources_param != "none" else None
155+
156+ resources = self .resources (server_url )
146157 bundle = bundle_for_objs_and_resources (None , resources )
147158
148159 render_items = [RenderItem (token = session .token , elementid = element_id , use_for_title = False )]
@@ -157,34 +168,42 @@ async def handle(self, body: bytes) -> None:
157168 ]
158169 await self .send_response (200 , js .encode (), headers = headers )
159170
171+
160172class DocConsumer (SessionConsumer ):
161173
162174 async def handle (self , body : bytes ) -> None :
163175 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+ )
169183 await self .send_response (200 , page .encode (), headers = [(b"Content-Type" , b"text/html" )])
170184
185+
171186class WSConsumer (AsyncWebsocketConsumer , ConsumerHelper ):
172187
173188 _clients : Set [ServerConnection ]
174189
175- application_context : ApplicationContext
190+ _application_context : ApplicationContext | None
176191
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 ()
179197
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" ]
182203
183- if self .application_context .io_loop is None :
204+ if self ._application_context .io_loop is None :
184205 raise RuntimeError ("io_loop should already been set" )
185-
186- self ._clients = set ()
187- self .lock = locks .Lock ()
206+ return self ._application_context
188207
189208 async def connect (self ):
190209 log .info ('WebSocket connection opened' )
@@ -259,6 +278,7 @@ async def _async_open(self, token: str) -> None:
259278 log .info ("ServerConnection created" )
260279
261280 except Exception as e :
281+ breakpoint ()
262282 log .error ("Could not create new server session, reason: %s" , e )
263283 self .close ()
264284 raise e
@@ -285,33 +305,29 @@ async def _send_bokeh_message(self, message: Message) -> int:
285305 sent += len (header ) + len (payload )
286306 except Exception : # Tornado 4.x may raise StreamClosedError
287307 # 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" )
289309 return sent
290310
311+ async def send_message (self , message : Message ) -> int :
312+ return await self ._send_bokeh_message (message )
313+
291314 def _new_connection (self ,
292315 protocol : Protocol ,
293316 socket : AsyncConsumer ,
294317 application_context : ApplicationContext ,
295318 session : ServerSession ) -> ServerConnection :
296- connection = AsyncServerConnection (protocol , socket , application_context , session )
319+ connection = ServerConnection (protocol , socket , application_context , session )
297320 self ._clients .add (connection )
298321 return connection
299322
300- #-----------------------------------------------------------------------------
323+ # -----------------------------------------------------------------------------
301324# Dev API
302- #-----------------------------------------------------------------------------
325+ # -----------------------------------------------------------------------------
303326
304- #-----------------------------------------------------------------------------
327+ # -----------------------------------------------------------------------------
305328# Private API
306- #-----------------------------------------------------------------------------
307-
308- # TODO: remove this when coroutines are dropped
309- class AsyncServerConnection (ServerConnection ):
329+ # -----------------------------------------------------------------------------
310330
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 )
315331
316332class AttrDict (dict ):
317333 """ Provide a dict subclass that supports access by named attributes.
@@ -321,6 +337,6 @@ class AttrDict(dict):
321337 def __getattr__ (self , key ):
322338 return self [key ]
323339
324- #-----------------------------------------------------------------------------
340+ # -----------------------------------------------------------------------------
325341# Code
326- #-----------------------------------------------------------------------------
342+ # -----------------------------------------------------------------------------
0 commit comments