Skip to content

Commit 12f902e

Browse files
committed
Updates for djagno 3.2 compatibility
1 parent 208095e commit 12f902e

File tree

6 files changed

+149
-106
lines changed

6 files changed

+149
-106
lines changed

bokeh_django/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,5 @@
66
from .routing import autoload, directory, document
77
from .static import static_extensions
88

9-
import_required("django", "django is required by bokeh.server.django")
10-
import_required("channels", "The package channels is required by bokeh.server.django and must be installed")
11-
12-
default_app_config = "bokeh.server.django.DjangoBokehConfig"
9+
import_required("django", "django is required by bokeh-django")
10+
import_required("channels", "The package channels is required by bokeh-django and must be installed")

bokeh_django/apps.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
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+
# -----------------------------------------------------------------------------
1111
from __future__ import annotations
1212

1313
import logging # isort:skip
1414
log = logging.getLogger(__name__)
1515

16-
#-----------------------------------------------------------------------------
16+
# -----------------------------------------------------------------------------
1717
# Imports
18-
#-----------------------------------------------------------------------------
18+
# -----------------------------------------------------------------------------
1919

2020
# Standard library imports
2121
from importlib import import_module
@@ -28,21 +28,23 @@
2828
# Bokeh imports
2929
from .routing import Routing, RoutingConfiguration
3030

31-
#-----------------------------------------------------------------------------
31+
# -----------------------------------------------------------------------------
3232
# Globals and constants
33-
#-----------------------------------------------------------------------------
33+
# -----------------------------------------------------------------------------
3434

3535
__all__ = (
3636
'DjangoBokehConfig',
3737
)
3838

39-
#-----------------------------------------------------------------------------
39+
# -----------------------------------------------------------------------------
4040
# General API
41-
#-----------------------------------------------------------------------------
41+
# -----------------------------------------------------------------------------
42+
4243

4344
class DjangoBokehConfig(AppConfig):
4445

45-
name = label = 'bokeh.server.django'
46+
name = 'bokeh-django'
47+
label = 'bokeh_django'
4648

4749
_routes: RoutingConfiguration | None = None
4850

@@ -58,14 +60,14 @@ def routes(self) -> RoutingConfiguration:
5860
self._routes = RoutingConfiguration(self.bokeh_apps)
5961
return self._routes
6062

61-
#-----------------------------------------------------------------------------
63+
# -----------------------------------------------------------------------------
6264
# Dev API
63-
#-----------------------------------------------------------------------------
65+
# -----------------------------------------------------------------------------
6466

65-
#-----------------------------------------------------------------------------
67+
# -----------------------------------------------------------------------------
6668
# Private API
67-
#-----------------------------------------------------------------------------
69+
# -----------------------------------------------------------------------------
6870

69-
#-----------------------------------------------------------------------------
71+
# -----------------------------------------------------------------------------
7072
# Code
71-
#-----------------------------------------------------------------------------
73+
# -----------------------------------------------------------------------------

bokeh_django/consumers.py

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
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+
# -----------------------------------------------------------------------------
1111
from __future__ import annotations
1212

1313
import logging # isort:skip
1414
log = logging.getLogger(__name__)
1515

16-
#-----------------------------------------------------------------------------
16+
# -----------------------------------------------------------------------------
1717
# Imports
18-
#-----------------------------------------------------------------------------
18+
# -----------------------------------------------------------------------------
1919

2020
# Standard library imports
2121
import asyncio
@@ -56,19 +56,20 @@
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

7374
class 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+
98100
class 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+
126135
class 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+
160172
class 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+
171186
class 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

316332
class 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

Comments
 (0)