Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Commit 2fd1a11

Browse files
committed
Fix shutdown deprecation, #63
1 parent a6b9adf commit 2fd1a11

File tree

2 files changed

+30
-25
lines changed

2 files changed

+30
-25
lines changed

jupyter_dash/_stoppable_thread.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ctypes
2+
import threading
3+
4+
5+
class StoppableThread(threading.Thread):
6+
def get_id(self):
7+
if hasattr(self, "_thread_id"):
8+
return self._thread_id
9+
for thread_id, thread in threading._active.items():
10+
if thread is self:
11+
return thread_id
12+
13+
def kill(self):
14+
thread_id = self.get_id()
15+
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
16+
ctypes.c_long(thread_id), ctypes.py_object(SystemExit)
17+
)
18+
if res == 0:
19+
raise ValueError(f"Invalid thread id: {thread_id}")
20+
if res > 1:
21+
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id), None)
22+
raise SystemExit("Stopping thread failure")

jupyter_dash/jupyter_app.py

+8-25
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import dash
22
import os
33
import requests
4-
from flask import request
54
import flask.cli
6-
from threading import Thread
75
from retrying import retry
86
import io
97
import re
@@ -21,6 +19,7 @@
2119
from werkzeug.debug.tbtools import get_current_traceback
2220

2321
from .comms import _dash_comm, _jupyter_config, _request_jupyter_config
22+
from ._stoppable_thread import StoppableThread
2423

2524

2625
class JupyterDash(dash.Dash):
@@ -41,6 +40,8 @@ class JupyterDash(dash.Dash):
4140
_in_colab = "google.colab" in sys.modules
4241
_token = str(uuid.uuid4())
4342

43+
_server_thread = None
44+
4445
@classmethod
4546
def infer_jupyter_proxy_config(cls):
4647
"""
@@ -130,15 +131,6 @@ def __init__(self, name=None, server_url=None, **kwargs):
130131

131132
self.server_url = server_url
132133

133-
# Register route to shut down server
134-
@self.server.route('/_shutdown_' + JupyterDash._token, methods=['GET'])
135-
def shutdown():
136-
func = request.environ.get('werkzeug.server.shutdown')
137-
if func is None:
138-
raise RuntimeError('Not running with the Werkzeug Server')
139-
func()
140-
return 'Server shutting down...'
141-
142134
# Register route that we can use to poll to see when server is running
143135
@self.server.route('/_alive_' + JupyterDash._token, methods=['GET'])
144136
def alive():
@@ -217,7 +209,8 @@ def run_server(
217209
inline_exceptions = mode == "inline"
218210

219211
# Terminate any existing server using this port
220-
self._terminate_server_for_port(host, port)
212+
if self._server_thread:
213+
self._server_thread.kill()
221214

222215
# Configure pathname prefix
223216
requests_pathname_prefix = self.config.get('requests_pathname_prefix', None)
@@ -291,9 +284,9 @@ def run_server(
291284
def run():
292285
super_run_server(**kwargs)
293286

294-
thread = Thread(target=run)
295-
thread.setDaemon(True)
296-
thread.start()
287+
self._server_thread = StoppableThread(target=run)
288+
self._server_thread.setDaemon(True)
289+
self._server_thread.start()
297290

298291
# Wait for server to start up
299292
alive_url = "http://{host}:{port}/_alive_{token}".format(
@@ -412,16 +405,6 @@ def _wrap_errors(_):
412405

413406
return html_str, 500
414407

415-
@classmethod
416-
def _terminate_server_for_port(cls, host, port):
417-
shutdown_url = "http://{host}:{port}/_shutdown_{token}".format(
418-
host=host, port=port, token=JupyterDash._token
419-
)
420-
try:
421-
response = requests.get(shutdown_url)
422-
except Exception as e:
423-
pass
424-
425408

426409
def _custom_formatargvalues(
427410
args, varargs, varkw, locals,

0 commit comments

Comments
 (0)